每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程A把数据从用户空间拷到内核缓冲区,进程B再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

不同进程间的通信本质:进程之间可以看到一份公共资源;而提供这份资源的形式或者提供者不同,造成了通信方式不同,而 pipe就是提供这份公共资源的形式的一种。

2.匿名管道

2.1管道的创建

管道是由调用pipe函数来创建

#include <unistd.h>
int pipe (int fd[2]);
//返回:成功返回0,出错返回-1

  fd参数返回两个文件描述符,fd[0]指向管道的读端,fd[1]指向管道的写端。fd[1]的输出是fd[0]的输入。

2.2管道如何实现进程间的通信

(1)父进程创建管道,得到两个件描述符指向管道的两端

(2)父进程fork出子进程,子进程也有两个文件描述符指向同管道。

(3)父进程关闭fd[0],子进程关闭fd[1],即子进程关闭管道读端,父进程关闭管道写端(因为管道只支持单向通信)。子进程可以往管道中写,父进程可以从管道中读,管道是由环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

2.3如和用代码实现管道通信

2.3如和用代码实现管道通信

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
int main()
{
int fd[2];
int ret=pipe(fd);
if(ret==-1)
{
perror("pipe error\n");
return -1;
}
pid_t id=fork();
if(id==0)
{
int i=0;
close(fd[0]);
char* child="I am child!";
while(i<5)
{
write(fd[1],child,strlen(child)+1);
sleep(2);
i++;
}
}
else if(id>0)
{
close(fd[1]);
char msg[100];
int j=0;
while(j<5)
{
memset(msg,'\0',sizeof(msg));
ssize_t s=read(fd[0],msg,sizeof(msg));
if(s>0)
{
msg[s-1]='\0';
}
printf("%s\n",msg);
j++;
}
}
else
{
perror("fork error\n");
return -1;
}
return 0;
}

  

运行结果:每隔2秒打印一次I am child! 并且打印了五次。

2.4管道读取数据的四种的情况

(1)读端不读(fd[0]未关闭),写端一直写

(2)写端不写(fd[1]未关闭),但是读端一直读

(3)读端一直读,且fd[0]保持打开,而写端写了一部分数据不写了,并且关闭fd[1]。

如果一个管道读端一直在读数据,而管道写端的引⽤计数⼤于0决定管道是否会堵塞,引用计数大于0,只读不写会导致管道堵塞。

(4)读端读了一部分数据,不读了且关闭fd[0],写端一直在写且f[1]还保持打开状态。

#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
int main()
{
int fd[2];
int ret=pipe(fd);
if(ret==-1)
{
perror("pipe error\n");
return -1;
}
pid_t id=fork();
if(id==0)
{
int i=0;
close(fd[0]);
char *child="I am child!";
while(i<10)
{
write(fd[1],child,strlen(child)+1);
sleep(2);
i++;
}
}
else if(id>0)
{
close(fd[1]);
char msg[100];
int status=0;
int j=0;
while(j<5)
{
memset(msg,'\0',sizeof(msg));
ssize_t s=read(fd[0],msg,sizeof(msg));
if(s>0)
{
msg[s-1]='\0';
}
printf("%s %d\n",msg,j);
j++;
}
//写方还在继续,而读方已经关闭它的读端
close(fd[0]);
pid_t ret=waitpid(id,&status,0);
printf("exitsingle(%d),exit(%d)\n",status&0xff,(status>>8)&0xff);
//低八位存放该子进程退出时是否收到信号
//此低八位子进程正常退出时,退出码是多少
}
else
{
perror("fork error\n");
return -1;
}
return 0;
}

  运行结果:

使用kill -l 查看13号信号,可以知道13号信号代表SIGPIPE。

总结:
如果一个管道的写端一直在写,而读端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只写不读再次调用write会导致管道堵塞;
如果一个管道的读端一直在读,而写端的引⽤计数是否⼤于0决定管道是否会堵塞,引用计数大于0,只读不写再次调用read会导致管道堵塞;
而当他们的引用计数等于0时,只写不读会导致写端的进程收到一个SIGPIPE信号,导致进程终止,只写不读会导致read返回0,就像读到件末尾样。

2.5管道特点

1.管道只允许具有血缘关系的进程间通信,如父子进程间的通信。

2.管道只允许单向通信。

3.管道内部保证同步机制,从而保证访问数据的一致性。

4.面向字节流

5.管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失管道也消失。

2.6管道容量大小

测试管道容量大小只需要将写端一直写,读端不读且不关闭fd[0],即可。 
测试代码:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if (ret == -1)
{
perror("pipe error\n");
return -1;
}
pid_t id = fork();
if (id == 0)
{//child
int i = 0;
close(fd[0]);
char *child = "I am child!";
while (++i)
{
printf("pipe capacity: %d\n", i*(strlen(child) + 1));
//printf要写在write前面否则会因为write写满了而阻塞就不会进行下面的代码了,会使得输出计算少一次
write(fd[1], child, strlen(child) + 1); }
close(fd[1]);
}
else if (id>0)
{//father
close(fd[1]);//父进程的读端不能关闭,如果关闭了子进程写端会因为异常而退出
waitpid(id, NULL, 0);
}
else
{//error
perror("fork error\n");
return -1;
}
return 0;
}

  

  可以看到写到65520之后管道堵塞了,而65536即为64K大小即为管道的容量

