程序間通訊之管道--pipe和fifo使用
匿名管道pipe
函式原型:
#include <unistd.h> int pipe(int fildes[2]);
引數說明
fildes是我們傳入的陣列,也是一個傳出引數。fildes[0]是讀端,fildes[1]是寫端。
返回值
- 成功呼叫返回0。
- 失敗呼叫返回-1且設定errno.
例項
現在實現一個用父程序讀,子程序寫的管道例子。
int main(int argc, char const *argv[]) { int pipefd[2]; pipe(pipefd); pid_t pid = fork(); if (pid == 0) { //子程序寫 //關閉讀 close(pipefd[0]); write(pipefd[1], "hello fuck!", 15); sleep(1); } else if (pid > 0) { //關閉寫 close(pipefd[1]); char buf[16] = {0}; read(pipefd[0], buf, sizeof(buf)); printf("%s\n", buf); } return 0; }
在這個程式執行之後,父程序會讀取到子程序所寫的資料。在這個簡單的例子中看不到什麼坑點。
坑點
在親緣程序中,我們使用管道要注意以下幾點。就拿父子程序作為例子:
當前情況是,雙方都掌控有讀寫端,都可以操作。
fork之後,共享管道的讀寫端。我們一方讀一方寫,最好要自己指定方向,使資料沿著固定的方向流動。如:
我們這樣就指定了子程序來寫,父程序來讀。也就是說, 我們僅僅只需要在程式碼上加上兩個Close就可以完成這個功能:close父程序的寫端,close子程序的讀端。
如果我們不這樣會發生什麼?
坑點總結
- 寫程序:
- 讀端全部關閉: 在寫程序中,如果核心發現讀端全部關閉,核心會認為沒有人會來讀資料,因此殺死寫程序,通過發出的SIGPIPE。
- 讀端並未全部關閉: 在寫程序中,如果核心發現讀端並未全部關閉,會寫入資料直到管道滿,write會阻塞,等待資料被讀取,有地方可寫會再寫。
- 讀程序:
- 寫端全部關閉: 在讀程序中,寫端全部關閉,會讀取完管道中的資料,然後返回0,表示讀到末尾了。和檔案讀取一致。
- 寫端並未全部關閉: 在讀程序中,寫端並未全部關閉,如果管道有資料就會讀取,沒有資料就會阻塞等待寫端的寫入。
其中特別要注意,寫端沒有全關閉的時候並且無資料的時候,讀端會阻塞等待寫。
Note
- 資料不能反覆讀取,讀了就會被取走。
- 半雙工。
- 有親緣關係程序間使用。
- 可以通過ulimit -a指令檢視各種塊的限制大小。其中pipe size就是管道的大小。
FIFO有名管道
此管道突破了匿名管道只能親緣間通訊的限制。
可以用命令直接建立一個有名管道:
mkfifo myfifo
就可以看到一個myfifo管道了。Linux下一切皆檔案,開啟這個管道和開啟檔案一樣,用open函式即可。
下面寫了一個讀程式和寫程式,可以參考:
//讀程序,讀了就會被取走,可同時執行兩次看結果 int main(int argc, char const *argv[]) { int fd = open("myfifo", O_RDONLY); while (1) { char buf[128] = {0}; int ret = read(fd, buf, sizeof(buf));//如果寫端被關閉,讀端返回0,就是讀到末尾了。 if (ret)printf("%s\n", buf); else break; } return 0; }
寫程式如下:
//寫程序。 int main(int argc, char const *argv[]) { int fd = open("myfifo", O_WRONLY); int num = 1; while (1) { char buf[128] = {0}; sprintf(buf, "read data:%04d", num++); write(fd, buf, strlen(buf)); sleep(1); } return 0; }
和普通檔案操作並無區別。
Note
假設我們先執行讀程式,還沒執行寫程式時,會阻塞,直到寫端被開啟。
同理,我們先執行寫程式,還沒執行讀程式時,也會阻塞,知道讀端被開啟。
以上告訴我們,fifo必須兩端都開啟的時候才能工作,否則阻塞。