概述 進程間通信(IPC,Interprocess communication)是一組編程接口,讓程式設計師能夠協調不同的進程,使之能在一個作業系統里同時運行,並相互傳遞、交換信息。這使得一個程式能夠在同一時間裡處理許多用戶的要求。因為即使只有一個用戶發出要求,也可能導致一個作業系統中多個進程的運行,進程之間必須互相通話。IPC接口就提供了這種可能性。每個IPC方法均有它自己的優點和局限性,一般,對於單個程式而言使用所有的IPC方法是不常見的。 IPC方法包括管道(PIPE)、訊息排隊、旗語、共用記憶體以及套接字(Socket)。
主要分類 種類 進程間通信主要包括管道, 系統IPC(包括
訊息佇列 ,信號,共享存儲), 套接字(SOCKET).
管道包括三種:
1)普通管道PIPE, 通常有兩種限制,一是單工,只能單向傳輸;二是只能在父子或者兄弟進程間使用.
2)流管道s_pipe: 去除了第一種限制,為半雙工,可以雙向傳輸.
3)
命名管道 :name_pipe, 去除了第二種限制,可以在許多並不相關的進程之間進行通訊.
識別 系統IPC的三種方式類同,都是使用了核心里的
標識符 來識別.
答: 其實管道的使用方法與檔案類似,都能使用read,write,open等普通IO函式. 管道描述符來類似於檔案描述符. 事實上, 管道使用的描述符,
檔案指針 和檔案描述符最終都會轉化成系統中SOCKET描述符. 都受到系統核心中SOCKET描述符的限制. 本質上LINUX核心源碼中管道是通過空檔案來實現.
FAQ2: 管道的使用方法?
答: 主要有下面幾種方法: 1)pipe, 創建一個管道,返回2個管道描述符.通常用於父子進程之間通訊. 2)popen, pclose: 這種方式只返回一個管道描述符,常用於通信另一方是stdin or stdout; 3)mkpipe:
命名管道 , 在許多進程之間進行互動.
FAQ3: 管道與系統IPC之間的優劣比較?
答: 管道: 優點是所有的UNIX實現都支持, 並且在最後一個訪問管道的進程終止後,管道就被完全刪除;缺陷是管道只允許單向傳輸或者用於父子進程之間.
系統IPC: 優點是功能強大,能在毫不相關進程之間進行通訊; 缺陷是
關鍵字 KEY_T使用了
核心 標識,占用了核心資源,而且只能被顯式刪除,而且不能使用SOCKET的一些機制,例如select,epoll等.
FAQ4: WINDOS進程間通信與LINUX進程間通信的關係?
答: 事實上,WINDOS的
進程通信 大部分移植於UNIX, WINDOS的剪貼簿,檔案映射等都可從UNIX進程通信的共享存儲中找到影子.
FAQ5: 進程間通信與執行緒間通信之間的關係?
答: 因為WINDOWS運行的實體是執行緒, 狹義上的進程間通信其實是指分屬於不同進程的執行緒之間的通訊.而單個進程之間的
執行緒同步 問題可歸併為一種特殊的進程通信.它要用到
核心 支持的
系統調用 來保持執行緒之間同步. 通常用到的一些執行緒同步方法包括:Event, Mutex,
信號量 Semaphore,
臨界區 資源等.
IPC目的 1)數據傳輸:一個進程需要將它的數據傳送給另一個進程,傳送的數據量在一個位元組到幾兆位元組之間。
2)共享數據:多個進程想要操作共享數據,一個進程對共享數據的修改,別的進程應該立刻看到。
3)通知事件:一個進程需要向另一個或一組進程傳送訊息,通知它(它們)發生了某種事件(如進程終止時要通知
父進程 )。
4)資源共享:多個進程之間共享同樣的資源。為了作到這一點,需要
核心 提供鎖和同步機制。
5)進程控制:有些進程希望完全控制另一個進程的執行(如Debug進程),此時控制進程希望能夠攔截另一個進程的所有陷入和異常,並能夠及時知道它的狀態改變。
進程通過與核心及其它進程之間的互相通信來協調它們的行為。Linux支持多種進程間通信(IPC)機制,信號和管道是其中的兩種。除此之外,Linux還支持System V 的IPC機制(用首次出現的Unix版本命名)。
信號 信號(Signals )是Unix系統中使用的最古老的進程間通信的方法之一。作業系統通過信號來通知進程系統中發生了某種預先規定好的事件(一組事件中的一個),它也是用戶進程之間通信和同步的一種原始機制。一個鍵盤中斷或者一個錯誤條件(比如進程試圖訪問它的
虛擬記憶體 中不存在的位置等)都有可能產生一個信號。Shell也使用信號向它的子進程傳送
作業控制 信號。
信號是在Unix System V中首先引入的,它實現了15種信號,但很不可靠。BSD4.2解決了其中的許多問題,而在BSD4.3中進一步加強和改善了信號機制。但兩者的接口不完全兼容。在Posix 1003.1標準中做了一些強行規定,它定義了一個標準的信號接口,但沒有規定接口的實現。目前幾乎所有的Unix變種都提供了和Posix標準兼容的信號實現機制。
一、 在一個信號的生命周期中有兩個階段:生成和傳送。當一個事件發生時,需要通知一個進程,這時生成一個信號。當進程識別出信號的到來,就採取適當的動作來傳送或處理信號。在信號到來和進程對信號進行處理之間,信號在進程上掛起(pending)。
核心 為進程生產信號,來回響不同的事件,這些事件就是信號源。主要的信號源如下:
異常:進程運行過程中出現異常;
其它進程:一個進程可以向另一個或一組進程傳送信號;
終端中斷:Ctrl-C,Ctrl-\等;
分配額:CPU逾時或檔案大小突破限制;
通知:通知進程某事件發生,如I/O就緒等;
報警:計時器到期。
在 Linux 中,信號的種類和數目與硬體平台有關。
核心 用一個字代表所有的信號,每個信號占一位,因此一個字的位數就是系統可以支持的最多信號種類數。i386 平台上有32 種信號,而Alpha AXP 平台上最多可有 64 種信號。系統中有一組定義好的信號,它們可以由核心產生,也可以由系統中其它有許可權的進程產生。可以使用kill命令列出系統中的信號集。
下面是幾個常見的信號。
SIGHUP: 從終端上發出的結束信號;
SIGINT: 來自鍵盤的中斷信號(Ctrl-C);
SIGQUIT:來自鍵盤的退出信號(Ctrl-\);
SIGFPE: 浮點異常信號(例如
浮點運算 溢出);
SIGKILL:該信號結束接收信號的進程;
SIGALRM:進程的定時器到期時,傳送該信號;
SIGTERM:kill 命令發出的信號;
SIGCHLD:標識子進程停止或結束的信號;
SIGSTOP:來自鍵盤(Ctrl-Z)或調試程式的停止執行信號;
…………
每一個信號都有一個預設動作,它是當進程沒有給這個信號指定處理程式時,核心對信號的處理。有5種預設的動作:
異常終止(abort):在進程的當前目錄下,把進程的
地址空間 內容、暫存器內容保存到一個叫做core的檔案中,而後終止進程。
退出(exit):不產生core檔案,直接終止進程。
忽略(ignore):忽略該信號。
停止(stop):掛起該進程。
繼續(continue):如果進程被掛起,則恢復進程的運行。否則,忽略信號。
進程可以對任何信號指定另一個動作或
重載 預設動作,指定的新動作可以是忽略信號。進程也可以暫時地阻塞一個信號。因此進程可以選擇對某種信號所採取的特定操作,這些操作包括:
忽略信號:進程可忽略產生的信號,但 SIGKILL 和 SIGSTOP 信號不能被忽略,必須處理(由進程自己或由
核心 處理)。進程可以忽略掉系統產生的大多數信號。
阻塞信號:進程可選擇阻塞某些信號,即先將到來的某些信號記錄下來,等到以後(解除阻塞後)再處理它。
由進程處理該信號:進程本身可在系統中註冊處理信號的處理程式地址,當發出該信號時,由註冊的處理程式處理信號。
由核心進行預設處理:信號由核心的預設處理程式處理,執行該信號的預設動作。例如,進程接收到SIGFPE(浮點異常)的預設動作是產生core並退出。大多數情況下,信號由
核心 處理。
需要指出的是,對信號的任何處理,包括終止進程,都必須由接收到信號的進程來執行。而進程要執行
信號處理 程式,就必須等到它真正運行時。因此,對信號的處理可能需要延遲一段時間。
信號沒有固有的優先權。如果為一個進程同時產生了兩個信號,這兩個信號會以任意順序出現在進程中並會按任意順序被處理。另外,也沒有機制用於區分同一種類的多個信號。如果進程在處理某個信號之前,又有相同的信號發出,則進程只能接收到一個信號。進程無法知道它接收了1個還是42個SIGCONT信號。
管道 普通的Linux shell都允許重定向,而重定向使用的就是管道。例如:
$ ls | pr | lpr
把命令ls(列出目錄中的檔案)的輸出通過管道連線到命令pr的標準輸入上進行
分頁 。最後,命令pr的標準輸出通過管道連線到命令lpr的標準輸入上,從而在預設印表機上列印出結果。進程感覺不到這種重定向,它們和平常一樣地工作。正是shell建立了進程之間的臨時管道。
管道是單向的、先進先出的、無結構的、固定大小的
位元組流 ,它把一個進程的標準輸出和另一個進程的標準輸入連線在一起。寫進程在管道的尾端寫入數據,讀進程在管道的首端讀出數據。數據讀出後將從管道中移走,其它讀進程都不能再讀到這些數據。管道提供了簡單的流控制機制。進程試圖讀空管道時,在有數據寫入管道前,進程將一直阻塞。同樣,管道已經滿時,進程再試圖寫管道,在其它進程從管道中移走數據之前,寫進程將一直阻塞。
傳統上有很多種實現管道的方法,如利用檔案系統、利用
套接字 (sockets)、利用流等。在Linux中,使用兩個file數據結構來實現管道。這兩個file數據結構中的f_inode(f_dentry)
指針 指向同一個臨時創建的VFS I節點,而該VFS I節點本身又指向記憶體中的一個物理頁,如圖5.1所示。兩個file數據結構中的f_op指針指向不同的檔案操作例程向量表:一個用於向管道中寫,另一個用於從管道中讀。這種實現方法掩蓋了底層實現的差異,從進程的角度來看,讀寫管道的
系統調用 和讀寫普通檔案的普通系統調用沒什麼不同。當寫進程向管道中寫時,位元組被拷貝到了共享數據頁,當讀進程從管道中讀時,位元組被從共享頁中拷貝出來。Linux必須同步對於管道的存取,必須保證管道的寫和讀步調一致。Linux使用鎖、
等待佇列 和信號(locks,wait queues and signals)來實現同步。
管道示意圖 右圖 --管道示意圖所示
參見include/linux/inode_fs.h
當寫進程向管道寫的時候,它使用標準的write
庫函式 。這些庫函式(read、write等)要求傳遞一個
檔案描述符 作為參數。檔案描述符是該檔案對應的file數據結構在進程的file數據結構數組中的索引,每一個都表示一個打開的檔案,在這種情況下,是打開的管道。Linux
系統調用 使用描述這個管道的file數據結構中f_op所指的write例程,該write例程使用表示管道的VFS I 節點中存放的信息,來管理寫請求。如果共享數據頁中有足夠的空間能把所有的
位元組 都寫到管道中,而且管道沒有被讀進程鎖定,則Linux就在管道上為寫進程加鎖,並把位元組從進程的
地址空間 拷貝到共享數據頁。如果管道被讀進程鎖定或者共享數據頁中沒有足夠的空間,則當前進程被迫睡眠,它被掛在管道I節點的
等待佇列 中等待,而後調用調度程式,讓另外一個進程運行。睡眠的寫進程是可以中斷的(interruptible),所以它可以接收信號。當管道中有了足夠的空間可以寫數據,或者當鎖定解除時,寫進程就會被讀進程喚醒。當數據寫完之後,管道的VFS I 節點上的鎖定解除,在管道I節點的等待佇列中等待的所有讀進程都會被喚醒。
參見fs/pipe.c pipe_write()
從管道中讀取數據和寫數據非常相似。Linux允許進程無阻塞地讀檔案或管道(依賴於它們打開檔案或者管道的模式),這時,如果沒有數據可讀或者管道被鎖定,
系統調用 會返回一個錯誤。這意味著進程會繼續運行。另一種方式是阻塞讀,即進程在管道I節點的
等待佇列 中等待,直到寫進程完成。
如果所有的進程都完成了它們的
管道操作 ,則管道的I節點和相應的共享數據頁會被廢棄。
參見fs/pipe.c pipe_read()
Linux也支持
命名管道 (也叫FIFO,因為管道工作在先入先出的原則下,第一個寫入管道的數據也是第一個被讀出的數據)。與管道不同,FIFO不是臨時的對象,它們是檔案系統中真正的實體,可以用
mkfifo 命令創建。只要有合適的訪問許可權,進程就可以使用FIFO。FIFO的打開方式和管道稍微不同。一個管道(它的兩個file數據結構、VFS I
節點 和共享數據頁)是一次性創建的,而FIFO已經存在,可以由它的用戶打開和關閉。Linux必須處理在寫進程打開FIFO之前讀進程對它的打開,也必須處理在寫進程寫數據之前讀進程對管道的讀。除此以外,FIFO幾乎和管道的處理完全一樣,而且它們使用一樣的數據結構和操作。
從IPC的角度看,管道提供了從一個進程向另一個進程傳輸數據的有效方法。但是,管道有一些固有的局限性:
因為讀數據的同時也將數據從管道移去,因此,管道不能用來對多個接收者廣播數據。
管道中的數據被當作
位元組流 ,因此無法識別信息的邊界。
如果一個管道有多個讀進程,那么寫進程不能傳送數據到指定的讀進程。同樣,如果有多個寫進程,那么沒有辦法判斷是它們中那一個傳送的數據。
系統V 系統V IPC機制(System V IPC Mechanisms)
前面討論的信號和管道雖然可以在進程之間通信,但還有許多應用程式的IPC需求它們不能滿足。因此在System V UNIX(1983)中首次引入了另外三種進程間通信機制(IPC)機制:
訊息佇列 、信號燈和
共享記憶體 (message queues,semaphores and shared memory)。它們最初的設計目的是滿足事務式處理的套用需求,但目前大多數的UNIX供應商(包括基於BSD的供應商)都實現了這些機制。 Linux完全支持Unix System V中的這三種IPC機制。
System V IPC機制共享通用的認證方式。進程在使用某種類型的IPC資源以前,必須首先通過
系統調用 創建或獲得一個對該資源的引用
標識符 。進程只能通過系統調用,傳遞一個唯一的引用標識符到
核心 來訪問這些資源。在每一種機制中,對象的引用標識符都作為它在資源表中的索引。但它不是直接的索引,需要一個簡單的操作來從引用標識符產生索引。對於System V
IPC對象 的訪問,使用訪問許可權檢查,這很象對檔案訪問時所做的檢查。System V IPC對象的訪問許可權由對象的創建者通過系統調用設定。
系統中表示System V IPC對象的所有Linux數據結構中都包括一個ipc_perm數據結構,用它記錄IPC資源的認證信息。其定義如下:
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_tmode;
unsigned short seq;
};
在ipc_perm數據結構中包括了創建者進程的用戶和組標識、
所有者 進程的用戶和組標識、對於這個對象的訪問模式(屬主、組和其它)以及
IPC對象 的鍵值(key)。Linux通過key 來定位System V IPC對象的引用
標識符 ,每個IPC對象都有一個唯一的key。Linux支持兩種key:公開的和私有的。如果key是公開的,那么系統中的任何進程,只要通過了許可權檢查,就可以找到它所對應的System V IPC對象的引用標識符。System V IPC對象不能直接使用key來引用,必須使用它們的引用標識符來引用。(參見include/linux/ipc.h)每種IPC機制都提供一種
系統調用 ,用來將鍵值(key)轉化為對象的引用
標識符 。
對所有的System V IPC,Linux提供了一個統一的系統調用:sys_ipc,通過該函式可以實現對System V IPC的所有操作。函式sys_ipc的定義如下:
int sys_ipc (uint call, int first, int second,
int third, void *ptr, long fifth)
這裡call是一個32位的整數,其低16位指明了此次調用所要求的工作。對不同的call值,其餘各參數的意義也不相同。以下將分別介紹各IPC機制及其所提供的操作。
Message Queues(訊息佇列)
訊息佇列就是訊息的一個鍊表,它允許一個或多個進程向它寫訊息,一個或多個進程從中讀訊息。Linux維護了一個訊息佇列向量表:msgque,來表示系統中所有的訊息佇列。其定義如下:
struct msqid_ds *msgque[MSGMNI];
該向量表中的每一個元素都是一個指向msqid_ds數據結構的
指針 ,而一個msqid_ds數據結構完整地描述了一個
訊息佇列 。
MSGMNI的值是128,就是說,系統中同時最多可以有128個訊息佇列。
msqid_ds數據結構的定義如下:
struct msqid_ds {
struct ipc_perm msg_perm;
struct msg *msg_first; /* first message on queue */
struct msg *msg_last; /* last message in queue */
__kernel_time_t msg_stime; /* last msgsnd time */
__kernel_time_t msg_rtime; /* last msgrcv time */
__kernel_time_t msg_ctime; /* last change time */
struct wait_queue *wwait;
struct wait_queue *rwait;
unsigned short msg_cbytes; /* current number of bytes on queue */
unsigned short msg_qnum; /* number of messages in queue */
unsigned short msg_qbytes; /* max number of bytes on queue */
__kernel_ipc_pid_t msg_lspid; /* pid of last msgsnd */
__kernel_ipc_pid_t msg_lrpid; /* last receive pid */
};
其中包括:
l 一個ipc_perm的數據結構(msg_perm域),描述該
訊息佇列 的通用認證方式。
l 一對訊息
指針 (msg_first、msg_last),分別指向該訊息佇列的隊頭(第一個訊息)和隊尾(最後一個訊息)(msg)。傳送者將新訊息加到隊尾,接收者從隊頭讀取訊息。
l 三個時間域(msg_stime、msg_rtime、msg_ctime)用於記錄佇列最後一次傳送時間、接收時間和改動時間。
l 兩個進程
等待佇列 (wwait、rwait)分別表示等待向
訊息佇列 中寫的進程(wwait)和等待從訊息佇列中讀的進程(rwait)。如果某進程向一個訊息佇列傳送訊息而發現該佇列已滿,則進程掛在wwait佇列中等待。從該訊息佇列中讀取訊息的進程將從佇列中刪除訊息,從而騰出空間,再喚醒wwait佇列中等待的進程。如果某進程從一個訊息佇列中讀訊息而發現該佇列已空,則進程掛在rwait佇列中等待。向該訊息佇列中傳送訊息的進程將訊息加入佇列,再喚醒rwait佇列中等待的進程。
l 三個記數域(msg_cbytes、msg_qnum、msg_qbytes)分別表示佇列中的當前位元組數、佇列中的訊息數和佇列中最大位元組數;
l 兩個PID域(msg_lspid、msg_lrpid)分別表示最後一次向該
訊息佇列 中傳送訊息的進程和最後一次從該訊息佇列中接收訊息的進程。
見右圖(參見include/linux/msg.h)圖 System V IPC 機制——訊息佇列
System V IPC 機制——訊息佇列 當創建
訊息佇列 時,一個新的msqid_ds數據結構被從
系統記憶體 中分配出來,並被插入到msgque 向量表中。
每當進程試圖向訊息佇列寫訊息時,它的有效用戶和組
標識符 就要和訊息佇列的ipc_perm數據結構中的模式域比較。如果進程可以向這個訊息佇列寫(比較成功),則訊息會從進程的
地址空間 拷貝到一個msg數據結構中,該msg數據結構被放到訊息佇列的隊尾。每一個訊息都帶有進程間約定的、與應用程式相關的類型標記。但是,因為Linux限制了可以寫的訊息的數量和長度,所以可能會沒有空間來容納該訊息。這時,進程會被放到訊息佇列的寫
等待佇列 中,然後調度程式會選擇一個新的進程運行。當一個或多個訊息從這個訊息佇列中讀出去時,等待的進程會被喚醒。
從佇列中讀訊息與向佇列中寫訊息是一個相似的過程。進程對
訊息佇列 的訪問許可權一樣要被檢查。讀進程可以選擇從佇列中讀取第一條訊息而不管訊息的類型,也可以選擇從佇列中讀取特殊類型的訊息。如果沒有符合條件的訊息,讀進程會被加到訊息佇列的讀等待佇列,然後運行調度程式。當一個新的訊息寫到訊息佇列時,這個進程會被喚醒,繼續它的運行。
Linux提供了四個訊息佇列操作。
1. 創建或獲得訊息佇列(MSGGET)
在系統調用sys_ipc中call值為MSGGET,調用的函式為sys_msgget。該函式的定義如下:
int sys_msgget (key_t key, int msgflg)
其中key是一個鍵值,而msgflg是一個標誌。
該函式的作用是創建一個鍵值為key的
訊息佇列 ,或獲得一個鍵值為key的訊息佇列的引用
標識符 。這是使用訊息佇列的第一步,即獲得訊息佇列的引用標識符,以後就通過該標識符使用這個訊息佇列。
工作過程如下:
1) 如果key == IPC_PRIVATE,則申請一塊記憶體,創建一個新的訊息佇列(數據結構msqid_ds),將其初始化後加入到msgque向量表中的某個空位置處,返回標識符。
2) 在msgque向量表中找鍵值為key的
訊息佇列 ,如果沒有找到,結果有二:
l msgflg表示不創建新的佇列,則錯誤返回。
l msgflg表示要創建新的佇列,則創建新訊息佇列,創建過程如1)。
3) 如果在msgque向量表中找到了鍵值為key的訊息佇列,則有以下情況:
l 如果msgflg表示一定要創建新的訊息佇列而且不允許有相同鍵值的佇列存在,則錯誤返回。
l 如果找到的佇列是不能用的或已損壞的佇列,則錯誤返回。
l 認證和存取許可權檢查,如果該佇列不允許msgflg要求的存取,則錯誤返回。
2. 傳送訊息
在系統調用sys_ipc中call值為MSGSND,調用的函式為sys_msgsnd。該函式的定義如下:
int sys_msgsnd (int msqid, struct msgbuf *msgp, size_t msgsz, int msgflg)
msgp是訊息內容所在的緩衝區;
msgsz是訊息的大小;
msgflg是標誌。
該函式做如下工作:
1) 該訊息佇列在向量msgque中的索引是id = (unsigned int) msqid % MSGMNI,認證檢查(許可權、模式),合法性檢查(類型、大小等)。
2) 如果佇列已滿,以可中斷等待狀態(TASK_INTERRUPTIBLE)將當前進程掛起在wwait
等待佇列 上。
3) 申請一塊空間,大小為一個訊息數據結構加上訊息大小,在其上創建一個訊息數據結構struct msg,將訊息緩衝區中的訊息內容拷貝到該記憶體塊中訊息頭的後面(從
用戶空間 拷貝到
核心空間 )。
4) 將訊息數據結構加入到
訊息佇列 的隊尾,修改佇列的相應參數(大小等)。
5) 喚醒在該訊息佇列的rwait進程佇列上等待讀的進程。
6) 返回
3. 接收訊息
在系統調用sys_ipc中call值為MSGRCV,調用的函式為sys_msgrcv。該函式的定義如下:
int sys_msgrcv (int msqid, struct msgbuf *msgp, size_t msgsz,
long msgtyp, int msgflg)
msgp是接收到的訊息將要存放的緩衝區;
msgsz是訊息的大小;
msgtyp是期望接收的訊息類型;
msgflg是標誌。
該函式做如下工作:
1) 該
訊息佇列 在向量msgque中的索引是id = (unsigned int) msqid % MSGMNI,認證檢查(許可權、模式),合法性檢查。
2) 根據msgtyp和msgflg搜尋訊息佇列,情況有二:
l 如果找不到所要的訊息,則以可中斷等待狀態(TASK_INTERRUPTIBLE)將當前進程掛起在rwait等待佇列上。
l 如果找到所要的訊息,則將訊息從佇列中摘下,調整佇列參數,喚醒該
訊息佇列 的wwait進程佇列上等待寫的進程,將訊息內容拷貝到
用戶空間 的訊息緩衝區msgp中,釋放核心中該訊息所占用的空間,返回。
4. 訊息控制
在系統調用sys_ipc中call值為MSGCTL,調用的函式為sys_msgctl。該函式的定義如下:
int sys_msgctl (int msqid, int cmd, struct msqid_ds *buf)
cmd是執行命令;
buf是一個緩衝區。
該函式做如下工作:
該函式對
訊息佇列 做一些控制動作,如:釋放佇列,獲得佇列的認證信息,設定佇列的認證信息等。
訊息佇列和管道提供相似的服務,但訊息佇列要更加強大並解決了管道中所存在的一些問題。訊息佇列傳遞的訊息是不連續的、有格式的信息,給對它們的處理帶來了很大的靈活性。可以用不同的方式解釋訊息的類型域,如可以將訊息的類型同訊息的優先權聯繫起來,類型域也可以用來指定接收者。
小訊息的傳送效率很高,但大訊息的傳送性能則較差。因為
訊息傳送 的過程中要經過從
用戶空間 到
核心空間 ,再從核心空間到用戶空間的拷貝,所以,大訊息的傳送其性能較差。另外,訊息佇列不支持廣播,而且
核心 不知道訊息的接收者。
操作 管道分為有名管道和無名管道,無名管道只能用於親屬進程之間的通信,而有名管道則可用於無親屬關係的進程之間。
在Linux系統下,
命名管道 可由兩種方式創建(假設創建一個名為“fifoexample”的有名管道):
(1)mkfifo("fifoexample","rw");
(2)mknod fifoexample p
有名管道創建後,我們可以像讀寫檔案一樣讀寫它。
訊息佇列 用於運行於同一台機器上的進程間通信,與管道相似。
共享記憶體 通常由一個進程創建,其餘進程對這塊記憶體區進行讀寫。得到共享記憶體有兩種方式:映射/dev/mem設備和記憶體
映像檔案 。前一種方式不給系統帶來額外的開銷,但在現實中並不常用,因為它控制存取的是實際的
物理記憶體 ;常用的方式是通過shmXXX函式族來實現共享記憶體:
int shmget(key_t key, int size, int flag); /* 獲得一個共享存儲
標識符 */
該函式使得系統分配size大小的記憶體用作
共享記憶體 ;
void *shmat(int shmid, void *addr, int flag); /* 將共享記憶體連線到自身
地址空間 中*/
如果一個進程通過fork創建了子進程,則子進程繼承
父進程 的共享記憶體,既而可以直接對共享記憶體使用,不過子進程可以自身脫離共享記憶體。
shmid為shmget函式返回的共享存儲標識符,addr和flag參數決定了以什麼方式來確定連線的地址,函式的返回值即是該進程
數據段 所連線的
實際地址 。此後,進程可以對此地址進行讀寫操作訪問
共享記憶體 。
對於共享記憶體,linux本身無法對其做同步,需要程式自己來對共享的記憶體做出同步計算,而這種同步很多時候就是用
信號量 實現。
獲得共享資源 本質上,
信號量 是一個計數器,它用來記錄對某個資源(如
共享記憶體 )的存取狀況。信號量,分為互斥信號量,和條件信號量。一般說來,為了獲得共享資源,進程需要執行下列操作:
(1)測試控制該資源的信號量;
(2)若此信號量的值為正,則允許進行使用該資源,進程將信號量減去所需的資源數;
(3)若此信號量為0,則該資源目前不可用,進程進入睡眠狀態,直至信號量值大於0,進程被喚醒,轉入步驟(1);
(4)當進程不再使用一個信號量控制的資源時,信號量值加其所占的資源數,如果此時有進程正在睡眠等待此信號量,則喚醒此進程。
其他信息 套接字 通信並不為Linux所專有,在所有提供了TCP/IP協定棧的作業系統中幾乎都提供了socket,而所有這樣作業系統,對套接字的編程方法幾乎是完全一樣的。
效率比較 進程間通信各種方式效率比較
類型
無連線
可靠
流控制
記錄
訊息類型優先權
普通PIPE
N
Y
Y
N
流PIPE
N
Y
Y
N
命名PIPE(FIFO)
N
Y
Y
N
訊息佇列
N
Y
Y
Y
信號量
N
Y
Y
Y
共享存儲
N
Y
Y
Y
UNIX流SOCKET
N
Y
Y
N
UNIX數據包SOCKET
Y
Y
N
N