一、管道通信

管道的通信方式分为无名管道和有名管道,无名管道可用于具有亲缘关系的进程间的通信,有名管道克服了管道没有名字的限制。

管道是Linux支持的最初UNIX IPC形式之一,具有以下特点。

  1. 管道是半双工的,数据只能一个方向流动;在进行双方通信时,需要建立起两个管道。
  2. 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)
  3. 单独构成一种独立的文件系统。管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。
  4. 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出,写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓存区的头部读出数据。

二、创建无名管道

#include<unistd.h>

int pipe(int filedes[2]);

pipe()会建立管道,并将文件描述符有参数filedes数组返回,filedes[0]为管道里的读取端,filedes[1]则为管道端的写入端

管道用于不同进程间通信。通常先创建一个管道,再通过fork函数创建一个子进程,该子进程会继承父进程锁创建的管道

注:必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符,

2.读写无名管道

管道两端可分别用描述符fd[0]和fd[1]来描述,需要注意的是,管道的两端固定了任务,即一端只能用于读,由描述符fd[0]表示,称其为管道读段,另一端则智能用于写,由描述符fd[1]来表示,称其为管道写端。如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于管道,如close、read和write等。

从管道中读取数据点额步骤如下。

  1. 如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出的字节数为0.
  2. 若管道的写端存在,如果请求的字节数大于PIPE_BUF,则返回管道中现有的数据字节数;如果请求的字节数目不大于PIPE_BUF,则返回管道总现有数据字节数(此时,管道中数据量小于请求的数据量),或者返回请求的字节数(此时,管道中数据量不小于请求的额数据量)。

附加结论:在管道写端关闭后,写入的数据将一直存在,知道被读出为止。

在向管道中写入数据时,Linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓存区中的数据,那么写操作将一直阻塞。

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将受到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略。

例程

#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
int main()
{int pipe_fd[2];pid_t pid;char r_buf[4];char * w_buf[256];int childexit = 0;int i;int cmd;memset(r_buf,0,sizeof(r_buf));if(pipe(pipe_fd) < 0){printf("pipe create error\r\n");return -1;}if((pid = fork()) == 0){printf("\n");close(pipe_fd[1]);sleep(2);while(!childexit){read(pipe_fd[0],r_buf,4);cmd = atoi(r_buf);if(cmd == 0 ){printf("child :receive command form parent over\n now child precessexit\n");childexit = 1;}else if(handle_cmd(cmd)!=0)return;sleep(1);}close(pipe_fd[0]);exit(0);}else if(pid > 0){close(pipe_fd[0]);w_buf[0] = "003";w_buf[1] = "005";w_buf[2] = "777";w_buf[3] = "000";for(i =0;i<4;i++ )write(pipe_fd[1],w_buf[i],4);close(pipe_fd[1]);}}int handle_cmd(int cmd)
{if((cmd < 0) || (cmd > 256)){printf("child:invaild commad\r\n");return -1;}printf("child :the cmd form parent is %d\n ",cmd);return 0;
}

三、创建有名管道

管道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道提出后,克服了该限制。FIFO不同于管道之处在于他提供一个路径名与之关联,一FIFO的文件形式存在于文件系统中。这样,即使与FIFO的创建进程不选在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信,因此不相关的进程也能够通过FIFO交换数据。FIFO严格遵循先进先出,对管道及FIFO的读总是从开始处返回数据的,对他们的写则把数据添加到末尾,他们不支持诸如lseek()等文件定位操作

#include<sys/types.h>

#include<sys/stat.h>

int mkfifo(const char *pathname,mode_t mode)

mkfifo()会依参数pathname建立特殊的FIFO文件,该文件必须不存在,而参数mode为该文件的权限(mode%~umask),因此umask值也会影响到FIFO文件的权限,mkfifo()建立的FIFO文件其他进程都可以读写一般文件的方式存取。当使用open()来打开FIFO文件时,O_NONBLOCK旗标会有影响。

2.读写有名管道