原理是:我们写端每次写入的数据大小是13,统计我们可以进行多少次写入,写入次数*13就是管道容量,因为65533+13=65546>65536所以就不能继续输入了,有因为内存对齐问题,所以我们可以知道容量一定是64k

Linux下进程间通信方式——pipe(管道)的更多相关文章

  1. Linux下进程间通信方式——共享内存

    1.什么是共享内存? 共享内存就是允许两个或多个进程共享一定的存储区.就如同 malloc() 函数向不同进程返回了指向同一个物理内存区域的指针.当一个进程改变了这块地址中的内容的时候,其它进程都会察 ...

  2. Linux下进程间通信方式——使用消息队列

    一.什么是消息队列 消息队列提供了一种从一个进程向另一个进程发送一个数据块的方法.  每个数据块都被认为含有一个类型,接收进程可以独立地接收含有不同类型的数据结构.我们可以通过发送消息来避免命名管道的 ...

  3. Linux下进程间通信方式——信号量(Semaphore)

    1.信号量 信号量本质上是一个计数器(不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据 ...

  4. Linux环境进程间通信(一):管道及命名管道

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  5. Linux下进程间通信的六种机制详解

    linux下进程间通信的几种主要手段:        1.管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具 ...

  6. 浅析Linux下进程间通信:共享内存

    浅析Linux下进程间通信:共享内存 共享内存允许两个或多个进程共享一给定的存储区.因为数据不需要在客户进程和服务器进程之间复制,所以它是最快的一种IPC.使用共享内存要注意的是,多个进程之间对一给定 ...

  7. Linux下进程间通信--共享内存:最快的进程间通信方式

    共享内存: 一.概念: 共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式.两个不同进程A.B共享内存的意思是,同一块物理内存被映射到进程A.B各自的进程地址空间. 进程A可以即时看到进程B ...

  8. Linux下进程通信之管道

    每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把 ...

  9. linux下进程间通信

    信号 信号是进程间相互传递消息的一种方法,只是用来通知某进程发生了什么事件,并不给进程传递任何数据. #include <sys/types.h> #include <unistd. ...

随机推荐

  1. Go - chan 通道

    概述 原来分享的基础语法的时候,还未分享过 chan 通道,这次把它补上. chan 可以理解为队列,遵循先进先出的规则. 在说 chan 之前,咱们先说一下 go 关键字. 在 go 关键字后面加一 ...

  2. Navicat Premium 12 安装与激活

    一.Navicat Premium 12下载 官方下载地址:https://www.navicat.com.cn/download/navicat-premium 百度云盘:https://pan.b ...

  3. RocketMQ 使用情况梳理

    个人梳理有限:欢迎大家 丰富此文档 2018年 12 月 RocketMQ 版本  不适用于 新版关系请勿参考目前规划原则:          topic 创建基于业务  消费者基于模块 多对多关系 ...

  4. MongoDB学习笔记(五)

    MongoDB 查看执行计划 MongoDB 中的 explain() 函数可以帮助我们查看查询相关的信息,这有助于我们快速查找到搜索瓶颈进而解决它,本文我们就来看看 explain() 的一些用法及 ...

  5. Java程序使用Alpine Linux报错java.lang.NoClassDefFoundError: Could not initialize class org.xerial.snappy.Snappy解决

    报错内容 Caused by: java.lang.UnsatisfiedLinkError: /tmp/snappy-1.1.7-4a4b576a-c34c-481e-b6ac-9b4abacb11 ...

  6. Web应急:搜索引擎劫持

    当你直接打开网址访问网站,是正常的,可是当你在搜索引擎结果页中打开网站时,会跳转到一些其他网站,比如博彩,虚假广告,淘宝搜索页面等.是的,你可能了遇到搜索引擎劫持. 现象描述 从搜索引擎来的流量自动跳 ...

  7. pip 命令安装 rdbtools

    命令 pip install tdbtools 如果出现类似如下错误  Could not fetch URL https://pypi.org/simple/redis/ 说明morning的pip ...

  8. EF启程--概念理解(数据库连接)

    简介:Entity Framework 是一种支持 .NET 开发人员使用 .NET 对象处理数据库的对象关系映射程序 (O/RM). 它不要求提供开发人员通常需要编写的大部分数据访问代码. 其中有E ...

  9. K8S CoreDNS部署失败,发现的一个问题

    K8S CoreDNS部署失败,查看错误日志,提示如下 root >> kubectl get all --all-namespaces -o wide root >> kub ...

  10. Delphi - 采用第三方控件TMS、SPComm开发串口调试助手

    第三方控件TMS.SPComm的下载与安装 盒子上可搜索关键字进行下载,TMS是.dpk文件,SPComm.pas文件: 安装方法自行百度,不做赘述. 通过TMS控件进行界面布局 界面预览: Delp ...