文章目录

  • 内核层实现
    • 结构
    • 通信原理
    • 特点
  • 使用
    • 函数声明
    • 使用实例
      • 单向通信
      • 双向通信
    • 编程注意事项
      • 管道中无数据时读操作会阻塞
      • 将管道的写端句柄关闭,不会影响读端数据读取
      • 管道中没有数据,写操作关闭则读操作会立即返回
      • 管道大小测试 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_pipe
zhang@ubuntu:~/test$ ./test_pipe
child process input :
aa
parent 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 :
hello
parent process output : hello
in parent process write:
world
zhang@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 4
hello
管道中没有数据,写操作关闭则读操作会立即返回

测试代码如下:

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

总结

综上可见,管道是应用在拥有亲缘关系的进程之间的共享资源。
优点也很明显:
管道属于系统调用,且数据存放在内存之中,它的父子进程通信过程效率非常高
缺点同样也很明显:
父子进程通信交互并不友好,阻塞式的通信非常影响用户体验

linux进程间通信:无名管道 pipe相关推荐

  1. Linux进程间通信之管道(pipe)、命名管道(FIFO)与信号(Signal)

    整理自网络 Unix IPC包括:管道(pipe).命名管道(FIFO)与信号(Signal) 管道(pipe) 管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道 ...

  2. 无名管道PIPE,进行父子双进程的“双向通信”

    更多资料请点击:我的目录 本篇仅用于记录自己所学知识及应用,代码仍可优化,仅供参考,如果发现有错误的地方,尽管留言于我,谢谢. 本篇记录应用无名管道PIPE,进行父子双进程的"双向通信&qu ...

  3. Windows API 进程间通信,管道(Pipe)

    2019独角兽企业重金招聘Python工程师标准>>> 转载自:Windows API 进程间通信,管道(Pipe) 管道是一种用于在进程间共享数据的机制,其实质是一段共享内存.Wi ...

  4. 【Linux系统编程】进程间通信--无名管道(pipe)

    管道的概述 管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制. 无名管道有如下特点: 1.半双工,数据在同一时刻只能在一个方向上流 ...

  5. Linux内核中无名管道pipe和有名管道fifo的分析

    1.管道(pipe) 管道是进程间通信的主要手段之一.一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端.管道是一种特殊的文件,它不属于某一种 ...

  6. Linux系统无名管道通信实验,Linux进程间通信(二)---管道通信之无名管道及其基础实验...

    管道简介 管道是Linux中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入(其实我更愿意将管道比喻为农村浇地的管子).Linux的管道主要包括两种:无名管道和有名管道.这一节主要讲 ...

  7. linux系统调用创建无名管道,linux 无名管道pipe和有名管道FIFO

    1.管道(pipe) 管道是进程间通信的主要手段之一.一个管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端.管道是一种特殊的文件,它不属于某一种 ...

  8. 进程间通信--无名管道(pipe)

    无名管道通信(pipe) 无名管道通信主要用于父子进程之间,依赖fork的复制,或者vfork的共享,得以分享所得的无名管道的文件描述符. 总结其性质有以下几点 1. 管道是半双工的,数据在一个时间只 ...

  9. Linux进程间通信分类 以及 pipe的原理实现

    http://blog.sina.com.cn/s/blog_4a84bd960100by8s.html http://home.lupaworld.com/home-space-uid-296848 ...

最新文章

  1. centos7grub2 引导win10
  2. git进入项目目录 windows_Windows下搭建Git服务器
  3. 英国再推人工智能报告: 四方面发力打造AI强国
  4. 洛谷 2680 (NOIp2015) 运输计划
  5. 在eclipse中配置android ndk的自动编译环境builders
  6. vue html5 picker,基于vue的picker组件
  7. GStreamer(二)
  8. WampServer服务器离线问题的解决方法
  9. js 中exec、test、match、search、replace、split用法
  10. 子页面应用母版页图片无法显示
  11. 手机腾讯网mt框架简介
  12. VS 2005 下载地址
  13. word绿豆沙颜色设置_Win7系统下将txt和word背景颜色设置为豆沙绿的方法
  14. 弘辽科技:直通车测款的必备技巧
  15. linux 卸载dnw命令,《转载》linux下利用dnw烧写文件,dnw安装与配置
  16. L1-084 拯救外星人 C语言
  17. UE4 令Actor对象在Editor下执行Tick
  18. 如何避免搜索引擎爬虫产生的流量过大以及搜索引擎设置优化
  19. 结束语-放弃完美主义,执行力就是限时限量认真完成
  20. 匈牙利命名法(指导)

热门文章

  1. 重新mysql-server
  2. CodeForces 595A
  3. flask异步操作_Python Flask后端异步处理(三)
  4. android 不固定指示器,Android简单的页面指示器
  5. 字符串数组(就自己做个笔记)
  6. oracle valueerror,Oracle VALUE_ERROR异常(挑战题编号000005)
  7. java菱形乱码 编码_JAVA:编码与乱码问题
  8. C mysql添加_MYSQL将表 B C 插入表A方法
  9. 乔安监控云存储_让数据更安全可用,阿里云存储多项新功能发布
  10. Eigen(1):Matrix模板类