日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux下进程间通信概述

發布時間:2024/4/24 linux 70 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux下进程间通信概述 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.?Linux下進程間通信概述 P83-P84

將第一頁和第二頁合并起來講了

引言:前面我們學習了一下進程,我們知道多,進程間的地址空間相對獨立。進程與進程間不能像線程間通過全局變量通信。 如果想進程間通信,就需要其他機制。

UNIX平臺進程通信方式

}?早期進程間通信方式:

ü?無名管道(pipe):UNIX IPC的最古老的形式,所有的UNIX系統都支持,命令行里常用“|

ü?有名管道(fifo)

ü?信號(signal)

UNIX發展做出重要貢獻的兩大主力是AT&T的貝爾實驗室和BSD(加州大學伯克利分校的伯克利軟件發布中心),他們在進程間通信方面也有很大的貢獻,但兩者的研發側重點不同導致了兩種標準。

}?AT&T的貝爾實驗室,對Unix早期的進程間通信進行了改進和擴充,形成了“system V IPC”,其通信進程主要局限在單個計算機內:

ü?共享內存(share memory)

ü?消息隊列(message queue)

ü?信號燈(semaphore)?或者叫信號量,嚴格意義上應該定義其為同步原語,而不是IPC,其存在常用于配合共享內存使用,用于同步和互斥。

}?BSD(加州大學伯克利分校的伯克利軟件發布中心),跳過了該限制,支持不同主機上各個進程間IPC

ü?形成了基于套接字(socket)的進程間通信機制:BSD并不是沒有涉足單機內的進程間通信(socket本身就可以用于單機內的進程間通信)

由于UNIX版本的多樣性,電子電氣工程協會(IEEE)開發了一個獨立的UNIX標準-POSIX,包含對IPC的支持部分。

Linux繼承了上述所有的通信方式, 在這里我們主要介紹

l?早期進程間通信方式下的:無名管道(pipe),?有名管道(fifo)和信號(signal)

l?重點講解System V IPC,?POSIX IPC不展開,(在講解線程同步時已經涉獵過POSIX IPC的信號量部分)。在嵌入式領域,還是SystemV IPC比較多,由于歷史的原因,POSIXSystemV出現的晚,很多嵌入式系統對POSIX IPC支持的還不是很好。

l?BSD Socket,留在講解網絡時講解。

參考:

1)深刻理解Linux進程間通信(IPChttps://www.ibm.com/developerworks/cn/linux/l-ipc/

2linux進程間通信(消息隊列、信號量、共享內存等)http://www.docin.com/p-725076998.html

3)進程互斥鎖 (比較了SystemVPOSIX的平臺兼容性)http://blog.csdn.net/luansxx/article/details/7736618

提高: <<<<?UNIX世界里IPC兩套(標準)。

System V IPC and POSIX IPC: http://www.findfunaax.com/notes/file/213

這兩套標準都包含并定義了消息隊列,共享內存和信號量這三套機制。這兩套標準孰優孰劣還可參考:

http://stackoverflow.com/questions/368322/differences-between-system-v-and-posix-semaphores

其中對POSIX 的信號量還分兩種:

}?有名信號燈

}?基于內存的信號燈

兩者的區別參考:http://blog.csdn.net/shanshanpt/article/details/7376311

注意看一下最后一段話講得不錯。

有名信號燈參考:http://blog.chinaunix.net/uid-25324849-id-207480.html

所以總結來看,信號燈種類可以分成三種:

}?posix有名信號燈

}?posix基于內存的信號燈(無名信號燈)

}?System V信號燈(IPC對象)

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

2.?經典進程間通信方式

2.1無名管道

重點:

l?PIPE的特點

l?PIPE的創建典型流程

l?PIP的讀寫注意點

2.1.1概念介紹

2.1.1.1 概念:(P85

類似時空隧道的概念,建立兩個進程之間的通訊橋梁。數據的讀出和寫入:一個進程向管道中寫的內容被管道另一端的進程讀出。寫入的內容每次都添加在管道緩沖區的末尾,并且每次都是從緩沖區的頭部讀出數據

2.1.1.2 特點:(P86

}?只能用于具有親緣關系的進程之間的通信:通過創建的流程來理解

}?半雙工的通信模式,具有固定的讀端和寫端:傳輸方向同時只能是一個方向,強調固定。

}?管道可以看成是一種特殊的文件,對于它的讀寫可以使用文件IOreadwrite函數:但和FIFO不同的是在文件系統里并不存在pipe對應的文件。

}?不支持如lseek() 操作。

2.1.2無名管道創建的典型流程P87-P90

先講看一下API

舉例:<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

samples\3.ProcessII\2.1-pipe\?pipe_open.c

半雙工管道的典型創建流程,可以step by step地畫圖增加學生的理解。

強調一下:單獨創建一個無名管道,并沒有實際的意義。我們一般是在一個進程在由pipe()創建管道后,一般再由fork一個子進程,然后通過管道實現父子進程間的通信(因此也不難推出,只要兩個進程中存在親緣關系,這里的親緣關系指的是具有共同的祖先,都可以采用管道方式來進行通信)。這也是為何前面說只能用于具有親緣關系的進程之間的通信的原因。

step1: 父進程創建一個pipe,其中fd[0]固定用于讀管道,而fd[1]固定用于寫管道。

?

Step2:父進程fork,子進程繼承了父進程的管道

?

Step3:之后取決于我們想要的數據流方向來關閉相應的端。

?

?

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

2.1.3管道讀寫注意點(P91

參考ppthttp://blog.chinaunix.net/uid-26833883-id-3227144.html

man 7 pipe

}?當管道中無數據時,讀操作會阻塞 - pipe_rw.c

}?向管道中寫入數據時…- pipe_buf.c 用該原理來測試管道內部緩沖區的大小

}?只有在管道的讀端存在時,…SIGPIPE信號(通常Broken pipe錯誤)- 信號未講,暫不演示

2.1.4 作業:

實驗5.1 <<<<< ?labs\5.1

注意:

1)實驗手冊上的例子代碼為實現先接收P1,后P2,采用了我們課程上沒有講過的函數來實現進程間同步。由于我們到目前課程為止還沒有講過進程間同步的方法,POSIX信號量可以支持,但我們截止目前課程也只講了線程間同步,所以P1P2的順序實現作為提高,需要和學生說一下。

2)因為n<PIPE_BUF,所以我們不需要擔心破壞寫的原子性。這里可以體現出來。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

