程序間通訊(IPC)

2021-09-02 17:34:58 字數 4501 閱讀 7071

管道是一種ipc方式,它具有兩點侷限性,第一點是由於管道在最初是一種半雙工的程序間通訊方式,雖然有的系統允許通過管道進行全雙工通訊,但是為了可移植性不應做這種假設。第二點是管道只能在具有公共祖先的兩個程序之間通訊(那使用者建立的程序不都有公共祖先init程序?所以該公共祖先還應該建立了管道,這樣子程序才能繼承引用該管道的檔案描述符)。

管道使用函式int pipe(int fd[2])進行建立,該函式會設定兩個描述符fd[0]和fd[1]分別作為管道的讀端和寫端。一般使用管道的方式是,父程序先呼叫pipe函式建立乙個管道,之後呼叫fork建立子程序,此時子程序會繼承父程序的檔案描述符,這樣父子程序便可以引用同一管道(注意fork並不會複製管道,複製的是檔案描述符),之後便可以使用該管道進行通訊。為了可移植性,預設遵循管道為半雙工,父子程序應該乙個關閉讀端(close(fd[0])),乙個關閉寫端(close(fd[1])),這依據通訊的方向而定。**示例如下:

#include int main()

;

ipc的許可權型別也類似於檔案的許可權,但是ipc不存在執行許可權,只有讀,寫許可權,對於訊號量叫讀,更改(其實就是寫)許可權。ipc許可權與相應值如下表所示:許可權位

使用者讀使用者寫(更改)

0400                            

組讀組寫(更改)

其他讀其他寫(更改)

1)缺點

【注】:對於未命名管道當最後乙個引用的程序終止時,未命名管道就被完全的刪除了。而對於fifo,雖然最後乙個引用的程序結束後,fifo仍會留在系統中,但fifo中的資料會被刪除。

2)優點

不同ipc的比較如下圖所示:

【注】:流控制指的是沒如果系統資源(緩衝區)不足,或者接收程序不能再接收更多的訊息,則傳送程序陷入睡眠,當條件滿足時再自動喚醒傳送程序。

【注】:由於訊息佇列在通訊之前需要通過某種技術先獲知佇列識別符號,因此並不認為訊息佇列是無連線的

訊息佇列雖稱為佇列,但是它是訊息的連線表,而且並不用遵循佇列先進先出的順序,而是可以根據訊息型別取出相應訊息(訊息型別是在放入資料時由使用者指定的,會在之後介紹訊息佇列收發資料時進行說明),訊息佇列時全雙工的。每乙個訊息佇列都有乙個msqid_ds結構與其關聯(該結構的資訊其實取自訊息佇列在核心中的結構體struct msg_queue{},在說明《深入理解linux核心》一書時會進行說明),該結構記錄了訊息佇列的許可權,佇列中的訊息數,佇列中的位元組數等等,如下所示:

struct msqid_ds ;
我們可以使用msgget函式通過鍵值建立(鍵值需未關聯或為ipc_private)或引用(鍵值必須與某乙個現有訊息佇列的鍵值相等)乙個訊息佇列,若成功該函式會返回乙個非負識別符號,供呼叫其它函式使用,否則返回-1。

若是新建立的訊息佇列,那麼其msqid_ds中的相應值會被初始化(即核心會初始化乙個struct msg_queue{}),許可權值msg_perm中的mode會被設定為flag中指定的許可權值,這些許可權使用4.3中**內的值,msg_qbytes被設定為系統限制值,msg_ctime被設定為當前時間,其它值為0。

該函式可以根據命令cmd的不同對訊息佇列進行不同的操作,比如獲取訊息佇列的狀態(即msqid_ds結構),設定訊息佇列的許可權,刪除訊息佇列。

1)向訊息佇列寫資料 int msgsnd(int msqid, const void* ptr, size_t bytes, int flags)

該函式可用於向識別符號msqid所指定的訊息佇列寫如資料,而ptr指向的就是一塊位址空間,該空間由兩部分組成:乙個long型的變數用於標識該訊息的型別(我們在前面說過訊息佇列可以根據訊息型別取資料,而無需先進先出,所指的訊息型別便是此處的值),而該變數之後緊跟著的便是用於儲存資料的緩衝區,比如以下結構:struct mymsg。bytes指定了快取中資料的位元組數。而引數flags可以指定為ipc_nowait,即類似於檔案i/o的非阻塞i/o標誌。當該函式成功返回時,msqid_ds結構中的相應欄位也會進行更新。

2)從訊息佇列中讀資料 int msgrcv(int msqid,  void *ptr,  size_t nbytes, long type, int flag)

