天天看點

Linux的程序通信(IPC)

程序間通信的目的

資料傳輸:一個程序需要将它的資料發送給另一個程序,發送的資料量在一個位元組到幾兆位元組之間。

共享資料:多個程序想要操作共享資料,一個程序對共享資料的修改,别的程序應該立刻看到。

通知事件:一個程序需要向另一個或一組程序發送消息,通知它(它們)發生了某種事件(如程序終止時要通知父程序)。

資源共享:多個程序之間共享同樣的資源。為了作到這一點,需要核心提供鎖和同步機制。

程序控制:有些程序希望完全控制另一個程序的執行(如Debug程序),此時控制程序希望能夠攔截另一個程序的所有陷入和異常,并能夠及時知道它的狀态改變。

程序通信方式

linux下程序間通信的幾種主要方式:

(1)管道(pipe)和有名管道(FIFO)

(2)信号(signal)

(3)消息隊列

(4)共享記憶體(shared memory)

(5)信号量(semaphore)

(6)套接字(socket)

管道

管道(pipe)及有名管道(named pipe):管道可用于具有親緣關系程序間的通信,有名管道克服了管道沒有名字的限制,是以,除具有管道所具有的功能外,它還允許無親緣關系程序間的通信。

管道是單向的、先進先出的、無結構的、固定大小的位元組流,它把一個程序的标準輸出和另一個程序的标準輸入連接配接在一起。寫程序在管道的尾端寫入資料,讀程序在管道的首端讀出資料。資料讀出後将從管道中移走,其它讀程序都不能再讀到這些資料。管道提供了簡單的流控制機制。程序試圖讀空管道時,在有資料寫入管道前,程序将一直阻塞。同樣,管道已經滿時,程序再試圖寫管道,在其它程序從管道中移走資料之前,寫程序将一直阻塞。通常有種限制,一是半雙工,隻能單向傳輸;二是隻能在父子程序間使用。

有名管道(也叫FIFO,因為管道工作在先入先出的原則下,第一個寫入管道的資料也是第一個被讀出的資料)。與管道不同,FIFO不是臨時的對象,它們是檔案系統中真正的實體,可以用mkfifo指令建立。隻要有合适的通路權限,程序就可以使用FIFO。FIFO的打開方式和管道稍微不同。一個管道(它的兩個file資料結構、VFS I節點和共享資料頁)是一次性建立的,而FIFO已經存在,可以由它的使用者打開和關閉。Linux必須處理在寫程序打開FIFO之前讀程序對它的打開,也必須處理在寫程序寫資料之前讀程序對管道的讀。除此以外,FIFO幾乎和管道的處理完全一樣,而且它們使用一樣的資料結構和操作。

信号

信号(signal):信号是比較複雜的通信方式,用于通知接受程序有某種事件發生,除了用于程序間通信外,程序還可以發送信号給程序本身;linux除了支援Unix早期信号語義函數sigal外,還支援語義符合Posix.1标準的信号函數sigaction(實際上,該函數是基于BSD的,BSD為了實作可靠信号機制,又能夠統一對外接口,用sigaction函數重新實作了signal函數)。

信号是在軟體層次上對中斷機制的一種模拟,是一種異步通信方式。

信号可以直接進行使用者空間程序和核心程序之間的互動,核心程序也可以利用它來通知使用者空間程序發生了哪些系統事件。它可以在任何時候發給某一程序,而無需知道該程序的狀态。

如果該程序目前并未處于執行态,則該信号就由核心儲存起來,直到該程序恢複執行再傳遞給它;如果一個信号被程序設定為阻塞,則該信号的傳遞被延遲,直到其阻塞被取消時才被傳遞給程序 。

程序執行信号的方式:

忽略信号,即對信号不做任何處理,其中,有兩個信号不能忽略:SIGKILL及SIGSTOP。

捕捉信号,定義信号處理函數,當信号發生時,執行相應的處理函數。

執行預設操作,Linux對每種信号都規定了預設操作。

消息隊列

