当前位置:网站首页>进程间通信之匿名管道
进程间通信之匿名管道
2022-06-30 04:09:00 【*insist】
进程间通信
文章目录
1.1进程间通信的概念
进程间通信就是进程之间进行信息的交换和传播
很重要的一句话:进程间通信的本质是让不同的进程看到同一根资源
1.2进程间通信的目的
- 数据传输:一个进程需要将自己的数据发生给其他的进程
- 资源共享:多个进程之间共用相同的资源
- 通知事件:一个进程需要向拎一个进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
- 进程控制:有些进程希望完全控制另一个进程的执行(如debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
1.3进程间通信的方式
- 管道(本文主讲)
- 匿名管道pipe
- 命名管道
- System V进程间通信(本文不涉及)
- System V消息队列
- System V共享内存
- System V信号量
- POSIX进程间通信(本文不涉及)
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
2.1管道的概念
管道是一种古老的进程间通信的形式
- 通常把一个进程连接到另一个进程的一个数据流称为一个“管道”
2.2匿名管道的介绍
匿名管道就是没有名字的管道,由函数pipe(int fd[2]) 创建,常用于具有亲缘关系的进程间进行通信,作为两个进程共享的资源,从而实现两个进程间的信息交换达到通信的目的。
2.2.1管道的5种特征4种情况
5种特征:
1.管道内部已经自动提供了同步与互斥机制(读和写只能一个进行另一个等待,不能同时进行)
2.如果打开文件的进程退出了,文件也会被释放掉(所有打开了某一文件的进程全部退出了,该文件的资源才会彻底释放掉)
3.管道是提供流式服务的(本文只是提及,知道有这么个东西即可,后续的博客应该会讲)
4.管道是半双工通信的(管道只能是单向通信的,例如父进程在进行写入数据到匿名管道,那么子进程就不能进行读取管道信息,必须等父进程写完了才可以进行读操作,也就是父子进程只能是其中一个对管道进行读或写操作,不能父子进程同时对管道进行读或者写操作!!!)
5.匿名管道适合具有血缘关系的进程进行进程间通信,常用于父子
4种情况:
- 1.(子或父)不write,(父或子)一直read,read阻塞
- 2.(子或父) 不read ,(父或子)一直write,write阻塞
- 3.write写完后关闭,read返回值为0
- read关闭,一直写,写方会被操作系统杀掉,写入无意义
2.3管道读写的规则说明
(fd_arrray[3] fd_array[4] 不理解的第三张图有解释)

通过pipe(int fd[2])创建了管道后,再通过fork()创建子进程
错误读写:
正确读写:

父进程读,子进程写的相关代码(本文主要代码,举例也是这在这段代码的基础上变换来的):
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main()
{
int fd[2];
if(pipe(fd)<0)
{
//创建管道
perror("pipe!");//这个函数自带换行
return 1;
}
//printf("fd[0]\n",fd[0]);
//printf("fd[1]\n",fd1]);
//创建子进程
pid_t id = fork();
if(id==0)
{
//child
//子进程要做的是向管道里写数据 要把读端关闭 最后写完了再把写端关闭
close(fd[0]);
int count=10;
const char* str="hello father i am your child!\n";
while(count)
{
write(fd[1],str,strlen(str));
// printf("%s",str);
count--;
sleep(1);
}
close(fd[1]);
exit(1);
}
else if(id>0)
{
//father
close(fd[1]);//父进程关闭写端,只进行读
char buff[64];
while(1)
{
int ret=read(fd[0],buff,sizeof(buff));
if(ret>0)
{
buff[ret]='\0';
printf("child send to father's message is:%s\n",buff);
// close(fd[0]);
// break;
//这里不能直接就break 不然就是只读了一次 这里写了10次要一直读 直到读到了文件结尾才停止 才能读完
}
else if(ret==0)
{
printf("read file of end!\n");
break;
}
else
{
perror("read");
break;
}
}
}
else
{
//error
perror("fork!");
return 1;
}
int status=0;
int ret=waitpid(id,&status,0);//阻塞式等待
if(ret>0)
{
printf("child is quit! single is:%d",status&0x7F);
}
return 0;
}
4种情况的分析:
第一种:(子或父)不write,(父或子)一直read,read阻塞

代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret<0)
{
perror("pipe");
return -1;
}
pid_t id=fork();//创建子进程
if(id==0)
{
//child
close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
const char* str="i\n";
int count=5;
while(count)
{
if(count>=2)//count 大于等于2就写 否则就休眠
{
write(fd[1],str,strlen(str));
count--;
sleep(1);
}
else{
sleep(1000);//子进程写两次就不写了 进行休眠 但是父进程一直在读 这样就会形成读堵塞
}
}
//一直写 当不再打印count时说明管道的缓冲区写满了,此时的count就是管道的容量
}
else if(id>0)
{
//father
close(fd[1]);//关闭写端
// sleep(1000);
char arr[1000];
while(1)
{
int ret=read(fd[0],arr,sizeof(arr));
if(ret>0)//读到了内容
{
arr[ret]='\0';
printf("%s",arr);
}
else if(ret==0)
{
//读到了文件结尾
printf("read end of file!\n");
break;
}
else
{
//read error
// perror("read");
printf("read error!\n");
break;
}
}
}
else
{
//error
perror("fork!");
}
int status=0;
int s=waitpid(id,&status,0);//阻塞式等待
if(s>0)
{
printf("child quit!\n");
}
else
{
printf("wait error!");
}
printf("quit singal is:%d\n",ret&0x7F);
return 0;
}
第二种:(子或父) 不read ,(父或子)一直write,write阻塞