同樣的,ptr仍指向一塊上述的緩衝,nbytes為緩衝區資料緩衝的長度,若返回的資料大於nbytes則根據是否在flag中設定msg_noerror而做不同的處理,若設定了則截斷資料,被截部分會丟棄,且不通知程序。若未設定則返回-1,errno設定未e2big,但訊息仍留在訊息佇列中。

同樣的,flag也可設定為非阻塞ipc_nowait。

而根據指定的type值的不同,又3種從訊息佇列種取資料的策略:

type == 0: 返回佇列中的第一條訊息。

type > 0  : 返回佇列中訊息型別為type的第乙個訊息

type < 0  : 返回佇列中訊息型別小於等於type絕對值的值最小的一條訊息

利用上述特點可以將訊息型別根據訊息優先順序的不同設定為相應的優先權值,也可以在多客戶單伺服器中,使用type記錄客戶的程序id。

【注】:apue中在對比了訊息佇列與其它ipc後,發現訊息佇列原先速度較高的優點已不復存在,綜合4.4中所述的訊息佇列的缺點得出的結論是:在新的應用程式中不...應...該...在...使...用...它...們....~-~...。

共享記憶體允許兩個或多個程序共享乙個儲存區,因為資料不需要在程序間傳遞資料,因此這是最快的一種ipc。但是使用共享記憶體時應該注意多個程序之間的同步。我們可以使用訊號量,互斥鎖和檔案鎖來是實現程序間的同步。

【猜測】:作業系統應該是在位址空間中取出了一段空間作為共享記憶體,在將該段空間的首位址與程序中的位址進行了儲存對映一樣,只是共享記憶體與檔案無關,關於共享記憶體具體如何實現的,再看過《深入理解linux核心》後再進行說明。

核心為每個共享記憶體段維護了乙個資料結構, 至少包含以下字段:

struct shmid_ds ;
其使用方式與訊息通道基本相同,此處不再贅述,只是需要說明的是當建立乙個共享記憶體時必須指定共享記憶體的大小size,若是獲取乙個已建立的共享記憶體,那麼size應該為0。乙個建立的新段其內容為0。

當cmd為ipc_stat或ipc_set時與訊息通道相同此處不再贅述,此處只說明幾個不同的命令:

建立從程序位址空間內某位址addr到共享記憶體的對映,一般將addr指定為0,以便由系統選擇位址。若flag指定了shm_rdonly則以唯讀方式連線此段,否則以讀寫方式連線。

該函式與指定ipc_rmid的shmctl不同,其並不將共享記憶體標記為刪除,仍可使用shmat連線到該共享記憶體。

1)方式1

先執行以下**,其會建立乙個共享記憶體,方便起見直接輸出其識別符號,以便放在第二個程式中

接著執行第二個程式:

【注】:可以看到兩個程式返回的位址是不同的,因此shmat函式比非直接將共享記憶體的位址返回,而是進行了某種對映。其實是對映到了程序位址空間的某一塊,即堆與棧之間

2)方式2(使用/dev/zero檔案)

共享記憶體與多個程序同時儲存對映到乙個檔案十分相似,只是儲存對映是與檔案相關聯的,而共享記憶體則並無這種對映。我們可以mmap間程序的位址空間對映到/dev/zero檔案,其效果等同於使用一段匿名的記憶體。 /dev/zero 是乙個特殊的檔案,當你讀它的時候,它會提供無限的空字元(null, 0x00)。

使用方法:暫未試,可參apue p463

程序間通訊IPC

這兩天學習了 unix 的程序間通訊 ipc,這裡面有幾個很重要的基本概念,特別是訊息佇列和我的畢設很有關係,因此多說幾句。以前學習的程序間通訊方式,一般都是經由 fork 或exec 開啟檔案,或經過檔案系統。而 ipc是程序間通訊方式的統稱。下面一一道來。一 管道 管道是最老的 ipc形式。管道...

程序間通訊 IPC

part2 index1.html part2 index2.html linux至少支援如下ipc機制 同時支援posix和system v方式 streams ipc機制,linux本身不支援,有乙個單獨的安裝包 可以跨pc的程序通訊 互斥鎖或條件變數 動態初始化 不能使靜態分配 在共享記憶體中...

程序間通訊IPC

乙個大型的應用系統,往往需要眾多程序協作,程序間通訊的重要性顯而易見。首先,程序間通訊至少可以通過傳送開啟檔案來實現,不同的程序通過乙個或多個檔案來傳遞資訊,事實上,在很多應用系統裡,都使用了這種方法。但一般說來,程序間通訊 ipc interprocess communication 不包括這種似...