2.2 FIFO

重點:

l?無名管道和有名管道的區別

l?FIFO的創建方法

l?FIFO的讀寫注意點

l?總結

2.2.1 FIFO的概念(P92

又叫有名管道,為何要提出有名管道的說法,克服了無名管道的什么問題

}?無名管道只能用于具有親緣關系的進程之間,這就限制了無名管道的使用范圍

}?有名管道可以使互不相關的兩個進程互相通信。有名管道可以通過路徑名來指出,并且在文件系統中可見

FIFO不同于無名管道之處在于它提供了一個路徑名與之關聯,FIFO的文件形式存在于文件系統中這樣,即使與FIFO的創建進程不存在親緣關系的進程,只要可以訪問該路徑,就能夠彼此通過FIFO相互通信,因此,通過FIFO不相關的進程也能交換數據。

}?進程通過文件IO來操作有名管道:FIFO是一種文件類型,讓學員回顧全面文件類型和stat結構中st_mode以及S_ISFIFO的宏

2.2.2 FIFO的創建mkfifo和打開openP93-P94

該函數的第一個參數是一個普通的路勁名,也就是創建后FIFO的名字。第二個參數與打開普通文件的open()函數中的mode參數相同。如果mkfifo的一個參數是一個已經存在路徑名時,會返回EEXIST錯誤,所以一般典型的調用代碼首先會檢查是否返回該錯誤,如果確實返回該錯誤,那么只要調用打開FIFO的函數open就可以了。

mkfifo函數時可以讓學員觀察一下參數filename是一個文件路徑,存在于文件系統中,mode則和openmode一樣。注意,當多個進程通過FIFO交換數據時,內核通過內部的緩存,并不會往我們創建的文件里寫入東西,所以該文件的內容是空的,有興趣可以看看。這個文件只是在文件系統里提供了一個名字供多個進程訪問pipe,所以我們稱其為有名管道的由來。

注意mkfifo只是基于文件系統創建了一個文件。具體讀寫之前有名管道比無名管道多了一個打開操作:open調用mkfifo成功后,必須繼續用open打開它,得到文件描述符,然后進一步用read/write/close等文件IO進行操作。

FIFOopen/打開規則:

l?在一個FIFO上打開一個讀端,

寫端是否存在

阻塞方式

非阻塞方式

成功返回

成功返回

阻塞一直到有其他相應進程在該FIFO上打開寫端

成功返回

l?在一個FIFO上打開一個寫端

讀端是否存在

阻塞方式

非阻塞方式

成功返回

成功返回

阻塞一直到有相應進程在該FIFO上打開讀端

返回ENXIO錯誤

舉例<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

samples\3.ProcessII\2.2-FIFO\fifo_open.c

兩個進程,一個在FIFO上只寫,一個在FIFO上只讀

演示重點:嘗試先執行讀進程后執行寫進程,或者先寫后讀。探究發現,如果open時沒有使用O_NONBLOCK參數,我們發現不論讀端還是寫端先打開,先打開者都會阻塞,一直阻塞到另一端打開。

如果open時使用了O_NONBLOCK參數,此時打開FIFO 又會是什么情況?

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

2.2.3 有名管道的讀寫規則

有名管道的讀寫規則和無名管道是一致的。重點講清楚無名管道的讀寫就好了

2.2.3.3 小結:

The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics.

不同點

相同點

l?PipeFIFO的區別只在于其創建和打開階段。此后的操作兩者的行為幾乎是一致的。

ü?對于pipe,調用pipe()返回的兩個fd,已經賦予了讀端和寫端的屬性,不需要另外指定。

ü?對于FIFO,通過mkfifo創建,在文件系統中有了一個名字。然后要從該FIFO中讀數據的進程以O_RDONLY?調用open()要從該FIFO中寫數據的進程以O_WRONLY?調用open()。或者直接用O_RDWR

l?有名管道可以使互不相關,沒有親緣關系的的兩個進程互相通信。

l?有名管道的讀寫需要通過打開文件,對于文件我們可以通過控制所有者和權限管理對管道的訪問。

l?雖然管道,特別是有名管道可以很方便地在雙向上打開讀寫,但其內核實現依然是單向的。嚴格遵循先進先出(first in first out),對管道及FIFO的讀總是從開始處返回數據,對它們的寫則把數據添加到末尾。

l?pipe, fifo都不支持諸如lseek()等文件定位操作。

l?對于pipe或者fifo,如果在讀端或者寫端打開了多個讀寫端(進程),之間的讀寫是不確定的,需要通過其他的同步機制實現多進程通訊的同步。

2.2.4 典型的FIFO模型(可忽略)

對照ppt以概念講述為主。如果可以給些例子講解。

2.2.5 實驗:

實驗5.2 ?<<<<<< labs\5.2

需要講解一下編寫方向再做

三個程序,程序1,輸入參數為FIFO文件路徑,創建fifo文件;程序2,輸入參數為FIFO文件路徑,以只讀打開,打開后執行循環并讀管道中的數據。如果讀到文件尾則退出。程序3,輸入參數為FIFO文件路徑,以只寫方式打開,從stdin上讀取數據,讀一行,寫一行。>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

2.3?信號通信

2.3.1 信號基礎

重點:

l?了解信號的概念

l?了解系統對信號的處理方式

2.3.1.1?信號的來源(P96

參考:linux系統編程之信號(三):信號的阻塞與未決:http://blog.csdn.net/jnu_simba/article/details/8944982

}?信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式

信號則是由內核(或其他進程)對某個進程的中斷,不是軟中斷,更不是硬中斷。

}?信號可以直接進行用戶空間進程和內核進程之間的交互,內核進程也可以利用它來通知用戶空間進程發生了哪些系統事件。

信號事件的發生主要有兩個來源:

l?硬件來源

ü?用戶在終端按下某些鍵時,終端驅動程序會發送信號給前臺進程,例如ctr+c產生SIGINT,該信號會終止進程, ?ctr + \產生SIGQUI信號,和Ctrl+C類似,ctr + z產生SIGTSTP,暫停進程并放到后臺,即掛起。

ü?硬件異常產生信號,這些條件由硬件檢測到并通知內核,然后內核向當前進程發送適當的信號。例如當前進程執行了除以0的指令,CPU的運算單元會產生異常,內核將這個異常解釋為SIGFPE信號發送給進程。再比如當前進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋為SIGSEGV信號發送給當前進程 。

