程序間通訊(一)

2022-08-05 17:57:14 字數 3457 閱讀 4827

1.程序是一種單執行流,每個程序都私有/獨佔一份系統資源,**段共享但是資料段不共享,子程序寫資料時會觸發寫實拷貝,從而保證資源獨享。總的來說 每個程序各自有不同的使用者地址空間,任何一個程序的全域性變數在另一個程序中都看不到 。

所以程序之間要交換資料必須通過核心,在核心中開闢一塊緩衝區,程序1把資料從使用者空間 拷到核心緩衝區,程序2再從核心緩衝區把資料讀走,核心提供的這種機制稱為程序間通訊 如圖所示:

程序間通訊(ipc,interprocess communication)的方式:

1.管道:匿名管道、命名管道

2.xsi ipc:訊息佇列、訊號量、共享記憶體

xsi ipc,依託識別符號和鍵來實現的,如同管道靠檔案描述符來實現一樣。

xsi ipc使用一般步驟:

1)ipc物件程序內部用識別符號identifier,程序外部標識用key

2)首先用semget,shmget,msgget等函式根據key建立或獲取ipc物件的identifier

3)然後根據identifier用semctrl,shmctrl等控制函式做某些修改,這步可選

4)最後用各個ipc特有操作函式如semop,shmat等函式操作)

匿名管道(pipe)

調⽤用pipe函式時在核心中開闢一塊緩衝區(稱為管道)用於通訊,它有一個讀端一個寫端,然後 通過filedes引數傳出給使用者程式兩個檔案描述符,filedes[0]指向管道的讀端,filedes[1]指向 管道 的寫端(很好記,就像0是標準輸入1是標準輸出一樣)。所以管道在使用者程式看起來就像一個開啟的檔案,通過read(filedes[0]);或者write(filedes[1]);向這個檔案讀寫資料其實是在讀寫核心緩衝區。pipe函式呼叫成功返回0,呼叫失敗返回-1。

1. 父程序呼叫pipe開闢管道,得到兩個檔案描述符指向管道的兩端。 

2. 父程序呼叫fork建立子程序,那麼子程序也有兩個檔案描述符指向同一管道。 

3. 父程序關閉管道讀端,子程序關閉管道寫端。父程序可以往管道里寫,子程序可以從管道里讀,管道是用環形佇列實現的,資料從寫端流入從讀端流出,這樣就實現了程序間通訊

int test1()         //test creat ipc fun "pipe()"

pid_t id = fork();

if (id < 0)

else if (id == 0)         //child only write,father only read

}else

}return 0;

}使用管道有一些限制:

兩個程序通過一個管道只能實現單向通訊。比如上面的例子,父程序寫子程序讀,如果有時候也需要子程序寫父程序讀,就必須另開一個管道。(原因:管道是一種半雙工方式,即對於程序來說,要麼只能讀管道,要麼只能寫管道。不允許對管道又讀又寫)《半雙工資料傳輸允許資料在兩個方向上傳輸,但是,在某一時刻,只允許資料在一個方向上傳輸》

管道的讀寫端通過開啟的檔案描述符來傳遞,因此要通訊的兩個程序必須從它們的公共祖先 那⾥裡繼承管道檔案描述符。

父程序fork兩次,把檔案描述符傳給兩個子程序,然後兩個子程序之間通訊, 總之需要通過fork傳遞檔案描述符使兩個程序都能訪問同一管道,它們才能通訊。也就是說,管道通訊是需要程序之間有血緣關係。

⽤用管道需要注意以下4種特殊情況(假設都是阻塞i/o操作,沒有設定o_nonblock標誌):  

1. 如果所有指向管道寫端的檔案描述符都關閉了(管道寫端的引⽤用計數等於0),而仍然有程序 從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像 讀到檔案 末尾一樣。

2. 如果有指向管道寫端的檔案描述符沒關閉(管道寫端的引用計數⼤大於0),而持有管道寫端的程序也沒有向管道中寫資料,這時有程序從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回 

3. 如果所有指向管道讀端的檔案描述符都關閉了(管道讀端的引用計數等於0),這時有程序向管道的寫端write,那麼該程序會收到訊號sigpipe,通常會導致程序異常終止。

4. 如果有指向管道讀端的檔案描述符沒關閉(管道讀端的引⽤用計數⼤大於0),而持有管道讀端的程序也沒有從管道中讀資料,這時有程序向管道寫端寫資料,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入資料並返回。

命名管道(fifo) 檔案系統中的路徑名是全域性的,各程序都可以訪問,因此可以⽤用檔案系統中的路徑名來標識一 個ipc通道。  命名管道也被稱為fifo檔案,它是一種特殊型別的檔案,它在檔案系統中以檔名的形式存在,但是它的行為卻和之前所講的沒有名字的管道(匿名管道)類似。

由於linux中所有的事物都可被視為檔案,所以對命名管道的使用也就變得與檔案操作非常的統一,也使它的使用非常方便,同時我們也可以像平常的檔名一樣在命令中使用。 建立命名管道 我們可以使用兩下函式之一來建立一個命名管道,他們的原型如下:

#include

#include

int mkfifo(const char *filename, mode_t mode);  

int mknod(const char *filename, mode_t mode | s_ififo, (dev_t)0);  

這兩個函式都能建立一個fifo檔案,注意是建立一個真實存在於檔案系統中的檔案, filename指定了檔名,而mode則指定了檔案的讀寫許可權。

mknod是比較老的函式,而使 用mkfifo函式更加簡單和規範,所以建議在可能的情況下,儘量使⽤用mkfifo而不是mknod。

int main()

char buf[_size_];

memset(buf, '\0' ,sizeof (buf));

while (1)

printf( "%s\n" ,buf);

if (strncmp(buf,"quit" ,4) == 0)

break ;

}close(fp);

return 0;

}int main()

int fp = open(_path_,o_wronly);

if (fp == -1)

char buf[_size_];

memset(buf, '\0' ,sizeof (buf));

while (1)

if (strncmp(buf,"quit" ,4) == 0)

break ;

}close(fp);

return 0;

}