Linux程序間通訊總結
目錄
Linux下的程序間通訊(Interprocess Communication,IPC)方式基本上是從Unix繼承而來的。對Unix發展做出重大貢獻的兩個實驗室:AT&T的貝爾實驗室、BSD在IPC方面的側重點有所不同。前者對Unix早期的IPC手段進行了系統的改進和擴充,形成了“System V IPC”,通訊程序侷限在單個計算機內;後者則跳過了該限制,形成了基於套接字(Socket)的程序間通訊機制。Linux則把兩者都繼承了下來,加上作業系統早期的訊號、管道、命名管道,構成了Linux豐富的IPC方式。
下面先介紹幾個作業系統最早使用的IPC機制:
訊號
訊號又稱軟體中斷,本質是對硬體中斷的模擬,是作業系統最原始的非同步通訊機制,Linux提供了一系列函式讓使用者註冊訊號,自定義中斷處理例程,傳送訊號等,程序間通過訊號進行同步。但訊號存在系統開銷大、傳送資訊量有限等問題,所以實際應用中,訊號機制常常用於程序間的事件通知,而不應用於複雜的互動操作。
關於中斷概念及中斷處理請點選:https://blog.csdn.net/qq_37653144/article/details/82929246
關於Linux訊號機制請點選:https://blog.csdn.net/qq_37653144/article/details/81942026
管道
管道也稱匿名管道,相當於一塊環形緩衝區,它只存在於記憶體中,程序通過對管道的讀寫來實現資料互動。管道有單工的也有半雙工的,單工管道只能單向傳輸,半雙工管道可以實現雙向傳輸,但同一時刻只允許一個方向的傳輸。管道只存在於記憶體中的作業系統核心部分,因此只能在有親屬關係(父子程序、兄弟程序)的程序間使用。
Linux管道程式設計請點選:https://blog.csdn.net/qq_37653144/article/details/83927634
命名管道
命名管道相較於管道去除了只能在有親屬關係的程序間使用的限制,因為命名管道是一個實實在在的檔案,存在於檔案系統中,因此任何有許可權訪問該命名管道的程序都能使用命名管道進行資料互動。
Linux命名管道程式設計請點選:https://blog.csdn.net/qq_37653144/article/details/83932845
System V IPC
需要注意的是,POSIX大體繼承了System V IPC,可以說POSIX只是做了統一標準化的工作,而除了在函式命名格式上有所不同外,可以將POSIX的IPC視為System V IPC看待。Linux同時支援這兩種標準。
System V IPC由以下三種IPC物件組成,三者都有著類似的結構(下文詳述):
訊息佇列:https://blog.csdn.net/qq_37653144/article/details/83960769
訊號量:https://blog.csdn.net/qq_37653144/article/details/83963143
共享記憶體:https://blog.csdn.net/qq_37653144/article/details/83993840
組成
System V IPC由訊息佇列、訊號量和共享記憶體三種IPC方式組成,這三種IPC通訊方式在程式設計介面和內部實現上都非常類似,例如都採用類似的控制函式、ipc_perm結構等。
Linux核心提供了相應的標頭檔案用於實現System V IPC,三種不同的通訊方式分別對應不同的標頭檔案和動作操作函式,如下表所示:
實現方式 | 標頭檔案 | 建立或開啟IPC物件 | IPC物件控制函式 | IPC物件操作函式 |
---|---|---|---|---|
訊息佇列 | <sys/msg.h> | msgget | msgctl | msgsnd msgcv |
訊號量 | <sys/sem.h> | semget | semctl | semop |
共享記憶體 | <sys/shm.h> | shmget | shmctl | shmat shmdt |
識別符號
每一個System V IPC的結構(訊息佇列、訊號量和共享記憶體)都對應了一個識別符號(ID),它是一個非負整數,當一個IPC結構被建立時,和該結構相關的識別符號會自動加1並賦予新建立的IPC結構,這個非負整數溢位時會回滾到0重新開始。
識別符號的唯一性侷限於結構型別內。假設某個訊息佇列的識別符號為1,則這個識別符號在所有訊息佇列中是唯一的,但有可能存在識別符號為1的訊號量或共享記憶體。
三種IPC結構的建立函式都有一個key_t型別的引數key,使用者程序將這個引數值傳遞給核心,由核心轉換為識別符號。
ftok函式
Linux提供了ftok函式用於將一個路徑和專案ID轉換為關鍵字,其標準呼叫格式說明如下:
#include <sys/types.h>
#incldue <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
其中第一個引數pathname必須是一個存在的、可以訪問的檔案路徑名,而第二個引數proj_id則只有低8位有效(如果低8位為0,返回的是一個隨機結果)。函式呼叫成功則返回key_t型別的關鍵字供程序建立IPC物件使用,否則返回-1。
需要注意的是,ftok函式返回的關鍵字是根據檔案的物理索引(inode) 確定的,因此路徑名相同並不意味著frok函式產生的關鍵字也會相同。
雖然使用IPC機制的程序可以直接利用諸如123這樣的整數作為關鍵字,但它們要在程式編碼上保持一致,並且這麼做還有一個缺點:其他程序也可能使用這個整數作為另外的IPC資源的關鍵字,這種情況下可能導致混亂。因此最好利用ftok函式來生成關鍵字。
結構定義
每一個IPC物件都有一個ipc_perm結構與之對應,這個結構中記錄了物件的一些資訊,如所有者、建立者和許可權等:
struct ipc_perm
{
uid_t uid; //所有者的有效使用者ID
gid_t gid; //所有者的有效組ID
uid_t cuid; //建立者的有效使用者ID
gid_t cgid; //建立者的有效組ID
mode_t mode; //訪問許可權
unsigned long seq; //應用序號,每次IPC物件被使用都會加1,溢位後回滾
key_t key; //關鍵字
};
ipc_perm結構中的mode記錄了IPC物件的訪問許可權,它與檔案的訪問許可權類似,同樣分為使用者、組使用者和其他使用者三類。但是這些許可權中沒有可執行許可權。
許可權 | 訊息佇列 | 訊號量 | 共享記憶體 | |
---|---|---|---|---|
使用者 | 可讀 | MSG_R | SEM_R | SHM_R |
可寫 | MSGW | |||
組使用者 | 可讀 | MSG_R >> 3 | SEM_R >> 3 | SHM_R >> 3 |
可寫 | MSG_W >> 3 | SEM_A >> 3 | SHM_W >> 3 | |
其他使用者 | 可讀 | MSG_R >> 6 | SEM_R >> 6 | SHM_R >> 6 |
可寫 | MSG_W >> 6 | SEM_A >> 6 | SHM_W >> 6 |
mode位還經常和IPC_CREATE、IPC_EXCL兩種標誌位進行位或運算,以完成對IPC物件的建立管理。
#include <sys/ipc.h>
#define IPC_CREATE 01000 /* Create key if key does not exist. */
#define IPC_EXCL 02000 /* Fail if key exists. */
Linux核心提供了相應的函式來建立一個新的或者訪問一個已經存在的IPC物件,對其建立或者訪問的規則如下:
·指定key為IPC_PRIVATE,作業系統保證建立一個唯一的IPC物件。
·設定flag引數的IPC_CREATE位,但不設定它的IPC_EXCL位時,如果所指定key的IPC物件不存在,就建立一個新的物件,否則返回該物件。
·同時設定flag引數的IPC_CREATE和IPC_EXCL時,如果所指定key的IPC物件不存在,就建立一個新的物件,否則返回一個EEXIST錯誤。
特點
System V IPC機制具有以下幾個特點:
·IPC結構存在於Linux核心空間,它沒有計數機制,也不會自我刪除,停止使用的IPC結構會一直保留在系統核心空間中,除非主動刪除。
·IPC結構不能再檔案系統中公開訪問,不能使用檔案操作的函式來對這些結構進行操作。
·IPC結構沒有檔案描述符,所以不能對其使用I/O多路複用seclect和epoll,也不能在檔案或者裝置I/O中使用IPC結構,還不能一次性使用多個IPC結構。
訊息佇列
訊息佇列(Message Queue)由連結串列實現,訊息不一定要按照先進先出的形式來讀寫,因此可以實現訊息的隨機查詢。最重要的是,訊息佇列可以按格式讀寫資料,而管道和有名管道只能處理無格式位元組流。
訊息佇列程式設計:https://blog.csdn.net/tennysonsky/article/details/46331643
訊號量
訊號量的本質是一個計數器,可以用來控制多個程序對共享資源的訪問。它常作為一種鎖機制,提供對共享資源的互斥訪問。而線上程中訊號量的應用十分廣泛,因為同一程序內的多個執行緒共享程序的資源,因此執行緒之間對共享資源的訪問是訊號量應用的理想場景。
訊號量程式設計:https://blog.csdn.net/qq_37653144/article/details/83963143
共享記憶體
共享記憶體就是將一塊記憶體對映到其他程序的虛擬地址空間中,這段共享記憶體由一個程序建立,但多個程序都可以訪問。共享記憶體是最快的IPC方式,它是針對其他程序間通訊方式執行效率低而專門設計的。它往往與其他通訊機制,如訊號量,配合使用,來實現程序間的同步和通訊。
共享記憶體程式設計:https://blog.csdn.net/qq_37653144/article/details/83993840
套接字
套接字(Socket)最早是由BSD為實現多計算機之間的通訊而開發的,在計算機網路通訊中套接字起著非常重要的作用。
套接字程式設計(TCP):https://blog.csdn.net/qq_37653144/article/details/81605294