l?軟件來源

ü?系統調用:一個進程調用int kill(pid_t pid,int sig)函數可以給另一個進程發送信號

ü?終端命令:可以用kill命令給某個進程發送信號,如果不明確指定信號則發送SIGTERM信號,該信號的默認處理動作是終止進程。

ü?當內核檢測到某種軟件條件發生時也可以通過信號通知進程,例如子進程結束,內核給父進程發送SIGCHILD;鬧鐘超時產生SIGALRM信號;向讀端已關閉的管道寫數據時產生SIGPIPE信號。

}?如果該進程當前并未處于執行態,則該信號就由內核保存起來,直到該進程恢復執行再傳遞給它;如果一個信號被進程設置為阻塞,則該信號的傳遞被延遲,直到其阻塞被取消時才被傳遞給進程

實際執行信號的處理動作稱為信號投遞(Delivery),信號從發生到投遞之間的狀態,稱為信號未決(Pending)。進程可以選擇阻塞(Block)某個信號。被阻塞的信號產生后將保持在未決狀態,直到進程解除對此信號的阻塞,才執行投遞的動作。

每個信號都有兩個標志位分別表示阻塞和未決,還有一個函數指針表示處理動作。信號產生時,內核在進程控制塊中設置該信號的未決標志(表示某信號是否發生過),直到信號投遞完畢才清除該標志。在上圖的例子中,

1. SIGHUP信號未阻塞也未產生過,當它遞達時執行默認處理動作。

2. SIGINT信號產生過,但正在被阻塞,所以暫時不能投遞。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之后再解除阻塞。

3. SIGQUIT信號未產生過,一旦產生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函數sighandler

未決和阻塞標志可以用相同的數據類型sigset_t來存儲,sigset_t稱為信號集,這個類型可以表示每個信號的“有效”或“無效”狀態,,在阻塞信號集中“有效”和“無效”的含義是該信號是否被阻塞,而在未決信號集中“有效”和“無效”的含義是該信號是否處于未決狀態。

2.3.1.2?信號的生存周期(P97

參考:signal.txt 。對著圖講一下

對于一個完整的信號生命周期(從信號發送到相應的處理函數執行完畢)來說,可以分為三個階段:

l?信號發生?(前面講過主要分硬件和軟件兩個來源)

l?信號在進程中注冊:進程的task_struct結構中有關于本進程中未決信號的數據成員,里面記錄著哪些信號發生了以及對這些信號的處理策略(阻塞,忽略還是執行用戶定義的處理函數),Linux的術語叫disposition(部署)信號發生時,通過調用系統調用陷入內核并在task_struct這個結構中打標記,如果發送給一個處于可運行狀態的進程,則只置相應的域即可。如果信號發送給一個正在睡眠的進程,如果進程睡眠在可被信號中斷的優先級上,則會因為信號臨時喚醒進程;否則僅設置進程表中信號域相應的位,而不喚醒進程。注意,此時信號還只是在隊列中,對進程來說暫時是不知道有信號到來的。

注:信號的部署(disposition)信息是per-process的,這意味這對于多線程的進程,共享同一份信號部署。A child created via fork(2) inherits a copy of its parent's signal dispositions. During an execve(2), the dispositions of handled signals are reset to the default; the dispositions of ignored signals are left unchanged.

l?信號的投遞,執行和注銷:注冊是先在進程的控制結構(task_struct)中記錄下收到了某某信號,然后等到進程即將從內核態返回用戶態的時候,流程才被中斷handle函數才被調用。用戶進程什么時候會從內核態返回用戶態呢?那么首先要說進程何時會進入內核態,一般主要是種情況:

ü?系統調用(用戶進程主動進入內核),信號的產生也會通過系統調用進入內核。

ü?中斷(用戶進程被動進入內核),調度也是由時間中斷導致。

那么當進程由于以上原因進入內核態而需要返回用戶態時,返回有可能是被信號喚醒或者是正常調度重新獲得CPU,在其從內核空間返回到用戶空間時會檢測是否有信號等待處理。如果存在未決信號等待處理且該信號沒有被進程阻塞,則投遞該信號并執行相應的信號處理函數,然后進程會把信號在未決信號結構中注銷(刪除)掉。

我們知道處理信號有三種類型:進程接收到信號后退出;進程忽略該信號;進程收到信號后執行用戶設定用系統調用signal的函數。當進程接收到一個它忽略的信號時,進程丟棄該信號,就象沒有收到該信號似的繼續運行。如果進程收到一個要捕捉的信號,?

2.3.1.3 用戶進程對信號的響應方式:

}?忽略信號(ignore the signal):對信號不做任何處理,但是有兩個信號不能忽略:即SIGKILL及SIGSTOP。

為什么不能忽略:向超級用戶提供一種使進程終止或停止的可靠方法。

}?捕捉信號(catch the signal with a signal handler):類似中斷的處理程序,對于需要處理的信號,進程可以安裝處理函數,由該函數來處理定義信號處理函數,進程從內核態返回用戶態時執行用戶定義的函數(為何不是直接在內核調用?當然不行。內核代碼運行在高CPU特權級別下,如果直接調用handle函數,則handle函數也將在相同的CPU特權下被執行。那么用戶將可以在handle函數里面為所欲為。)。執行完畢后又返回內核態繼續檢查是否還有其他未決信號,直至所有未決信號處理完,所以我們知道進程在用戶態下是不會有未處理完的信號的. 所有未決信號處理完畢后才會決定是否最終返回用戶態執行其他的用戶態程序代碼。(最后是否會返回并執行后繼的代碼還不一定,比如syscallresume問題,但我們目前不講這么深)

}?執行缺省操作(perform the default action):Linux對每種信號都規定了默認操作

對該信號的處理保留系統的默認值,這種缺省操作,對大部分的信號的缺省操作是使得進程終止。進程通過系統調用signal來指定進程對某個信號的處理行為。后面會講到。?

2.3.1.4 使用信號的場合(P99)

}?后臺進程需要使用信號,如xinetd

沒有控制界面,或者守護進程連終端都沒有,用信號來和他們通訊最好

}?如果兩個進程沒有親緣關系,無法使用無名管道

}?如果兩個通信進程之一只能使用標準輸入和標準輸出,則無法使用FIFO

由于某些應用限制不可以為FIFO創建文件