代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret<0)
{
perror("pipe");
return -1;
}
pid_t id=fork();//创建子进程
if(id==0)
{
//child
close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
const char* str="i";//每次写一个字节
int count=0;
while(1)
{
write(fd[1],str,strlen(str));
count++;
printf("%d\n",count);//写一次打印一次count
}
//一直写 当不再打印count时说明管道的缓冲区写满了,此时的count就是管道的容量
}
else if(id>0)
{
//father
close(fd[1]);//关闭写端
sleep(1000);//直接让父进程休眠,就不会读取管道里的数据,让子进程一直往里写就行了
char arr[1000];
while(1)
{
int ret=read(fd[1],arr,sizeof(arr));
while(1)
{
if(ret>0)//读到了内容
{
arr[ret]='\0';
printf("%s",arr);
}
else if(ret==0)
{
//读到了文件结尾
printf("read end of file!\n");
break;
}
else
{
//read error
perror("read");
break;
}
}
}
}
else
{
//error
perror("fork!");
}
//等待子进程退出
int status=0;
int s=waitpid(id,&status,0);//阻塞式等待
if(s>0)
{
printf("child quit!\n");
}
else
{
printf("wait error!");
}
printf("quit singal is:%d\n",ret&0x7F);
return 0;
}

运行结果-> 不再打印count 说明write阻塞了 测得管道缓冲区大小为65536个字节
结合文档 本机是3.几的版本 所以是65536个字节 得到证实

第三种:.write写完后关闭,read返回值为0
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret<0)
{
perror("pipe");
return -1;
}
pid_t id=fork();//创建子进程
if(id==0)
{
//child
close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
const char* str="i\n";//每次写一个字节
int count=5;
while(count)
{
write(fd[1],str,strlen(str));
count--;
sleep(1);
}
//重点
close(fd[1]);//写完关闭 写端 父进程读的时候就会读到文件结尾 返回值为0
exit(0);//子进程写完退出
}
else if(id>0)
{
//father
close(fd[1]);//关闭写端
// sleep(1000);
char arr[1000];
while(1)
{
int ret=read(fd[0],arr,sizeof(arr));
if(ret>0)//读到了内容
{
arr[ret]='\0';
printf("%s",arr);
}
else if(ret==0)
{
//读到了文件结尾
printf("read end of file!\n");
break;
}
else
{
//read error
// perror("read");
printf("read error!\n");
break;
}
}
int status=0;
int s=waitpid(id,&status,0);//阻塞式等待
if(s>=0)
{
printf("child quit!\n");
}
else
{
printf("wait error! s:%d\n",s);
}
printf("quit singal is:%d\n",status&0x7F);
return 0;
}
else
{
//error
perror("fork!");
}
return 0;
}
运行结果:写端写完关闭 读端再读 读到文件结尾 read返回值为0

