Linux管道FIFO
2.1 管道基本概念
管道是針對於本地計算機的兩個程式之間的通訊而設計的通訊方法,管道建立後,實際上是獲得兩個檔案描述符:一個用與讀取而另一個用於寫入。任何從管道寫入端寫入的資料,可以從管道讀取端讀出。
管道通訊具有以下特點:
管道是半雙工的,資料只能向一個方向流動,需要雙方通訊時,要建立起兩個管道。
管道存放在記憶體中,是一種獨立的檔案系統。
2.2 無名管道的建立與讀寫
系統呼叫pipe()用於建立一個管道,其函式原型如下:
int pipe(int pipefd[2]);
pipe()將建立一對檔案描述符,放到引數pipefd
中。pipefd[0]
檔案描述符用來從管道中讀取資料,pipefd[1]
用於寫入資料到管道。
單個程式中的管道幾乎沒有任何意義,通常,程式會先呼叫pipe,接著呼叫fork,從而建立從父程式到子程式的通道。
fork出子程式後,父程式的檔案描述表也複製到子程式,於是如下圖:
關閉掉父程式的fd[0], 以及子程式的fd[1],則父程式可以傳送訊息給子程式,反之亦然。
原始碼:pipe_test.c
void child_process(int pipefd[]) { int i = 0; char writebuf[128] = {0}; /*子程式關閉讀檔案描述符*/ close(pipefd[0]); while(1) { sprintf(writebuf, "write pipe : %d", i); /*子程式往管道寫入資料*/ write(pipefd[1], writebuf, strlen(writebuf)); printf("write pipe: %s\n", writebuf); i = (i + 1) % 10; sleep(1); } } void father_process(int pipefd[]) { char readbuf[128] = {0}; /*父程式關閉寫檔案描述符*/ close(pipefd[1]); while(1) { /*父程式從管道中讀取資料*/ read(pipefd[0], readbuf, sizeof(readbuf)); printf("read pipe: %s\n", readbuf); } } int main(int argc, char** argv) { int pipefd[2]; pid_t pid; if( pipe(pipefd) == -1) { perror("pipe:"); return -1; } pid = fork(); if(pid == 0) { child_process(pipefd); } else if(pid != -1) { father_process(pipefd); } else { perror("fork:"); } return 0; }
執行結果:子程式寫資料,父程式讀出資料並且列印。
where@ubuntu:~$ ./pipe_test write pipe: write pipe : 0 read pipe: write pipe : 0 write pipe: write pipe : 1 read pipe: write pipe : 1 write pipe: write pipe : 2 read pipe: write pipe : 2 write pipe: write pipe : 3
注意:匿名管道只能用於父子程式或者兄弟程式之間(具有親緣關係的程式)。
如果管道讀端被關閉,那麼這個時候如果繼續write()
管道,會發出訊號SIGPIPE
。如果管道寫端被關閉,
那麼這個時候如果繼續read()
管道直接返回0。
2.3 命名管道FIFO
FIFO是first in first out(先進先出)的縮寫,FIFO也稱為“命名管道”。FIFO是一種特殊型別的管道,它在檔案系統中有一個相應的檔案,稱為管道檔案。
FIFO檔案可以通過mkfifo()
函式建立。在FIFO檔案建立之後,任何一個具有適當許可權的程式都可以開啟FIFO檔案。
mkfifo()函式原型為:
int mkfifo(const char* pathname, mode_t mode);
引數:
pathname
一個FIFO檔案的路徑名。
mode
與普通檔案creat()
函式中的mode引數相同。
返回值:
如果要建立的檔案已經存在,返回-1, errno
為EEXIST
錯誤, 成功返回0。
一般檔案的I/O函式都可以用於FIFO,如open()、read()、write()、close()等等。
原始碼fifo_write_test.c
void handle_sig(int sig) { printf("signal pipe\n"); exit(-1); } int main(int argc, char** argv) { int fd; int ret; char buf[128]; int i = 0; /*處理管道訊號*/ signal(SIGPIPE, handle_sig); if(mkfifo("./fifo", 0640) == -1) { if(errno != EEXIST) { perror("mkfifo"); return -1; } } /*只寫方式開啟管道*/ fd = open("./fifo", O_WRONLY); if(fd == -1) { perror("open"); return -1; } while(1) { sprintf(buf, "data %d", i++); /*往管道寫資料*/ ret = write(fd, buf, strlen(buf)); printf("write fifo [%d] %s\n", ret, buf); sleep(1); } return 0; }
原始碼中,通過mkfifo來建立FIFO檔案,並且以只寫 的方式開啟,只有當兩邊的管道都開啟的時候才能寫進去,否則阻塞在write()
函式上,如果管道另一端開啟後被關閉,那麼這個時候如果繼續write()
FIFO管道,會發出訊號SIGPIPE
.
原始碼fifo_read_test.c
int main(int argc, char** argv) { int fd; int ret; char buf[128]; if(mkfifo("./fifo", 0640) == -1) { if(errno != EEXIST) /*如果錯誤型別是fifo檔案已經存在,則繼續執行*/ { perror("mkfifo"); return -1; } } /*以只讀方式開啟管道*/ fd = open("./fifo", O_RDONLY); if(fd == -1) { perror("open"); return -1; } while(1) { memset(buf, 0, sizeof(buf)); /*讀管道*/ ret = read(fd, buf, sizeof(buf) - 1); printf("read fifo [%d] : %s\n", ret, buf); sleep(1); } return 0; }
原始碼中,通過mkfifo()
函式建立FIFO管道,如果已經存在那麼就直接只讀方式開啟,如果另一端沒有被開啟,則阻塞在read()
函式上,如果另一端開啟後關閉,則read()
一直讀到EOF也就是0個位元組。
2.4 管道容量
管道有它的容量大小,通過man 7 pipe來檢視。預設情況下為64k位元組,也可以通過fcntl函式來檢視:
//在任何標頭檔案包含之前新增這個巨集定義 int fcntl(int fd, int cmd, ... /* arg */ ); /* cmd:F_GETPIPE_SZ 獲取管道容量大小,設定管道容量大小F_SETPIPE_SZ */
例如:
int main() { if(mkfifo("./fifo", 0640) == -1) { if(errno != EEXIST) { perror("mkfifo"); return -1; } } /*只寫方式開啟管道*/ int fd = open("./fifo", O_WRONLY | O_NONBLOCK); if(fd == -1) { perror("open"); return -1; } printf("pipe size %d\n", fcntl(fd, F_GETPIPE_SZ)); fcntl(fd, F_SETPIPE_SZ, 4096 * 3); /*必須填寫4096的整數倍*/ printf("pipe size %d\n", fcntl(fd, F_GETPIPE_SZ)); return 0; }
2.5 注意事項
使用管道需要注意以下4種特殊情況(假設都是阻塞I/O操作,沒有設定O_NONBLOCK標誌):
1.如果所有指向管道寫端的檔案描述符都關閉了(管道寫端的引用計數等於0),而仍然有程式從管道的讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會返回0,就像讀到檔案末尾一樣。
2.如果有指向管道寫端的檔案描述符沒關閉(管道寫端的引用計數大於0),而持有管道寫端的程式也沒有向管道中寫資料,這時有程式從管道讀端讀資料,那麼管道中剩餘的資料都被讀取後,再次read會阻塞,直到管道中有資料可讀了才讀取資料並返回。
3.如果所有指向管道讀端的檔案描述符都關閉了(管道讀端的引用計數等於0),這時有程式向管道的寫端write,那麼該程式會收到訊號SIGPIPE,通常會導致程式異常終止。講訊號時會講到怎樣使SIGPIPE訊號不終止程式。
4.如果有指向管道讀端的檔案描述符沒關閉(管道讀端的引用計數大於0),而持有管道讀端的程式也沒有從管道中讀資料,這時有程式向管道寫端寫資料,那麼在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入資料並返回。
相關文章
- Linux程式間通訊②:有名管道FIFOLinux
- linux程式間通訊--管道(PIPE & FIFO)Linux
- [Linux]管道Linux
- 【linux】管道!!!Linux
- Linux 管道Linux
- linux 程式間通訊之FIFOLinux
- Linux管道符Linux
- Linux 之管道Linux
- linux管道詳解Linux
- linux——管道詳解Linux
- 介紹 Linux 中的管道和命名管道Linux
- FIFO
- Linux 命令 管道 緩衝區Linux
- linux9-grep&wc&管道符Linux
- linux 程式間通訊之管道Linux
- Linux 的程式間通訊:管道Linux
- (9)FIFO ip核
- Linux管道命令是什麼?如何使用?Linux
- Linux中重定向和管道介紹Linux
- Linux系統程式設計—有名管道Linux程式設計
- Linux的管道機制和重定向Linux
- Linux系統程式設計之匿名管道Linux程式設計
- FIFO讀數取數
- Linux中的管道是什麼?管道與共享記憶體的區別有哪些?Linux記憶體
- linux3-管道符、重定向、環境變數Linux變數
- 在Linux中,什麼是管道?它是如何工作的?Linux
- 用verilog/systemverilog 設計fifo (2)
- 用verilog/systemverilog 設計fifo (1)
- (5)FIFO知識點總結
- 使用兩個FIFO完成流水操作
- (js佇列,堆疊) (FIFO,LIFO)JS佇列
- 管道 |
- 在Linux中,什麼是管道操作,以及如何使用它?Linux
- Linux 核心最新高危提權漏洞:髒管道 (Dirty Pipe)Linux
- Linux:管道命令與文字處理三劍客(grep、sed、awk)Linux
- 在Linux中,管道(pipe)和重定向(redirection)的是什麼?Linux
- 『學了就忘』Linux基礎命令 — 33、管道符的使用Linux
- 【linux】系統程式設計-1-程式、管道和訊號Linux程式設計
- Filter管道Filter