管道的概述

管道也叫无名管道,它是是 UNIX 系统 IPC(进程间通信) 的最古老形式,所有的 UNIX 系统都支持这种通信机制。

无名管道有如下特点:

1、半双工,数据在同一时刻只能在一个方向上流动。

2、数据只能从管道的一端写入,从另一端读出。

3、写入管道中的数据遵循先入先出的规则。

4、管道所传送的数据是无格式的,这要求管道的读出方与写入方必须事先约定好数据的格式,如多少字节算一个消息等。

5、管道不是普通的文件,不属于某个文件系统,其只存在于内存中。

6、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

7、从管道读数据是一次性操作,数据一旦被读走,它就从管道中被抛弃,释放空间以便写更多的数据。

8、管道没有名字,只能在具有公共祖先的进程(父进程与子进程,或者两个兄弟进程,具有亲缘关系)之间使用。

对于无名管道特点的理解,我们可以类比现实生活中管子,管子的一端塞东西,管子的另一端取东西。

无名管道是一种特殊类型的文件,在应用层体现为两个打开的文件描述符。

管道的操作

所需头文件:

#include <unistd.h>

int pipe(int filedes[2]);

功能:

创建无名管道。

参数:

filedes: 为 int 型数组的首地址,其存放了管道的文件描述符 filedes[0]、filedes[1]。

当一个管道建立时,它会创建两个文件描述符 fd[0] 和 fd[1]。其中 fd[0] 固定用于读管道,而 fd[1] 固定用于写管道。一般文件 I/O 的函数都可以用来操作管道( lseek() 除外)。

返回值:

成功:0

失败:-1