2.3.1.5 SIGNAL列表(P100

每個信號都有一個名字,名字以三個字符SIG開頭。查看signal標準規范:Man 7 signal或者Kill –l

重點點一下SIGSTOPSIGKILL,不能被阻塞block,忽略ignore和處理caught。Others:

SIGSTOP & SIGTSTP

兩個信號都會使進程掛起并可以通過另一個信號SIGCONT恢復,但SIGTSTP一般通過鍵盤產生CTRL+Z,而SIGSTOP可以通過kill命令發送。SIGSTOP不能被忽略和捕獲,SIGTSTP可以。

SIGCHLD:

參考:UNIX系統中wait函數族和SIGCHLD信號的關系:http://blog.csdn.net/soloopin/article/details/8226748

簡單的說,子進程退出時父進程會收到一個SIGCHLD信號,若不處理,默認方式則子進程會成為僵尸。而常規的做法是在這個信號處理函數中調用wait函數獲取子進程的退出狀態并回收僵尸。也可以注冊直接忽略,則系統會將結束的子進程寄養給initinit會回收它。

SIGABORT

abort() ?該函數產生SIGABRT信號并發送給自己,默認情況下導致程序終止

SIGALRM

alarm 函數產生,后面會介紹。

2.3.2 信號相關調用

重點:

l?掌握在程序中發送信號的方法

l?掌握信號的不同處理方式

l?signal函數原型

2.3.2.1 kill()和raise()

}?kill函數同讀者熟知的kill系統命令一樣,可以發送信號給進程或進程組(實際上,kill系統命令只是kill函數的一個用戶接口)。 可以對照man 2 kill 看看解釋

}?kill l 命令查看系統支持的信號列表

}?raise函數允許進程向自己發送信號

舉例:<<<< P105 例子

子進程的打印沒有機會執行就被父進程殺死了 >>>>>>>>>>>

2.3.2.2 alarmpause

對照man手冊講解。

}?alarm()也稱為鬧鐘函數,它可以在進程中設置一個定時器。當定時器指定的時間到時,內核就向進程發送SIGALARM信號。

2?如果不忽略或者不捕捉此信號,默認動作是終止該進程

2?經過指定秒后,信號由內核產生,由于進程調度的延遲,進程得到控制能夠處理該信號還需一段時間,所以該設置的時間不會非常準確。

2?每個進程只能有一個鬧鐘,新鬧鐘會替代老鬧鐘。

2?如果參數seconds0,則之前設置的鬧鐘會被取消 ?實踐中常常會使用,防止多余的鬧鐘又來干擾。

}?pause()函數是用于將調用進程掛起直到收到信號為止。

2?只有執行了一個信號處理程序并從其返回時,pause才返回;如果我們沒有提供處理函數,pause缺省會終止進程,就不會返回了

2?對指定為忽略的信號,pause()不會返回。只有執行了一個信號處理函數,并從其返回,puase()才返回-1,并將errno設為EINTR

舉例:<<<< 參考P109的例子,實現了進程等待sleep 5s

最后一句打印不會出來,為什么? 原因是前述pause的處理邏輯的第一條 >>>>>>>

2.3.2.3 signal –?信號的安裝

信號的安裝的主要方法有兩種

}?使用簡單的signal()函數

}?使用信號集函數族

Linux在支持新版本的信號安裝函數sigation()以及信號發送函數sigqueue()的同時,仍然支持早期的signal()信號安裝函數,支持信號發送函數kill()我們主要介紹使用傳統的signal函數的方式

這里 signal() 這個函數的原型我當時看了頭有點大,還是先說明一下:首先該函數原型整體指向一個無返回值并且帶一個整形參數的函數指針,也就是信號的原始配置函數;接著該原型又帶有兩個參數,其中第2個參數可以是用戶自定義的信號處理函數的函數指針。不明白也沒事,后邊做實驗就明白了,會用就行了。分解為如下比較容易理解:

typedef void (*sighandler_t)(int);

sighandler_t signal(int signum, sighandler_t handler);

更多的signal介紹請看man 7 signal

舉例:<<<<< P112例子

本質上是一個注冊信號處理方式的過程, 參考課件的ppt page97

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

舉例:<<<<< 利用signal實現消除僵尸的兩種方法

回顧消除僵尸的正確方式:1)父進程提前退出;2)真父委托繼父;3)真父wait

l?1已經前面演示過;

l?2涉及signal:繼父方式:如果真父進程的確不想回收。父進程可以通知內核忽略子進程的退出狀態,讓內核來處理,其基本原理是可讓內核把僵尸子進程轉交給init進程去處理,而init一般會主動回收結束的子進程。類似于交給福利院,自己不管了。samples\3.ProcessII\2.3-signal\z_sigign.c演示重點:子進程退出后父進程不會收到signal異步通知。子進程僵尸會被init自動回收。

l?對于3。由真父調用wait, waitpid來接收子進程退出狀態。達到收尸的作用。但簡單使用該方法需要父進程等待子進程結束,會導致父進程掛起。特別是在某些服務器實現上,一般由父進程fork很多子進程,如果需要父進程去長時間等待子進程,會很影響服務器進程的并發性能。其實為了避免父進程長時間等待還有一種方法是用signal函數為SIGCHLD安裝handler,因為子進程結束后, 父進程會收到該信號,可以在handler中調用wait回收。samples\3.ProcessII\2.3-signal\z_sighdlr.c。演示重點:子進程退出后父進程收到signal異步通知。子進程僵尸會被回收。ps aux后面講網絡TCP服務器時還會用到該技術。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

實驗?<<<<< 講解實驗5.3 使用信號實現同步,讓學生自己先做再講解

功能需求:

1)?同步過程 ?見實驗手冊

2)?利用兩個進程(父子進程)來模擬司機和售票員

技術要點:

l?利用kill來發送信號,

l?信號的注冊和捕獲處理

l?父子進程在一個進程組內,如何避免同一個信號不同處理之間的干擾(IGN的作用)

l?Pause函數的使用

畫一個流程圖方便編寫程序

>>>>>>>>>

3.?SystemV IPC