第四种:read关闭,一直写,写方会被操作系统杀掉,写入无意义
代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<string.h>
int main()
{
int fd[2];
int ret = pipe(fd);
if(ret<0)
{
perror("pipe");
return -1;
}
pid_t id=fork();//创建子进程
if(id==0)
{
//child
close(fd[0]);//子进程关闭读端 使得其只能往管道中进行写入
const char* str="i\n";//每次写一个字节
while(1)
{
write(fd[1],str,strlen(str));
sleep(1);
}
close(fd[1]);//写完关闭 写端 父进程读的时候就会读到文件结尾 返回值为0
exit(0);
}
else if(id>0)
{
//father
close(fd[1]);//关闭写端
close(fd[0]);//父进程直接关闭读端 不读取管道信息 子进程还在一直写 没有意义 操作系统会将其杀掉
// sleep(1000);
char arr[1000];
while(1)
{
int ret=read(fd[0],arr,sizeof(arr));
if(ret>0)//读到了内容
{
arr[ret]='\0';
printf("%s",arr);
}
else if(ret==0)
{
//读到了文件结尾
printf("read end of file!\n");
break;
}
else
{
//read error
// perror("read");
printf("read error!\n");
break;
}
}
int status=0;
int s=waitpid(id,&status,0);//阻塞式等待
if(s>=0)
{
printf("child quit!\n");
}
else
{
printf("wait error! s:%d\n",s);
}
printf("quit singal is:%d\n",status&0x7F);
return 0;
}
else
{
//error
perror("fork!");
}
return 0;
}
运行结果:子进程被操作系统杀掉 因为父进程不会读 子进程写的没有意义 且子进程退出信号为13 SINPIPE

注意的点(重点):
1.pipe()创建出来的管道是要传一个元素为二的数组的 fd[0] f[1]分别代表读和写端
2.既然创建子进程后要把父子进程的读或写端关掉一个,那么为什么不开始创建的时候父进程的读写端都是打开的呢?
实质上是为了让子进程继承下来,后面关掉一端,是因为管道只能是单向通信的
3.管道是自带同步与互斥机制的,这样就使得父子进程不能同时使用一块相同资源,子进程写的时候父进程不能读,只能等待子进程写完了才可以去读,即读写不能同时进行,否则会使得数据错乱。在多执行流下(父子)看到的同一份资源就是叫临界资源
4如果写端关闭,读端就会读到文件的结尾,返回0,代表文件结束
5.文件的生命周期随着进程的结束而结束,也就是说打开文件的进程退出了,文件也就会被释放掉,这里可能是多个进程都打开了某个文件,则所有打开此文件的进程都退出了此文件资源才会完全释放掉
6.管道提供流式服务且管道是半双工通信
7.匿名管道适合具有血缘关系的进程进行进程间通信,常用于父子
边栏推荐
- 云原生——Web实时通信技术之Websocket
- Idea grey screen problem
- If you encounter problems when using spark for the first time, please ask for help
- Radiant energy, irradiance and radiance
- 数据链路层详解
- Educoder group purchase suspension box page production
- 【云原生】AI云开发平台——AI Model Foundry介绍(开发者可免费体验AI训练模型)
- Ananagrams(UVA156)
- Concatenation of Languages(UVA10887)
- [cloud native] AI cloud development platform - Introduction to AI model foundry (developers can experience AI training model for free)
猜你喜欢

An error occurs when sqlyog imports the database. Please help solve it!

接口测试--如何分析一个接口?

DBT product initial experience

el-upload上传文件(手动上传,自动上传,上传进度)

SQLyog导入数据库时报错,求帮解决!

ThingsBoard教程(二三):在规则链中计算二个设备的温度差

(Reprinted) an article will take you to understand the reproducing kernel Hilbert space (RKHS) and various spaces

Geometric objects in shapely

Graduation project EMS office management system (b/s structure) +j2ee+sqlserver8.0

声网自研传输层协议 AUT 的落地实践丨Dev for Dev 专栏
随机推荐
lego_loam 代码阅读与总结
如何利用FME 创建自己的功能软件
Linear interpolation of spectral response function
Maya Calendar(POJ1008)
(03). Net Maui actual combat basic control
网络层详解
(04). Net Maui actual MVVM
[punch in - Blue Bridge Cup] day 4--------- split ('') cannot be used. There is a space after the last number of test cases. Split ()
The jupyter notebook kernel hangs up frequently and needs to be restarted
How to use FME to create your own functional software
Solutions for project paths
Unity échappe à l'entrée de caractères lors de l'entrée de chaînes dans l'éditeur
About manipulator on Intelligent Vision Group
el-upload上傳文件(手動上傳,自動上傳,上傳進度)
Radiant energy, irradiance and radiance
Smart use of bitmap to achieve 100 million level massive data statistics
使用IDEAL连接数据库,运行出来了 结果显示一些警告,这部分怎么处理
Sql语句遇到的错误,求解
Node-RED系列(二八):基于OPC UA节点与西门子PLC进行通讯
Error encountered in SQL statement, solve