1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > linux进程间通信:无名管道 pipe

linux进程间通信:无名管道 pipe

时间:2022-08-07 07:19:26

相关推荐

linux进程间通信:无名管道 pipe

文章目录

内核层实现结构通信原理特点使用函数声明使用实例单向通信双向通信编程注意事项管道中无数据时读操作会阻塞将管道的写端句柄关闭,不会影响读端数据读取管道中没有数据,写操作关闭则读操作会立即返回管道大小测试 64K管道发生写满阻塞,一旦有4k空间,写继续总结

内核层实现

结构

Linux操作系统中的无名管道结构如下图:

管道在内核中的实现即如一个缓冲区,内核提供将该缓冲区以一个文件句柄的形式提供给用户态供其调用,进程1使用文件句柄f[0]读,进程2使用文件句柄f[1]写。

无名管道的数据结构声明文件pipe_fs_i.h

执行命令locate pipe_fs_i.h即可找到该文件路径,查看管道文件描述符如下,由于我的内核版本较新,可能该声明和其他版本差异较大:

/***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;};

通信原理

管道的内核封装 是一个文件(pipefs):

a. 内核将一个缓冲区与管道文件进行关联、封装

b. 用户通过open/read/write/close等IO接口进行读写

读写过程如下所示,进程运行时使用管道会默认打开一些文件句柄包括标准输入流/输出流/错误 等。

特点

像一个管道连接两个进程一个进程作为输入,一个进程作为输出用于亲缘关系进程之间的通信:共享资源

使用

函数声明

int pipe(int pipefd[2]);int pipe(int pipefd[2],int flags);

成功返回0,失败返回-1

函数空间主要包含两个文件描述符,一个用来读,一个用来写

详细信息可以通过man 2 pipe查看系统调用信息

使用实例

单向通信

实现方式如下

代码如下:

#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) //创建管道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)); //子进程向管道写内容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); //父进程从管道读内容close(pipe_fd[0]);_exit(0);}return 0;}

输出如下:

zhang@ubuntu:~/test$ gcc test_pipe.c -o test_pipezhang@ubuntu:~/test$ ./test_pipe child process input :aaparent process output : aa

双向通信

实现方式如下,父进程和子进程之间既可以读也可以写,该实现是通过两个管道进行读写处理。

代码实现如下:

#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)) //创建两个管道handle_error("pipe");int f;f = fork();if (f == -1)handle_error("fork");if(f == 0) //子进程处理,子进程先写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) //父进程和子进程相反,先读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 :helloparent process output : helloin parent process write: worldzhang@ubuntu:~/test$ in child process read : world

编程注意事项

管道中无数据时读操作会阻塞

如下代码

#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)// 创建无名管道{perror("pipe fail: ");exit(1);}printf("%d %d\n",fd[0],fd[1]);//打开的文件描述符,此处为3,4 默认打开 0,1,2,标准输入,输出,出错//管道中没有数据的时候读阻塞// write(fd[1],"hello",10); //此处不向管道写数据时,读操作会阻塞,管道中有数据时,读操作后结束进程read(fd[0],buf,10);printf("%s",buf);putchar(10); // '\n'return 0;}

输出如下

将管道的写端句柄关闭,不会影响读端数据读取

代码如下

#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)// 创建无名管道{perror("pipe fail: ");exit(1);}printf("%d %d\n",fd[0],fd[1]);//打开的文件描述符,此处为3,4 默认打开 0,1,2,标准输入,输出,出错//管道中没有数据的时候读阻塞write(fd[1],"hello",10); //此处不向管道写数据时,读操作会阻塞,管道中有数据时,读操作后结束进程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 4hello

管道中没有数据,写操作关闭则读操作会立即返回

测试代码如下:

#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)// 创建无名管道{perror("pipe fail: ");exit(1);}printf("%d %d\n",fd[0],fd[1]);//打开的文件描述符,默认打开 0,1,2//写端关闭,管道中无数据,读操作立即返回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)// 创建无名管道{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]);//打开的文件描述符,默认打开 0,1,2//写端关闭,管道中无数据,读操作立即返回read(fd[0],buf,65536);printf("write result is %s\n",buf);close(fd[0]);_exit(1);}return 0;}

输出如下,当写入数据达到64K时会阻塞

write count is 63write size is 1024write count is 64...

管道发生写满阻塞,一旦有4k空间,写继续

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)// 创建无名管道{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]);//打开的文件描述符,默认打开 0,1,2//写端关闭,管道中无数据,读操作立即返回read(fd[0],buf,4096);printf("write result is %s\n",buf);close(fd[0]);_exit(1);}return 0;}

输出如下

write count is 15get the wirte result is 3 4 //读出来一次,write result is 123 zhanghuigui@ubuntu:~/test$ write size is 4096 子进程继续写入读出的空间write count is 16 //写满后又发生了阻塞...

总结

综上可见,管道是应用在拥有亲缘关系的进程之间的共享资源。

优点也很明显:

管道属于系统调用,且数据存放在内存之中,它的父子进程通信过程效率非常高

缺点同样也很明显:

父子进程通信交互并不友好,阻塞式的通信非常影响用户体验

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。