1.1?IPC對象的創建 (P113

重點:

l?講清楚IPC對象,IPC標識符,IPC鍵的概念以及之間的關系。

l?IPC對象的創建方法,包括KEY的創建方法和區別。

參考http://www.embedu.org/column/column616.htm

P113這張圖主要是要說明以下幾個概念并用文件系統對象對比講解

內核對象

文件對象

IPC對象

內核標識符

文件描述符。進程范圍內唯一分配,最小可用值

IPC標識符。系統全局的流水號

進程共享名

文件系統路徑

IPC

創建API

open

Xget

3.1.1 IPC對象

linux中,可以使用IPC對象來進行進程間通信。IPC對象存在于內核中,作為橋梁供多進程操作進行數據通訊

需要注意的是IPC對象是系統范圍內起作用的,創建后進程不刪除它們就退出則會遺留在系統里。為此系統也提供了命令來維護:

查看IPC對象信息

刪除IPC對象

命令:ipcs [-aqms]

參數說明:

1-a:查看全部IPC對象信息。

2-q:查看消息隊列信息。

3-m:查看共享內存信息。

4-s:查看信號量信息。

命令1ipcrm -[qms] ID

命令2ipcrm -[QMS] key

參數說明:

1-q-Q:刪除消息隊列信息。

2-m-M:刪除共享內存信息。

3-s-S:刪除信號量信息。

注意事項:如果指定了qms,則用IPC對象的標識符(ID)作為輸入;如果指定了QMS,則用IPC對象的鍵值(key)作為輸入。

3.1.2 IPC標識符

消息隊列,信號量或者共享存儲段在內核中都有對應的數據結構,每個內核中的IPC結構在內核中都用一個非負的整數作為標識符來唯一標識并加以引用,類似內核中每個文件對應的文件描述符(與文件描述符不同點在于IPC標識符實現為一個系統全局的流水號(文件描述符是進程范圍內唯一),當達到一個整型的最大值后歸零)。

提高:<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

IPC結構的訪問采用了獨立于文件系統的體系。不能像FIFO一樣采用統一文件的方式來訪問他們。比如不能用ls查看IPC對象,也不能用rm來刪除它們也不能用chmod更改其訪問權限。正如前面所說的對V5IPC我們需要用專用的ipcsipcrm。總之這讓IPC看起來和Unix的其他對象不是很統一。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

3.1.3 IPC

l?IPC標識符可以認為是IPC對象的內部名。- 類比文件IO的文件描述符

l?外部名方案:進程訪問IPC對象時使用的是“鍵” –?類比文件IOpathname

每個IPC的對象都有唯一的名字,稱為""(key)。通過"",多個進程能夠識別所用的同一個IPC對象。""IPC對象的關系就如同文件名稱“/A/B/C”于文件,通過文件名,多個進程能夠打開同一個內核文件對象并讀寫文件內的數據,甚至多個進程能夠公用一個文件。而在 IPC的通訊模式下,通過""的使用也使得一個IPC對象能為多個進程所共用。

關鍵字的數據類型由系統定義為ket_t, 通常在頭文件<sys/types.h>被定義為是一個非負長整數。

簡而言之,可以將key類比于文件的文件名稱(文件系統中的路徑)。

l?打開(創建)一個文件 int open(const char *path, int?oflag, ... );?輸入文件路徑,創建時還要指定文件訪問權限,得到文件句柄。

l?打開(創建)一個IPC int shmget(key_t?key, size_t?size, int?shmflg); 輸入keykey可以由文件路徑和項目ID通過ftok產生), 指定shmflg(包含IPC對象訪問權限),得到IPC句柄。

http://pages.ramapo.edu/~vmiller/UNIX/Lecture-IPC.htm

IPC structures have owners and groups. These permissions are similar to file permissions. For example, shared memory may be set up so that it has read/write access for owner processes and it has read only by any process that does not belong to the owner of the shared memory. (This is assuming that the operating system/hardware supports access control to memory.)

3.1.4 IPC結構的創建

在使用Xget過程中,當然可以由人工指定key值(直接使用立即數),但這么做很容易發生不同的應用之間可能會因為使用同一個key而產生沖突(文件路徑沒有這個問題,文件系統中的路徑要存在必然唯一,要么不存在)。為此,Linux系統提供了如下機制產生惟一的關鍵字。

1)?普適方法,使用ftokFtok的入參有兩個,一個是路徑名,另一個是項目id。不同進程之間可以事先協商好這兩個參數。然后都先后(必須的)調用ftok確保產生一個相同的唯一的key。然后某一方用該key調用Xget(需要指明IPC_CREAT參數)創建ID,而另一方則用相同的key調用Xget函數得到已經創建好的IDMan ftok: If the values for path and id are the same as a previous call to ftok() and the file named by path was not deleted and re-created in between calls to ftok(), ftok() will return the same key.

舉例:<<<<< ?samples\3.ProcessII\3.1-ipc\ftok.c

這是具體使用方法,在創建一個消息隊列(其他ipc相同)時,需要先通過文件路徑名和項目ID獲取一個鍵值,然后通過此鍵值由內核生成標識符,在以后可通過此標識符來使用此消息隊列。 >>>>>>>>>>>

2)?如果兩個進程之間有親緣關系,則可以簡化方法1,使用預定義的keyIPC_PRIVATE來直接創建ID然后通過fork或者文件共享的方式將ID傳遞給另外一個進程。。如果子進程還要exec新的程序,則子進程可以用exec的參數將ID傳給新的程序。

3.2 共享內存

重點:

l?了解共享內存的特點

l?掌握使用共享內存的方法

3.2.1概述

SysV5IPC中最有價值的就是共享內存(+信號量為輔助)

}?共享內存是一種最為高效的進程間通信方式,進程可以直接讀寫內存,而不需要任何數據的拷貝

最快的IPC,使用共享存儲的唯一竅門是多個進程之間對一給定存儲區的同步存儲。后面要說的信號量就是用來實現serverclient對共享存儲的同步。

}?為了在多個進程間交換信息,內核專門留出了一塊內存區,可以由需要訪問的進程將其映射到自己的私有地址空間

4G進程空間中專門開辟了一塊地址范圍用于映射共享內存。見下附圖。課件p115也是在說這個事情。

}?進程就可以直接讀寫這一內存區而不需要進行數據的拷貝,從而大大提高的效率。

}?由于多個進程共享一段內存,因此也需要依靠某種同步機制,如互斥鎖和信號量等

Memory layout on an Intel-based Linux system

?

