匿名管道和pipe函数
一、進程間通信
每個進程各自有不同的用戶地址空間,任何一個進程的全局變量在另一個進程中都看不到,所以進程之間要交換數(shù)據(jù)必須通過內(nèi)核,在內(nèi)核中開辟一塊緩沖區(qū),進程1把數(shù)據(jù)從用戶空間拷到內(nèi)核緩沖區(qū),進程2再從內(nèi)核緩沖區(qū)把數(shù)據(jù)讀走,內(nèi)核提供的這種機制稱為進程間通信(IPC,InterProcess Communication)。如下圖所示。
二、管道是一種最基本的IPC機制,由pipe函數(shù)創(chuàng)建:
#include <unistd.h>
int pipe(int filedes[2]);
調(diào)用pipe函數(shù)時在內(nèi)核中開辟一塊緩沖區(qū)(稱為管道)用于通信,它有一個讀端一個寫端,然后通過filedes參數(shù)傳出給用戶程序兩個文件描述符,filedes[0]指向管道的讀端,filedes[1]指向管道的寫端(很好記,就像0是標準輸入1是標準輸出一樣)。所以管道在用戶程序看起來就像一個打開的文件,通過read(filedes[0]);或者write(filedes[1]);向這個文件讀寫數(shù)據(jù)其實是在讀寫內(nèi)核緩沖區(qū)。pipe函數(shù)調(diào)用成功返回0,調(diào)用失敗返回-1。
開辟了管道之后如何實現(xiàn)兩個進程間的通信呢?比如可以按下面的步驟通信。
示例程序如下:
?
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ????do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}?while(0) int?main(int?argc,?char?*argv[]) { ????int?pipefd[2]; ????if?(pipe(pipefd)?==?-1) ????????ERR_EXIT("pipe?error"); ????pid_t?pid; ????pid?=?fork(); ????if?(pid?==?-1) ????????ERR_EXIT("fork?error"); ????if?(pid?==?0) ????{ ????????close(pipefd[0]); ????????write(pipefd[1],?"hello",?5); ????????close(pipefd[1]); ????????exit(EXIT_SUCCESS); ????} ????close(pipefd[1]); ????char?buf[10]?=?{0}; ????read(pipefd[0],?buf,?10); ????printf("buf=%s\n",?buf); ????return?0; } |
1. 父進程調(diào)用pipe開辟管道,得到兩個文件描述符指向管道的兩端。
2. 父進程調(diào)用fork創(chuàng)建子進程,那么子進程也有兩個文件描述符指向同一管道。
3. 父進程關(guān)閉管道寫端,子進程關(guān)閉管道讀端。子進程可以往管道里寫,父進程可以從管道里讀,管道是用環(huán)形隊列實現(xiàn)的,數(shù)據(jù)從寫端流入從讀端流出,這樣就實現(xiàn)了進程間通信。
三、利用pipe和dup2函數(shù)模擬命令行 ls ?| wc -w 功能
?
C++ Code?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | ? | /************************************************************************* ????>?File?Name:?process_.c ????>?Author:?Simba ????>?Mail:?dameng34@163.com ????>?Created?Time:?Sat?23?Feb?2013?02:34:02?PM?CST ?************************************************************************/ #include<sys/types.h> #include<sys/stat.h> #include<unistd.h> #include<fcntl.h> #include<stdio.h> #include<stdlib.h> #include<errno.h> #include<string.h> #include<signal.h> #define?ERR_EXIT(m)?\ ????do?{?\ ????????perror(m);?\ ????????exit(EXIT_FAILURE);?\ ????}?while(0) int?main(int?argc,?char?*argv[]) { ????int?pipefd[2]; ????if?(pipe(pipefd)?==?-1) ????????ERR_EXIT("pipe?error"); ????pid_t?pid; ????pid?=?fork(); ????if?(pid?==?-1) ????????ERR_EXIT("fork?error"); ????if?(pid?==?0) ????{ ????????dup2(pipefd[1],?STDOUT_FILENO);?//輸出重定向 ????????close(pipefd[1]); ????????close(pipefd[0]); ????????execlp("ls",?"ls",?NULL); ????????fprintf(stderr,?"error?execute?ls\n"); ????????exit(EXIT_FAILURE); ????} ????dup2(pipefd[0],?STDIN_FILENO); ????close(pipefd[0]); ????close(pipefd[1]); ????execlp("wc",?"wc",?"-w",?NULL); ????fprintf(stderr,?"error?execute?wc\n"); ????exit(EXIT_FAILURE); ????return?0; } |
我們知道命令行 ls | wc -w 中 ls 會輸出到管道,而wc 從管道里讀取,現(xiàn)在使用dup2復制文件描述符,使ls 的標準輸出為管道,wc 的標準輸入也為管道,即使父進程先被調(diào)度,因為默認是阻塞I/O操作,故wc 會read 阻塞直到管道被子進程寫入了數(shù)據(jù)。
使用管道有一些限制:
兩個進程通過一個管道只能實現(xiàn)單向通信,比如最上面的例子,父進程讀子進程寫,如果有時候也需要子進程讀父進程寫,就必須另開一個管道。
管道的讀寫端通過打開的文件描述符來傳遞,因此要通信的兩個進程必須從它們的公共祖先那里繼承管道文件描述符。上面的例子是父進程把文件描述符傳給子進程之后父子進程之間通信,也可以父進程fork兩次,把文件描述符傳給兩個子進程,然后兩個子進程之間通信,總之需要通過fork傳遞文件描述符使兩個進程都能訪問同一管道,它們才能通信。
參考:《APUE》、《linux c 編程一站式學習》
總結(jié)
以上是生活随笔為你收集整理的匿名管道和pipe函数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: laravel数据迁移问题
- 下一篇: 同一master,两个slave的ser