linux进程间通信:无名管道 pipe
文章目錄
- 內(nèi)核層實(shí)現(xiàn)
- 結(jié)構(gòu)
- 通信原理
- 特點(diǎn)
- 使用
- 函數(shù)聲明
- 使用實(shí)例
- 單向通信
- 雙向通信
- 編程注意事項(xiàng)
- 管道中無數(shù)據(jù)時(shí)讀操作會(huì)阻塞
- 將管道的寫端句柄關(guān)閉,不會(huì)影響讀端數(shù)據(jù)讀取
- 管道中沒有數(shù)據(jù),寫操作關(guān)閉則讀操作會(huì)立即返回
- 管道大小測試 64K
- 管道發(fā)生寫滿阻塞,一旦有4k空間,寫繼續(xù)
- 總結(jié)
內(nèi)核層實(shí)現(xiàn)
結(jié)構(gòu)
Linux操作系統(tǒng)中的無名管道結(jié)構(gòu)如下圖:
管道在內(nèi)核中的實(shí)現(xiàn)即如一個(gè)緩沖區(qū),內(nèi)核提供將該緩沖區(qū)以一個(gè)文件句柄的形式提供給用戶態(tài)供其調(diào)用,進(jìn)程1使用文件句柄f[0]讀,進(jìn)程2使用文件句柄f[1]寫。
無名管道的數(shù)據(jù)結(jié)構(gòu)聲明文件pipe_fs_i.h
執(zhí)行命令locate pipe_fs_i.h即可找到該文件路徑,查看管道文件描述符如下,由于我的內(nèi)核版本較新,可能該聲明和其他版本差異較大:
/*** struct pipe_inode_info - a linux kernel pipe* @mutex: mutex protecting the whole thing* @wait: reader/writer wait point in case of empty/full pipe* @nrbufs: the number of non-empty pipe buffers in this pipe* @buffers: total number of buffers (should be a power of 2)* @curbuf: the current pipe buffer entry* @tmp_page: cached released page* @readers: number of current readers of this pipe* @writers: number of current writers of this pipe* @files: number of struct file referring this pipe (protected by ->i_lock)* @waiting_writers: number of writers blocked waiting for room* @r_counter: reader counter* @w_counter: writer counter* @fasync_readers: reader side fasync* @fasync_writers: writer side fasync* @bufs: the circular array of pipe buffers* @user: the user who created this pipe**/struct pipe_inode_info {struct mutex mutex;wait_queue_head_t wait;unsigned int nrbufs, curbuf, buffers;unsigned int readers;unsigned int writers;unsigned int files;unsigned int waiting_writers;unsigned int r_counter;unsigned int w_counter;struct page *tmp_page;struct fasync_struct *fasync_readers;struct fasync_struct *fasync_writers;struct pipe_buffer *bufs;struct user_struct *user;
};
通信原理
-
管道的內(nèi)核封裝 是一個(gè)文件(pipefs):
a. 內(nèi)核將一個(gè)緩沖區(qū)與管道文件進(jìn)行關(guān)聯(lián)、封裝
b. 用戶通過open/read/write/close等IO接口進(jìn)行讀寫讀寫過程如下所示,進(jìn)程運(yùn)行時(shí)使用管道會(huì)默認(rèn)打開一些文件句柄包括標(biāo)準(zhǔn)輸入流/輸出流/錯(cuò)誤 等。
特點(diǎn)
- 像一個(gè)管道連接兩個(gè)進(jìn)程
- 一個(gè)進(jìn)程作為輸入,一個(gè)進(jìn)程作為輸出
- 用于親緣關(guān)系進(jìn)程之間的通信:共享資源
使用
函數(shù)聲明
int pipe(int pipefd[2]);int pipe(int pipefd[2],int flags);
成功返回0,失敗返回-1
函數(shù)空間主要包含兩個(gè)文件描述符,一個(gè)用來讀,一個(gè)用來寫
詳細(xì)信息可以通過man 2 pipe查看系統(tǒng)調(diào)用信息
使用實(shí)例
單向通信
實(shí)現(xiàn)方式如下
代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>#define handle_error(msg)\
{perror(msg);exit(-1);}int main()
{int pipe_fd[2];if (pipe(pipe_fd) == -1) //創(chuàng)建管道handle_error("pipe");int f;f = fork();if (f == -1)handle_error("fork");if(f == 0){char str[100] = {0};printf("child process input :\n");scanf("%s",str);write(pipe_fd[1],str,strlen(str)); //子進(jìn)程向管道寫內(nèi)容close (pipe_fd[1]);_exit(0);}if (f > 0){char buf[100] = {0};read (pipe_fd[0],buf,30);printf("parent process output : %s\n",buf); //父進(jìn)程從管道讀內(nèi)容close(pipe_fd[0]);_exit(0);}return 0;
}
輸出如下:
zhang@ubuntu:~/test$ gcc test_pipe.c -o test_pipe
zhang@ubuntu:~/test$ ./test_pipe
child process input :
aa
parent process output : aa
雙向通信
實(shí)現(xiàn)方式如下,父進(jìn)程和子進(jìn)程之間既可以讀也可以寫,該實(shí)現(xiàn)是通過兩個(gè)管道進(jìn)行讀寫處理。
代碼實(shí)現(xiàn)如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>#define handle_error(msg)\
{perror(msg);exit(-1);}int main()
{int pipe_fd[2];int pipe_fd2[2];if (pipe(pipe_fd) == -1 || pipe(pipe_fd2)) //創(chuàng)建兩個(gè)管道handle_error("pipe");int f;f = fork();if (f == -1)handle_error("fork");if(f == 0) //子進(jìn)程處理,子進(jìn)程先寫pipe_fd[1],再讀pipe_fd[0]{char str[100] = {0};char str2[100] = {0};printf("child process input :\n");scanf("%s",str);write(pipe_fd[1],str,strlen(str));close (pipe_fd[1]);read(pipe_fd2[0],str2,100);printf("in child process read : %s\n",str2);close(pipe_fd2[0]);_exit(0);}if (f > 0) //父進(jìn)程和子進(jìn)程相反,先讀pipe_fd[0],再寫pipe_fd2[1]{char buf[100] = {0};char buf2[100] = {0};read (pipe_fd[0],buf,30);printf("parent process output : %s\n",buf);close(pipe_fd[0]);printf("in parent process write: \n");scanf("%s",buf2);write(pipe_fd2[1],buf2,strlen(buf2));_exit(0);}return 0;
}
最終輸出如下:
zhang@ubuntu:~/test$ ./test_pipe_double
child process input :
hello
parent process output : hello
in parent process write:
world
zhang@ubuntu:~/test$ in child process read : world
編程注意事項(xiàng)
管道中無數(shù)據(jù)時(shí)讀操作會(huì)阻塞
如下代碼
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{int fd[2];char buf[50] = {0};//緩存if(pipe(fd)!=0)// 創(chuàng)建無名管道{perror("pipe fail: ");exit(1);}printf("%d %d\n",fd[0],fd[1]);//打開的文件描述符,此處為3,4 默認(rèn)打開 0,1,2,標(biāo)準(zhǔn)輸入,輸出,出錯(cuò)//管道中沒有數(shù)據(jù)的時(shí)候讀阻塞// write(fd[1],"hello",10); //此處不向管道寫數(shù)據(jù)時(shí),讀操作會(huì)阻塞,管道中有數(shù)據(jù)時(shí),讀操作后結(jié)束進(jìn)程read(fd[0],buf,10);printf("%s",buf);putchar(10); // '\n'return 0;
}
輸出如下
將管道的寫端句柄關(guān)閉,不會(huì)影響讀端數(shù)據(jù)讀取
代碼如下
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{int fd[2];char buf[50] = {0};//緩存if(pipe(fd)!=0)// 創(chuàng)建無名管道{perror("pipe fail: ");exit(1);}printf("%d %d\n",fd[0],fd[1]);//打開的文件描述符,此處為3,4 默認(rèn)打開 0,1,2,標(biāo)準(zhǔn)輸入,輸出,出錯(cuò)//管道中沒有數(shù)據(jù)的時(shí)候讀阻塞write(fd[1],"hello",10); //此處不向管道寫數(shù)據(jù)時(shí),讀操作會(huì)阻塞,管道中有數(shù)據(jù)時(shí),讀操作后結(jié)束進(jìn)程close(fd[1]);read(fd[0],buf,10);close(fd[0]);printf("%s",buf);putchar(10); // '\n'return 0;
}
輸出正常如下:
zhang@ubuntu:~/test$ ./a.out
3 4
hello
管道中沒有數(shù)據(jù),寫操作關(guān)閉則讀操作會(huì)立即返回
測試代碼如下:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{int fd[2];char buf[50] = {0};//緩存if(pipe(fd)!=0)// 創(chuàng)建無名管道{perror("pipe fail: ");exit(1);}printf("%d %d\n",fd[0],fd[1]);//打開的文件描述符,默認(rèn)打開 0,1,2//寫端關(guān)閉,管道中無數(shù)據(jù),讀操作立即返回close(fd[1]);read(fd[0],buf,5);return 0;
}
zhang@ubuntu:~/test$ ./a.out
3 4
管道大小測試 64K
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{int fd[2];char buf[65536] = {0};//緩存if(pipe(fd)!=0)// 創(chuàng)建無名管道{perror("pipe fail: ");exit(1);}int f = fork();int num = 0;if (f == 0) {int ret = write(fd[1],"123",1024);while (1 && ret != 0){ret = write(fd[1],"123",1024);printf("write size is %d\n",ret);num ++;printf("write count is %d\n",num);}close(fd[1]);_exit(1);}if (f > 0){printf("%d %d\n",fd[0],fd[1]);//打開的文件描述符,默認(rèn)打開 0,1,2//寫端關(guān)閉,管道中無數(shù)據(jù),讀操作立即返回read(fd[0],buf,65536);printf("write result is %s\n",buf);close(fd[0]);_exit(1);}return 0;
}
輸出如下,當(dāng)寫入數(shù)據(jù)達(dá)到64K時(shí)會(huì)阻塞
write count is 63
write size is 1024
write count is 64
...
管道發(fā)生寫滿阻塞,一旦有4k空間,寫繼續(xù)
include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, const char *argv[])
{int fd[2];char buf[65536] = {0};//緩存if(pipe(fd)!=0)// 創(chuàng)建無名管道{perror("pipe fail: ");exit(1);}int f = fork();int num = 0;if (f == 0) {int ret = write(fd[1],"123",1024);while (1 && ret != 0){ret = write(fd[1],"123",4096);printf("write size is %d\n",ret);num ++;printf("write count is %d\n",num);}close(fd[1]);_exit(1);}if (f > 0){sleep(1);printf("get the wirte result is %d %d\n",fd[0],fd[1]);//打開的文件描述符,默認(rèn)打開 0,1,2//寫端關(guān)閉,管道中無數(shù)據(jù),讀操作立即返回read(fd[0],buf,4096);printf("write result is %s\n",buf);close(fd[0]);_exit(1);}return 0;
}
輸出如下
write count is 15
get the wirte result is 3 4 //讀出來一次,
write result is 123
zhanghuigui@ubuntu:~/test$ write size is 4096 子進(jìn)程繼續(xù)寫入讀出的空間
write count is 16 //寫滿后又發(fā)生了阻塞
...
總結(jié)
綜上可見,管道是應(yīng)用在擁有親緣關(guān)系的進(jìn)程之間的共享資源。
優(yōu)點(diǎn)也很明顯:
管道屬于系統(tǒng)調(diào)用,且數(shù)據(jù)存放在內(nèi)存之中,它的父子進(jìn)程通信過程效率非常高
缺點(diǎn)同樣也很明顯:
父子進(jìn)程通信交互并不友好,阻塞式的通信非常影響用戶體驗(yàn)
總結(jié)
以上是生活随笔為你收集整理的linux进程间通信:无名管道 pipe的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 7plus多少钱啊?
- 下一篇: 再记一次ceph object unfo