3.2.2 共享內存實現(P116

函數

說明

int shmget(key_t key, int size, int shmflg);

l?功能說明:分配一塊共享內存

l?返回值:調用成功返回一個shmid(類似打開一個或創建一個文件獲得的文件描述符一樣);調用失敗返回-1

l?參數說明

2?Key: 前面介紹過

2?size是要建立共享內存的長度。所有的內存分配操作都是以頁為單位的。所以如果一個進程只申請一塊只有一個字節的內存,內存也會分配整整一頁(i386機器中一頁的缺省大小PACE_SIZE = 4096字節)

2?shmflg有效的標志包括IPC_CREAT IPC_EXCL,他們的功能與open()O_CREATO_EXCL相當。

ü?IPC_CREAT:如果共享內存不存在,則創建一個共享內存,否則直接打開已存在的

ü?IPC_EXCL:只有在共享內存不存在的時候,新的共享內存才建立,否則就產生錯誤

例子一:假設鍵值為key,創建一個共享內存大小為4k,訪問權限為066,如果已經存在則返回其標識號

int shmid;

if( (shmid = shmget(key,4 * 1024,0666 | IPC_CREAT)) < 0)?{

????perror("Fail to shmget");

????exit(EXIT_FAILURE)

}

例子二、假設鍵值為key,創建一個共享內存大小為1k,訪問權限為0666,如果已經存在則報錯

int shmid;

if((shmid ?= shmget(key,1024,0666 | IPC_CREAT | IPC_EXCL)) < 0)?{

????perror("Fail to shmget");

????exit(EXIT_FAILURE);

}

總結Xget的使用方法-如何創建一個IPC對象。Xget函數都有兩個類似的參數,key flag。如果滿足下列兩個條件之一,則在內核中創建一個新的IPC結構。:

l?KeyIPC_PRIVATE

l?Key不是IPC_PRIVATE,比如由ftok+ flag=IPC_CREAT創建并且該Key未與一個已存在的IPC對象綁定。

void *shmat(int shmid, const void *shmaddr, int shmflg);

?

l?功能說明:函數shmat將標識號為shmid共享內存映射到調用進程的地址空間中。

l?返回值 :調用成功放回映射后的地址 ,出錯放回(void *)-1

l?參數說明:

2?shmid ?: ?要映射的共享內存區標識符

2?shmaddr ?: ?指定共享內存映射到的虛擬地址(若為NULL,則表示由系統自動完成映射) 一般shmaddr填寫為NULL/0,讓系統內核替我們選擇地址。除非你寫的程序特定于某種需要手工指定共享地址(VM)。

2?shmflg ?: ?SHM_RDONLY ?共享內存只讀。默認0:共享內存可讀寫。

int shmdt(const void *shmaddr);

?

l?功能說明:取消共享內存與用戶進程之間的映射

l?參數說明: shmaddrshmat映射成功放回的地址。

注意:當一個進程不再需要共享內存段時,它將調用shmdt()系統調用取消這個段,但是,這并不是從內核真正地刪除這個段,而是把相關shmid_ds結構的shm_nattch域的值減1,當這個值為0時,內核才從物理上刪除這個共享段。

用完共享存儲段后,將進程和該共享存儲段脫離。這并不是從系統中刪除其標識符以及其數據結構,直到某個進程(一般是創建者server)調用shmctlIPC_RMID)特地刪除它。

int shmctl(int shmid, ?int cmd, ?struct shmid_ds ?*buf);

?

l?功能說明:控制共享內存

l?參數說明:

2?shmid ?共享內存標識ID

2?cmd

ü?IPC_STAT得到共享內存的狀態

ü?IPC_SET改變共享內存的狀態

ü?IPC_RMID刪除共享內存

2?buf ?是一個結構體指針。IPC_STAT的時候,取得的狀態放在這個結構體中。如果要改變共享內存的狀態,用這個結構體指定;

l?注意:

2?IPC_RMID命令實際上不從內核刪除一個段,而是僅僅把這個段標記為刪除,實際的刪除發生最后一個進程離開這個共享段時。

2?2.cmdIPC_RMID時,第三個參數應為NULL。呵呵,大部分我們都是這樣做,用這個函數刪除共享內存。

參考:LINUX共享內存使用常見陷阱與分析http://www.dcshi.com/?p=79

舉例 <<<<<< 課件(P121) ?同實驗5.4 ?實驗5.4也是參考的P121的講義

Demo 時注意顯示的下列列

shmid ?????owner ?????perms ?????bytes ?????nattch

實際的例子見信號量實驗5.6>>>>>>>>>>>>>>>>>>

實驗 <<<<< 實驗5.6POSIX信號量版本。因為還沒有講到SysV信號燈,所以這個實驗要用到POSIX信號燈。

要求:?samples\3.ProcessII\3.2-shm\shm.c

需要講解的注意點:

l?AB可以有親緣關系,也可以沒有。簡單點用fork做。

l?利用兩個POSIX無名信號燈實現同步A,B兩個進程對共享內存的先寫再讀,先:A負責從stdin讀一行后寫入共享區,后:B負責從共享區讀出并打印到屏幕上。提高:實現循環讀和寫,并且當A進程從stdin讀到“quit”時,在將quit寫入共享區后退出,B進程讀到“quit”后也退出。

l?信號量也需要放在進程間的共享內存區中,因為回憶前面線程間同步的AB例子,這里是兩個進程都要訪問信號量。需要將結構的概念重復,很多學生對這個想不通。

l?gcc 時要加上-lpthread?

更多有關共享內存同步的內容參考:http://www.embedu.org/Column/Column798.htm?>>>>>>>>>>>>>>>>

3.3 信號燈(信號量)(P135-P136

重點:

l?信號燈提前到隊列前講,我認為共享內存和信號燈是兩個關系很密切的IPC對象類型,消息隊列次要一點。

l?理解信號燈集相關概念

l?掌握信號燈集使用方法

l?使用信號燈集實現對共享內存的訪問控制

注意不要和信號(Signal)混淆

}?信號燈(semaphore),也叫信號量。它是不同進程間或一個給定進程內部不同線程間同步的機制。

}?信號燈種類:

根據目前的講解給學生總結一下

}?posix有名信號燈

主要用戶進程間的同步

}?posix基于內存的信號燈(無名信號燈)

可用于進程間和線程間的同步

}?System V信號燈(IPC對象)

Page136是在講解基本的信號量的概念

}?二值信號燈:值為0或1。與互斥鎖類似,資源可用時值為1,不可用時值為0。

}?計數信號燈:值在0到n之間。用來統計資源,其值代表可用資源數

}?等待操作是等待信號燈的值變為大于0,然后將其減1;而釋放操作則相反,用來喚醒等待資源的進程或者線程