1.从FIFO中读取数据

约定:如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。

(1)如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞,对于没有设置阻塞标志读操作来说则返回-1,当前error值为EAGAIN

(2)对了设置了阻塞标志的读操作来说,造成阻塞的原因有两种:当前FIFO内有数据,但是有其他进程在读这些数据;另外就是FIFO内没有数据,不阻塞的原因则是FIFO中有新的数据写入,不论写入数据量的大小,也不论读操作请求多少数据量。

(3)读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其他将要执行的读操作将不再阻塞,及时在执行读操作时,FIFO中没有数据也一样

(4)如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞

注:如果FIFO中有数据,则设置了阻塞标志的读操作不会因为FIFO中的字节数小于请求读的字节数而阻塞,此时,读操作会返回FIFO中的现有的数据量

2.从FIFO中写入数据

约定:如果一个进程为了从FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作。

(1)对于设置了阻塞标志的写操作

  1. 当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性,如果此时管道空闲缓存区不足以容纳要写入的字节数,则进入睡眠,直到当缓存区中能够容纳要写入的字节数时,才开始进行一次性写操作
  2. 当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性,FIFO缓存区已有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回

(2)对于没有设置阻塞标志的写操作

  1. 要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性,在写满所有FIFO空闲缓存区后,写操作返回
  2. 当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性,如果当前FIFO空闲缓存区能够容纳要写入的字节数,写完后成功返回;如果当前FIFO空闲缓存区不能够容纳要写入的字节数,则返回EAGAIN错误,提醒以后再写;

写端代码