消息隊列:消息隊列是消息的連結表,包括Posix消息隊列System V消息隊列。有足夠權限的程序可以向隊列中添加消息,被賦予讀權限的程序則可以讀走隊列中的消息。消息隊列克服了信号承載資訊量少,管道隻能承載無格式位元組流以及緩沖區大小受限等缺點。

消息隊列的實作包括建立或打開消息隊列、添加消息、讀取消息和控制消息隊列這四種操作:

建立或打開消息隊列使用的函數是msgget,這裡建立的消息隊列的數量會受到系統消息隊列數量的限制。

添加消息使用的函數是msgsnd函數,它把消息添加到已打開的消息隊列末尾。

讀取消息使用的函數是msgrcv,它把消息從消息隊列中取走,與FIFO不同的是,這裡可以指定取走某一條消息。

控制消息隊列使用的函數是msgctl,它可以完成多項功能。

信号量/信号燈

信号量(semaphore):主要作為程序間以及同一程序不同線程之間的同步手段。信号量是用來解決程序之間的同步與互斥問題的一種程序之間通信機制,包括一個稱為信号量的變量和在該信号量下等待資源的程序等待隊列,以及對信号量進行的兩個原子操作(PV操作)。其中信号量對應于某一種資源,取一個非負的整型值。信号量值指的是目前可用的該資源的數量,若它等于0則意味着目前沒有可用的資源。

P操作:如果有可用的資源(信号量值>0),則占用一個資源(給信号量值減去一,進入臨界區代碼)。如果沒有可用的資源(信号量值等于0),則被阻塞到,直到系統将資源配置設定給該程序(進入等待隊列,一直等到資源輪到該程序)。

V操作:如果在該信号量的等待隊列中有程序在等待資源,則喚醒一個阻塞程序。如果沒有程序等待它,則釋放一個資源(給信号量值加一)。

共享記憶體

共享記憶體(shared memory)可以說是最有用的程序間通信方式,也是最快的IPC形式。兩個不同程序A、B共享記憶體的意思是,同一塊實體記憶體被映射到程序A、B各自的程序位址空間。程序A可以即時看到程序B對共享記憶體中資料的更新,反之亦然。由于多個程序共享同一塊記憶體區域,必然需要某種同步機制,互斥鎖和信号量都可以。

采用共享記憶體通信的一個顯而易見的好處是效率高,因為程序可以直接讀寫記憶體,而不需要任何資料的拷貝。對于像管道和消息隊列等通信方式,則需要在核心和使用者空間進行四次的資料拷貝,而共享記憶體則隻拷貝兩次資料:一次從輸入檔案到共享記憶體區,另一次從共享記憶體區到輸出檔案。實際上,程序之間在共享記憶體時,并不總是讀寫少量資料後就解除映射,有新的通信時,再重建立立共享記憶體區域。而是保持共享區域,直到通信完畢為止,這樣,資料内容一直儲存在共享記憶體中,并沒有寫回檔案。共享記憶體中的内容往往是在解除映射時才寫回檔案的。是以,采用共享記憶體的通信方式效率是非常高的。

共享記憶體實作的步驟:

1.建立共享記憶體,這裡用到的函數是shmget,也就是從記憶體中獲得一段共享記憶體區域。

2.映射共享記憶體,也就是把這段建立的共享記憶體映射到具體的程序空間中去,這裡使用的函數是shmat。

3.使用不帶緩沖的I/O讀寫指令對其進行操作。

4.撤銷映射的操作,其函數為shmdt。

套接口

套接口(socket):更為一般的程序間通信機制,可用于不同機器之間的程序間通信。起初是由Unix系統的BSD分支開發出來的,但現在一般可以移植到其它類Unix系統上:Linux和System V的變種都支援套接字。

詳解參考《UNIX網絡程式設計 第2卷 程序間通信》

    本文轉自阿凡盧部落格園部落格,原文連結http://www.cnblogs.com/luxiaoxun/archive/2012/10/06/2712798.html:,如需轉載請自行聯系原作者

繼續閱讀