回憶線程時的PV概念。為了獲得共享資源,進程執行如下操作:

1)測試控制該資源的信號量

2)若此信號量的值為正,則進程可以使用該資源,然后進程將信號量減1

3)若此信號量的值為0,則進程進入休眠,直到信號量值大于0,進程被喚醒,返回第一步。

3.3.1 SystemV 信號燈

}?System V的信號燈是一個或者多個信號燈的一個集合。其中的每一個都是單獨的計數信號燈。而Posix信號燈指的是單個計數信號燈

systemV的信號燈實現不是一個簡單的非負整數,而是一個比較復雜的數據結構集合。詳細見p138頁的圖。大概講一下內核數據結構。

}?System V 信號燈由內核維護

}?主要函數semget,semop,semctl

3.3.2 信號燈函數

函數

說明

int semget(key_t key, int nsems, int semflg);

?

創建一個信號燈集

功能:創建一個信號燈集并返回這個信號燈集的ID 或直接返回一個已經存在的信號燈集的ID

參數說明:

l?key:如果key值為IPC_PRIVATEkey0并且semflg設置了IPC_CREAT,此時調用此函數總是創建一個新的信號燈集(我們在共享內存的時候驗證過,還記的嗎?其實system v ipc對象的創建機制都很類似。

l?nsems:指定這個信號燈集中信號燈的個數(每個信號燈代表了某一類資源)

l?semflg:可以指定為IPC_CREAT | 0666,其含義為,不存在則創建,訪問權限為0666。我們也可以通過IPC _CREAT | IPC_EXCL一起使用的時候確定要創建的信號燈集是否存在,如果存在此時這個函數放回-1

int semop ( int semid, struct sembuf ?*opsptr, ?size_t ?nops);

?

功能:semop系統調用可以實現對由semid標志的信號燈集中的某一個指定信號燈的一系列操作

參數說明:

l?semid信號燈集的標識ID

l?opsptr: 指向結構體sembuf的指針,指向一個結構體數組的指針。數組的每個成員是一個sembuf,用來設置信號燈集中某些個信號燈的工作方式。

struct sembuf {

short ?sem_num; ?// ?要操作的信號燈的編號

short ?sem_op; ??// ??0 : ?調用者阻塞等待,直到信號燈的值等于0時返回。可以用來測試共享資源是否已用完。

// ??1 ?: ?釋放資源,V操作

// ??-1 : ?分配資源,P操作 ???????????????????

short ?sem_flg; //

a. ??0 ?代表阻塞調用(資源不滿足阻塞)

b. ??IPC_NOWAIT ?代表非阻塞調用

c. ??如果設置了SEM_UNDO標志,那么在進程結束時,相應的操作將被取消,這是一個比較重要的一個標志。

};

例子:

struct sembuf sops[2];

int semid;

/* Code to set semid omitted */

sops[0].sem_num = 0; ???????/* Operate on semaphore 0 */

sops[0].sem_op = 0; ????????/* Wait for value to equal 0 */

sops[0].sem_flg = 0;

sops[1].sem_num = 0; ???????/* Operate on semaphore 0 */

sops[1].sem_op = 1; ????????/* Increment value by one */

sops[1].sem_flg = 0;

if (semop(semid, sops, 2) == -1) {

perror("semop");

exit(EXIT_FAILURE);

}

l?nops: ?要操作的信號燈的個數

案例:封裝一個P操作和一個V操作

//P操作

int my_sem_wait(int semid,int sem_num)

{

????struct sembuf ?op;

????op.sem_num = sem_num;

????op.sem_op = -1;

????op.sem_flg = 0;

????if(semop(sem_id,&op,1) < 0) {

perror("fail to semop");

exit(-1);

????}

????return 0;

}

//V操作

int my_sem_wait(int semid,int sem_num)

{

????struct sembuf ?op;

????op.sem_num = sem_num;

????op.sem_op = 1;

????op.sem_flg = 0;

????if(semop(sem_id,&op,1) < 0) {

perror("fail to semop");

exit(-1);

????}

????return 0;

}

int semctl ( int semid, int semnum, ?int cmd…/*union semun arg*/);

控制信號燈集

參數說明:

l?semid : 信號燈集ID

l?semnum:要修改的信號燈編號(創建信號燈集時,信號燈的編號從0開始)

l?cmd:

n?IPC_STAT ?獲取信號燈信息,信息由arg.buf(即第四個參數)返回

n?GETVAL ?: 獲取semnum信號燈的值

n?SETVAL ?: 設置semnum信號燈的值

n?IPC_RMID : 從系統中刪除semnum所代表的信號燈 ?

l?最后一個是可變參數,cmdGETVALSETVAL時,需要傳遞第四個參數,其參數類型為 union semun。這個結構體的類型必須在應用程序中定義

定義如下,必須在應用程序中自己定義如下類型

union semun?{

??int ??val;

??struct ?semid_ds ?*buf;

??unsigned ?short ?*array;

??struct ?seminfo ?* ?__buf;

};

例子1

union semun mysemun;

mysemun.val = 1;

//第一個信號燈的編號為0

if ( ( semctl ( ?semid ?, ?0 ?, ?SETVAL,mysemun)) < 0)?{

????perror("Fail to semctl");

????exit(EXIT_FAILURE);

}

例子2:刪除一個信號燈

if( ?semctl ?( ?semid ?, ?0 ?, ?IPC_RMID ?, ?0 ) < 0 )?{

????perror("Fail to semctl ?IPC_RMID");

????exit(EXIT_FAILURE);

}

3.3.3 信號燈的典型應用:

實驗:<<<<< SysV 信號燈實現前面講共享內存區時的例子。

講解時注重把SysV的信號燈和POSIX的信號燈做個比較 \labs\5.6

>>>>>>>>>>>>>>>>>>

3.4消息隊列(看時間允許就講,可選)

重點:

l?掌握消息隊列的使用方法

l?使用消息隊列實現多個進程之間的復雜通信

參考:

http://www.cnblogs.com/Anker/archive/2013/01/07/2848869.html

http://blog.csdn.net/anonymalias/article/details/9829417

消息隊列是由存放在內核中的消息組成的鏈表,由IPC id標識。

msgget創建新隊列或打開已經存在的隊列

msgsnd將消息添加到消息隊列尾,每個消息包括正整數標識的類型,非負的長度,及數據。

msgrcv從消息隊列中取消息,不必按FIFO取消息,可以通過類型字段取相應的消息。

函數

說明

int msgget(key_t key, int flag);

key是一個鍵值,由ftok獲得;

msgflg參數是一些標志位。該調用返回與健值key相對應的消息隊列描述字。

在以下兩種情況下,該調用將創建一個新的消息隊列:

1)?如果沒有消息隊列與健值key相對應,并且msgflg中包含了IPC_CREAT標志位;