#include<stdio.h>
#include<unistd.h>
#include<errno.h>#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>#define FIFO_SERVER "./fifoserver"int main(int argc,char *argv[])
{int fd;char w_buf[4096*2];int real_wnum;memset(w_buf,0,4096*2);if((mkfifo(FIFO_SERVER,O_CREAT|O_EXCL) < 0) && (errno != EEXIST)){printf("cannot create fifoserver\n");}//设置非阻塞标志fd = open(FIFO_SERVER,O_WRONLY,0);//fd = open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);if(fd == -1){if(errno == ENXIO)printf("open error; no reading process\n");}real_wnum = write(fd,w_buf,2048);if(real_wnum == -1){if(errno == EAGAIN)printf("wite to fifo error;try later");}else{printf("real wite num is %d\n",real_wnum);}//5000为了验证写入字节大于4096时的非原子性real_wnum = write(fd,w_buf,5000);//real_wnum = write(fd,w_buf,4096);if(real_wnum == -1){if(errno == EAGAIN)printf("try later");}}

读端代码

#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>#define FIFO_SERVER "./fifoserver"int main(int argc,char **argv)
{char r_buf[4096*2];int fd;int r_size;int ret_size;r_size = atoi(argv[1]);printf("requred real read bytes %d\n",r_size);memset(r_buf,0,sizeof(r_buf));// fd = open(FIFO_SERVER,O_RDONLY|O_NONBLOCK,0);fd = open(FIFO_SERVER,O_RDONLY,0);if(fd ==-1){printf("open %s for read error");exit(0);}while(1){memset(r_buf,0,sizeof(r_buf));ret_size = read(fd,r_buf,r_size);if(ret_size == -1){if(errno == EAGAIN)printf("no data avlaible\n");}printf("real read bytes %d\n",ret_size);sleep(1);}pause();unlink(FIFO_SERVER);
}

四、管道通信方式的应用场景

管道常用于两个方面:

(1)在shell中时常会用到管道(作为输入输出的重定向),在这种应用方式下,管道的创建对于用户来说是透明的

(2)用于具有情缘关系的进程间通信,用户自己创建管道,并完成读写操作

FIFO可以说是管道的推广,克服了无名管道的限制,使得无亲缘关系的进程同样可以采用先进先出的通信机制进行通信。

管道和FIFO的数据是字节流,应用程序之间必须是先确定特定的传输协议,采用广播具有特定意义的消息,要想灵活应用管道及FIFO,理解他们的读写规则是关键之处。

Linux进程间通信之管道通信相关推荐

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

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

  2. linux程序间管道通信,linux进程间通信——管道 详解

    管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入.常说的管道多是指无名管道, 无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别. 有名管道叫nam ...

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

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

  4. linux进程管道通信缺点,Linux 进程间通信(1) -- 管道

    进程间通信(IPC - InterProcess Communication) 通信的方式有很多: 文件, 管道, 信号, 共享内存, 消息队列, 套接字, 命名管道等等; 但是由于设计缺陷现在常用的 ...

  5. c语言程序实现进程的管道通信,C 进程间通信--命名管道通信代码实现及其原理图示...

    在将这个题目之前大家需要了解几个概念: 进程: 我们可以先看进程的定义:进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础: ...

  6. linux系统线程通信的几种方式,Linux进程间通信-线程间通信

    Linux作为一种新兴的操作系统,几乎支持所有的Unix下常用的进程间通信方法:管道.消息队列.共享内存.信号量.套接口. 1.管道 管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动 ...

  7. Linux 进程间通信:管道、共享内存、消息队列、信号量

    进程间通信 管道 共享内存 消息队列 信号量 进程间通信 https://blog.csdn.net/qq_35423154/article/details/105294963 在之前的一篇博客中讲过 ...

  8. 进程间通信之管道通信

    两个程序之间传递数据的一种简单方法是使用popen和pclose. #include <stdio.h> FILE *popen(const char *command, const ch ...

  9. linux 进程间通信 命名管道FIFO的原理与使用

    参考文章1:管道 pipe是什么?(进程通信的一种方式)(可以先大致了解管道) 参考文章2:FIFO(命名管道) FIFO常被称为命名管道,以区分管道(pipe).管道(pipe)只能用于" ...

最新文章

  1. iframe和HTML5 blob实现JS,CSS,HTML直接当前页预览
  2. php绑定变量,php动态绑定变量的用法
  3. netty系列之:channelPipeline详解
  4. conky在ubuntu xfce4下面的配置
  5. Spring学习笔记—最小化Spring XML配置
  6. BZOJ_1009_[HNOI2008]_GT考试_(动态规划+kmp+矩阵乘法优化+快速幂)
  7. Python3 爬虫实战 — 安居客武汉二手房【requests、Beautiful Soup、CSV】
  8. IOS精品源码,仿探探UIButton封装iOS提示弹框迅速引导页自定义导航栏
  9. Hadoop与Spark的集群复制
  10. node + ts读取csv文件为二维数组
  11. iNode客户端安装
  12. 微信公众平台接口测试帐号登录
  13. android 屏幕分辨率获取,Android获取屏幕分辨率的三种方法
  14. 阿里云Centos7服务器域名解析和Nginx配置
  15. 淘淘商城---8.8
  16. [论文阅读笔记]Deep Neural Networks are Easily Fooled:High Confidence Predictions for Unrecognizable Images
  17. 数据结构课程设计——通讯录管理系统
  18. 相关系数-excel-CORREL()
  19. 汇编:汇编的基本介绍
  20. Android之获取Fragment和activity的宽和高

热门文章

  1. 为别人造好车?争做头部Tier1?华为第一、立讯第二
  2. 【Unity Shader实战】卡通风格的Shader(二)
  3. 弘玑Cyclone2022产品发布会:超级自动化下的流程挖掘——弘观流程智能
  4. 5.6作业--JDBC注册功能实现
  5. 华为服务器t3200安装系统,学校云教室教学信息化建设方案-腾创TC-T3200
  6. 服务器监控与维护软件,服务器监控与维护软件
  7. 复旦学长:考完试的这段空档期应该怎么过
  8. sess.run()自我理解
  9. 告别单打独斗,美国网件Orbi多路由系统告诉你什么叫走到哪都有网
  10. linux-centos- 石头剪刀布小游戏-shell版