15.1 引言
*進程之間交換資訊的方法可以經由fork或exec傳送開啟檔案,或者通過檔案系統
*進程之間相互連信的其他技術——IPC(InterProcess Communication)包括半雙工管道、FIFO、全雙工系統管道、命名全雙工系統管道、訊息佇列、訊號量、共用儲存、通訊端、STREAMS
15.2 管道
*管道有兩種局限性:(1)曆史上,它們是半雙工的(即資料只能在一個方向上流動)(2)它們只能在具有公用祖先的進程之間使用
*每當你在管道線中鍵入一個由shell執行的命名序列時,shell為每一條命令單獨建立一進程,然後將前一條命令進程的標準輸出用管道與後一條命令的標準輸入相串連
*管道是由調用pipe函數而建立的
15.3 popen和pclose函數
*常見的操作是建立一個管道串連到另一個進程,然後讀其輸出或向其輸入端發送資料。為此,標準I/O庫提供了兩個函數popen和pclose。這兩個函數實現的操作是:建立一個管道,調用fork產生一個進程,關閉管道的不使用端,執行一個shell以運行命令,然後等待命令終止
15.4 協同進程
*UNIX系統過濾程式從標準輸入讀取資料,對其進行適當處理後寫到標準輸出。幾個過濾程式通常在shell管道命令列中線性串連。當一個程式產生某個過濾程式的輸入,同時又讀取該過濾程式的輸出時,則該過濾程式就稱為協同進程(coprocess)
*標準I/O的緩衝機制問題的解決方案是使被調用的協同進程(在本例中是awk)認為它的標準輸入和輸出都被串連到一個終端
15.5 FIFO
*FIFO有時被稱為具名管道。管道只能由相關進程使用,這些相關進程的共同的祖先進程建立了管道。(一個例外是已裝配的基於STREAMS的管道)。但是,通過FIFO,不相關的進程也能交換資料。
*FIFO有下面兩種用途:
(1)FIFO由shell命令使用以便將資料從一條管道線傳送到另一條,為此無需建立中間臨時檔案
(2)FIFO用於客戶進程-伺服器處理序應用程式中,以在客戶進程和伺服器處理序之間傳遞資料
15.6 XSI IPC
*有三種IPC我們稱作XSI IPC,即訊息佇列、訊號量以及共用儲存空間,它們之間有很多相似之處
*每個核心中的IPC結構(訊息佇列、訊號量或共用儲存段)都用一個非負整數的標識符(identifier)加以引用
*標識符是IPC對象的內部名。為使多個合作進程能夠在同一IPC對象上會合,需要提供一個外部名方案。為此使用鍵(key),每個IPC對象都與一個鍵相關聯,於是鍵就用作為該對象的外部名
*有多種方法可以使客戶進程和伺服器處理序在同一IPC結構上會合:
(1)伺服器處理序可以指定鍵IPC_PRIVATE建立一個新IPC結構,將返回的標識符存放在某處(例如一個檔案)以便客戶進程取用。鍵IPC_PRIVATE保證伺服器處理序建立一個新IPC結構。IPC_PRIVATE鍵也可用於父子進程關係。父進程指定IPC_PRIVATE建立一個新IPC結構,所返回的標識符在調用fork之後可由子進程使用。接著,子進程又可將此標識符作為exec函數的一個參數傳給一個新程式
(2)在一個公用標頭檔中定義一個客戶進程和伺服器處理序都認可的鍵。然後伺服器處理序指定此鍵建立一個新的IPC結構
(3)客戶進程和伺服器處理序認同一個路徑名和項目ID(項目ID是0~255之間的字元值),接著調用函數ftok將這兩個值變換為一個鍵。然後在方法(2)中使用此鍵。ftok提供的唯一服務就是由一個路徑名和項目ID產生一個鍵
15.7 訊息佇列
*msgget建立一個新隊列或開啟一個現存的隊列。msgsnd將新訊息添加到隊列尾端。每個訊息包含一個正長整數類型欄位,一個非負長度以及實際資料位元組(對應於長度),所有這些都在講訊息添加到隊列時,傳送給msgsnd。msgrcv用於從隊列中取訊息。
*每個訊息由三部分組成,它們是:正長整數型別欄位、非負長度(nbytes)以及實際資料位元組(對應於長度)。訊息總是放在隊列尾端
*若訊息佇列已滿(或者是隊列中的訊息總數等於系統限制值,或隊列中的位元組總數等於系統限制值),則指定IPC_NOWAIT使得msgsnd立即出錯返回EAGAIN。如果沒有指定IPC_NOWAIT,則進程阻塞直到下述情況出現為止:有空間可以容納要發送的訊息;從系統中刪除了此隊列,或捕捉到一個訊號,並從訊號處理常式返回。
15.8 訊號量
*訊號量(semaphore)與已經介紹過的IPC機構(管道、FIFO以及訊息佇列)不同。它是一個計數器,用於多進程對共用資料對象的訪問
15.9 共用儲存
*共用儲存允許兩個或更多進程共用一給定的儲存區。因為資料不需要在客戶進程和伺服器處理序之間複製,所以這是最快的一種IPC。使用共用儲存時要掌握的唯一竅門是多個進程之間對一給定儲存區的同步訪問,若伺服器處理序正在將資料放入共用儲存區,則它做完這一操作之前,客戶進程不應當去取這些資料。通常,訊號量被用來實現對共用儲存訪問的同步。(記錄鎖也可用於這種場合)
*為了獲得一個共用儲存標識符,調用的第一個函數通常是shmget
int shmget(key_t key, size_t size, int flag)
*shmctl函數對共用儲存段執行多種操作
int shmctl(int shmid, int cmd, struct shmid_ds *buf)
*cmd參數指定下列5種命令中一種,使其在shmid指定的段上執行
IPC_STAT
IPC_SET
IPC_RMID 從系統中刪除該共用儲存段
SHM_LOCK 將共用儲存段鎖定在記憶體中
SHM_UNLOCK 解鎖共用儲存段
*一旦建立了一個共用儲存段,進程就可調用shmat將其串連到它的地址空間中
void *shmat(int shmid, const void *addr, int flag)
共用儲存段串連到調用進程的哪個地址上與addr參數以及在flag中是否制定SHM_RND位有關
(1)如果addr為0,則此段串連到由核心選擇的第一個可用地址上
(2)如果addr非0,並且沒有指定SHM_RND,則此段串連到addr所指定的地址上
(3)如果addr非0,並且指定了SHM_RND,則此段串連到(addr-(addr mod ulus SHMLBA))所表示的地址上。SHM_RND命令的意思是“取整”。SHMLBA的意思是“低邊界地址倍數”,它總是2的乘方
*shmat的傳回值是該段所串連的實際地址,如果出錯則返回-1
*當對共用儲存段的操作已經結束時,則調用shmdt脫接該段
int shmdt(void *addr)
*很多實現提供了一種類似於/dev/zero的設施,稱為匿名儲存映射
15.10 客戶進程-伺服器處理序屬性
*客戶進程和伺服器處理序的某些屬性受到它們之間所使用的IPC類型的影響。最簡單的關聯類型是使客戶調用fork然後調用exec執行所希望的伺服器處理序。在fork之前先建立兩個半雙工管道使資料可在兩個方向傳輸