2)?key參數為IPC_PRIVATE;參數msgflg可以為以下:IPC_CREATIPC_EXCLIPC_NOWAIT或三者的或結果。

舉例:<<<<<< samples\3.ProcessII\3.3-msgq\?msgget.c

運行ipcs看看效果 ?>>>>>>>>>>>>>

int msgsnd(int msqid, const void *msgp, size_t size, int flag);

msqid:消息隊列的描述符;

msgp:指向存放消息的緩沖區,該緩沖區中包含消息類型消息體兩部分內容。該緩沖區的結構是由用戶定義的,在<sys/msg.h>中有關于該緩沖區結構定義的參版考模:與一個隊列中的每個消息相關聯的類型字段提供了兩個特性:

1)類型字段可以用于標識消息,從而允許多個進程在單個隊列上復用消息。

2)類型字段可以用做優先級,允許接收者以不同于先進先出的某個順序讀出各個消息。

size:緩沖區中消息體部分的長度;

flag:設置操作標志。可以為0IPC_NOWAIT;用于在消息隊列中沒有可用的空間時,調用線程采用何種操作方式。

標志為IPC_NOWAIT,表示msgsnd操作以非阻塞的方式進行,在消息隊列中沒有可用的空間時,msgsnd操作會立刻返回。并指定EAGAIN錯誤;

標志為0,表示msgsnd操作以阻塞的方式進行,這種情況下在消息隊列中沒有可用的空間時調用線程會被阻塞,直到下面的情況發生:

l?等到有存放消息的空間;

l?消息隊列從系統中刪除,這種情況下回返回一個EIDRM錯誤;

l?調用線程被某個捕捉到的信號中斷,這種情況下返回一個EINTR錯誤;

int msgrcv(int msgid, ?void* msgp, ?size_t ?size, ?long msgtype, ?int ?flag);

msqid:消息隊列的描述符;

msgp:指向待存放消息的緩沖區,該緩沖區中將會存放接收到的消息的消息類型和消息體兩部分內容。該緩沖區的結構是由用戶定義的,和msgsnd相對應。

size:緩沖區中能存放消息體部分的最大長度,這也是該函數能返回的最大數據量;該字段的大小應該是sizeof(msg buffer) - sizeof(long)

msgtyp:希望從消息隊列中獲取的消息類型。

l?msgtyp0,返回消息隊列中的第一個消息;

l?msgtyp > 0,返回該消息類型的第一個消息;

l?msgtyp < 0,返回小于或等于msgtyp絕對值的消息中類型最小的第一個消息;

flag:設置操作標志。可以為0IPC_NOWAITMSG_NOERROR;用于在消息隊列中沒有可用的指定消息時,調用線程采用何種操作方式。

l?標志為IPC_NOWAIT,表示msgrcv操作以非阻塞的方式進行,在消息隊列中沒有可用的指定消息時,msgrcv操作會立刻返回,并設定errnoENOMSG

l?標志為0,表示msgrcv操作是阻塞操作,直到下面的情況發生:

ü?消息隊列中有一個所請求的消息類型可以獲取;

ü?消息隊列從系統中刪除,這種情況下回返回一個EIDRM錯誤;

ü?調用線程被某個捕捉到的信號中斷,這種情況下返回一個EINTR錯誤;

l?標志為MSG_NOERROR,表示接收到的消息的消息體的長度大于msgsz長度時,msgrcv采取的操作。如果設置了該標志msgrcv在這種情況下回截斷數據部分,而不返回錯誤,否則返回一個E2BIG錯誤。

int msgctl ( int msgqid, int cmd, struct msqid_ds *buf );

msqid:消息隊列的描述符;

cmd:控制操作的命令,SUS標準提供以下三個命令:

ü?IPC_RMID,刪除一個消息隊列。執行該命令系統會立刻把該消息隊列從內核中刪除,該消息隊列中的所有消息將會被丟棄。這和已經討論過的POSIX消息隊列有很大差別,POSIX消息隊列通過調用mq_unlink來從內核中刪除一個消息隊列,但消息隊列的真正析構會在最后一個mq_close結束后發生。

ü?IPC_SET,根據buf的所指的值來設置消息隊列msqid_ds結構中的msg_perm.uidmsg_perm.gidmsg_perm.modemsg_qbytes四個成員。

ü?IPC_STAT,通過buf返回當前消息隊列的msqid_ds結構。

Linux下還有例如IPC_INFOMSG_INFO等命令,具體可以參考Linux手冊;

buf:指向msqid_ds結構的指針;消息隊列緩沖區

舉例:<<<<<< 課件P131-P134例子 samples\3.ProcessII\3.3-msgq\?msgq.c?>>>

舉例: <<<<如何定義消息的格式?samples\3.ProcessII\3.3-msgq\?msgq_t.c

解決方法:根據應用的需求,擴展結構體的成員。>>>>>>>>>>>>>

實驗:<<<<<<<< 實驗5.5使用消息隊列實現本地聊天室 >>>>>>>>>>>>>>>>>>>

4.?進程間通訊方式比較

除了ppt上的總結。增加自己從APUE上得到的總結:

l?從實際實現的簡單易用性出發,pipeFIFO依然是值得推薦的常用本地進程間通訊的方法。

l?SystemV提供的方法不推薦在新的程序中使用,不過還是要了解因為SystemVIPC實在太經典,其存在的時間比POSIX長,支持它的OS也更多(移植性好),在很多已有的代碼中隨處可見,使用更普及。實際上對應的POSIX標準(比較新)就是在SystemV的基礎上改進而來的,從接口定義和效率上都比SystemV有一定的改進,對于新寫的程序建議往POSIX標準上靠攏。但在我們這里就不展開講POSIX了,大家有興趣可以以后自己看。

l?SystemV中我們重點講了共享內存和信號量。shm:效率最高(直接訪問內存) ,需要同步、互斥機制。sem:配合共享內存使用,用以實現同步和互斥。消息隊列講得較少,相對來說應用也沒有共享內存方便。

總結

以上是生活随笔為你收集整理的Linux下进程间通信概述的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。