下面我们写这个一个例子,子进程通过无名管道给父进程传递一个字符串数据:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>  int main(int argc, char *argv[])
{  int fd_pipe[2] = {0};  pid_t pid;  if( pipe(fd_pipe) < 0 ){// 创建无名管道  perror("pipe");  }  pid = fork(); // 创建进程  if( pid < 0 ){ // 出错  perror("fork");  exit(-1);  }  if( pid == 0 ){ // 子进程  char buf[] = "I am sunplus";  // 往管道写端写数据  write(fd_pipe[1], buf, strlen(buf));  _exit(0);  }else if( pid > 0){// 父进程  wait(NULL); // 等待子进程结束,回收其资源  char str[50] = {0};  // 从管理里读数据  read(fd_pipe[0], str, sizeof(str));  printf("str=[%s]\n", str); // 打印数据  }  return 0;
}  

运行结果:

管道的特点

每个管道只有一个页面作为缓冲区,该页面是按照环形缓冲区的方式来使用的。这种访问方式是典型的“生产者——消费者”模型。当“生产者”进程有大量的数据需要写时,而且每当写满一个页面就需要进行睡眠等待,等待“消费者”从管道中读走一些数据,为其腾出一些空间。相应的,如果管道中没有可读数据,“消费者” 进程就要睡眠等待,具体过程如下图所示:

默认的情况下,从管道中读写数据,最主要的特点就是阻塞问题(这一特点应该记住),当管道里没有数据,另一个进程默认用 read() 函数从管道中读数据是阻塞的。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>  int main(int argc, char *argv[])
{  int fd_pipe[2] = {0};  pid_t pid;  if( pipe(fd_pipe) < 0 ){// 创建无名管道  perror("pipe");  }  pid = fork(); // 创建进程  if( pid < 0 ){ // 出错  perror("fork");  exit(-1);  }  if( pid == 0 ){ // 子进程  _exit(0);  }else if( pid > 0){// 父进程  wait(NULL); // 等待子进程结束,回收其资源  char str[50] = {0};  printf("before read\n");  // 从管道里读数据,如果管道没有数据, read()会阻塞  read(fd_pipe[0], str, sizeof(str));  printf("after read\n");  printf("str=[%s]\n", str); // 打印数据  }  return 0;
}  

​​​​​​运行结果:

没有继续往下执行

当然,我们编程时可通过 fcntl() 函数设置文件的阻塞特性。

设置为阻塞:fcntl(fd, F_SETFL, 0);

设置为非阻塞:fcntl(fd, F_SETFL, O_NONBLOCK);

测试代码如下:

#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>  int main(int argc, char *argv[])
{  int fd_pipe[2] = {0};  pid_t pid;  if( pipe(fd_pipe) < 0 ){// 创建无名管道  perror("pipe");  }  pid = fork(); // 创建进程  if( pid < 0 ){ // 出错  perror("fork");  exit(-1);  }  if( pid == 0 ){ // 子进程  sleep(3);  char buf[] = "hello, edu";  write(fd_pipe[1], buf, strlen(buf)); // 写数据  _exit(0);  }else if( pid > 0){// 父进程  fcntl(fd_pipe[0], F_SETFL, O_NONBLOCK); // 非阻塞  //fcntl(fd_pipe[0], F_SETFL, 0); // 阻塞  while(1){  char str[50] = {0};  read( fd_pipe[0], str, sizeof(str) );//读数据  printf("str=[%s]\n", str);  sleep(1);  }  }  return 0;
}  

运行结果:

默认的情况下,从管道中读写数据,还有如下特点(知道有这么回事就够了,不用刻意去记这些特点):

1)调用 write() 函数向管道里写数据,当缓冲区已满时 write() 也会阻塞。

测试代码如下:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>  int main(int argc, char *argv[])
{  int fd_pipe[2] = {0};  pid_t pid;  char buf[1024] = {0};  memset(buf, 'a', sizeof(buf)); // 往管道写的内容  int i = 0;  if( pipe(fd_pipe) < 0 ){// 创建无名管道  perror("pipe");  }  pid = fork(); // 创建进程  if( pid < 0 ){ // 出错  perror("fork");  exit(-1);  }  if( pid == 0 ){ // 子进程  while(1){  write(fd_pipe[1], buf, sizeof(buf));  i++;  printf("i ======== %d\n", i);  }  _exit(0);  }else if( pid > 0){// 父进程  wait(NULL); // 等待子进程结束,回收其资源  }  return 0;
}  

运行结果:

不停的写,当写满的时候也是会产生阻塞的!!!!!!!!!!!

2)通信过程中,别的进程先结束后,当前进程读端口关闭后,向管道内写数据时,write() 所在进程会(收到 SIGPIPE 信号)退出,收到 SIGPIPE 默认动作为中断当前进程。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>  int main(int argc, char *argv[])
{  int fd_pipe[2] = {0};  pid_t pid;  if( pipe(fd_pipe) < 0 ){// 创建无名管道  perror("pipe");  }  pid = fork(); // 创建进程  if( pid < 0 ){ // 出错  perror("fork");  exit(-1);  }  if( pid == 0 ){ // 子进程  //close(fd_pipe[0]);  _exit(0);  }else if( pid > 0 ){// 父进程  wait(NULL); // 等待子进程结束,回收其资源  close(fd_pipe[0]); // 当前进程读端口关闭  char buf[50] = "12345";  // 当前进程读端口关闭  // write()会收到 SIGPIPE 信号,默认动作为中断当前进程  write(fd_pipe[1], buf, strlen(buf));  while(1);   // 阻塞  }  return 0;
}  

运行结果:

进程间通信无名管道 --- pipe 典型的生产者消费者模式相关推荐

  1. 面试官让我手写一个生产者消费者模式?

    不知道你是否遇到过面试官让你手写生产者消费者代码.别说,前段时间有小伙伴还真的遇到了这种情况,当时是一脸懵逼. 但是,俗话说,从哪里跌倒就要从哪里爬起来.既然这次被问到了,那就回去好好研究一下,争取下 ...

  2. python queue 生产者 消费者_Queue: 应用于生产者-消费者模式的Python队列

    图片来源于网络 版权声明 © 著作权归作者所有 允许自由转载,但请保持署名和原文链接. 不允许商业用途.盈利行为及衍生盈利行为. 什么是Queue? Queue是Python标准库中的线程安全的队列( ...

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

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

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

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

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

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

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

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

  7. 生产者/消费者模式的理解及实现

    ★简介 生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程 ...

  8. python 异步 生产者 消费者_python 生产者消费者模式 - 刘江的python教程

    生产者消费者模式 阅读: 9884 评论:4 利用多线程和队列可以实现生产者消费者模式.该模式通过平衡生产线程和消费线程的工作能力来提高程序整体处理数据的速度. 什么是生产者和消费者? 在线程世界里, ...

  9. 生产者/消费者模式之深入理解

    ★简介 生产者消费者模式并不是GOF提出的23种设计模式之一,23种设计模式都是建立在面向对象的基础之上的,但其实面向过程的编程中也有很多高效的编程模式,生产者消费者模式便是其中之一,它是我们编程过程 ...

  10. 消息队列:生产者/消费者模式

    1.什么是生产者消费者模式 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接 ...

最新文章

  1. vue实现一个星级打分效果_五分钟用vue实现一个五星打分效果
  2. Javascript中的对象和原型(一)(转载)
  3. NIOS2随笔——uCOS-II实时操作系统
  4. play2框架 jpa mysql_单元测试 – Playframework 2.2.x Java JPA – 用于单元测试和生产的独立数据库...
  5. mysql springboot 缓存_Spring Boot 整合 Redis 实现缓存操作
  6. 玩具(BZOJ-1307)
  7. react 渲染道具_在React中学习分解道具的基础
  8. VB小技巧 文本框中屏蔽系统右键菜单用自定义菜单代替自带右键菜单
  9. Java Timer TimerTask示例
  10. java简单图书馆管理系统_简单 图书馆管理系统 Java+Oracle
  11. 21天学通java web 第二版pdf_21天学通JAVA WEB开发 pdf完全版_IT教程网
  12. 计算机二级Office选择题考题大全【掌握】
  13. 形式语言与自动机 下推自动机
  14. IDEA——一个项目启动多个服务
  15. web服务器和数据库服务器分离的优势
  16. ch9200 usb网卡驱动_21包邮的PCMCIA无线网卡开箱+对比测评
  17. 山西初中计算机,山西初中信息技术教学计划
  18. 用U深度启动U盘清除系统登入密码
  19. python做房源饼状图_python使用matplotlib画饼状图
  20. lenove Anti-Vieus Powered by Huorong Security保护已过期怎么办

热门文章

  1. L1-044 稳赢 (15 分)—团体程序设计天梯赛
  2. centos7安装python3.6独立的virtualenv环境
  3. MQTT 连接服务端失败,报错客户机未连接(32104)
  4. 【学习笔记】深入理解Linux内核第三版 ——第二章 内存寻址
  5. 树和二叉树总结(三)—BST二叉排序树
  6. PTA是什么?BT-WIFI共存 转帖
  7. 位CPU和64位CPU 区别
  8. HCIE-Security Day16:防火墙双机热备实验(四)防火墙直路部署,上行连接路由器(OSPF),下行连接交换机
  9. 二十六、K8s系统强化2-seccomp与sysdig
  10. Kubernetes详解(十)——Pod对象高级控制命令