进程间通信
文章目录
- 进程间通信
-
- 1.1进程间通信的概念
- 1.2进程间通信的目的
- 1.3进程间通信的方式
- 2.1管道的概念
- 2.2匿名管道的介绍
- 2.2.1管道的5种特征4种情况
-
- 5种特征:
- 4种情况:
- 2.3管道读写的规则说明
- 4种情况的分析:
-
- 第一种:(子或父)不write,(父或子)一直read,read阻塞
- 第二种:(子或父) 不read ,(父或子)一直write,write阻塞
- 第三种:.write写完后关闭,read返回值为0
- 第四种:read关闭,一直写,写方会被操作系统杀掉,写入无意义
- 注意的点(重点):
1.1进程间通信的概念 进程间通信就是进程之间进行信息的交换和传播
很重要的一句话:进程间通信的本质是让不同的进程看到同一根资源
文章图片
1.2进程间通信的目的
- 数据传输:一个进程需要将自己的数据发生给其他的进程
- 资源共享:多个进程之间共用相同的资源
- 通知事件:一个进程需要向拎一个进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
- 进程控制:有些进程希望完全控制另一个进程的执行(如debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
- 管道(本文主讲)
- 匿名管道pipe
- 命名管道
- System V进程间通信(本文不涉及)
- System V消息队列
- System V共享内存
- System V信号量
- POSIX进程间通信(本文不涉及)
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
- 通常把一个进程连接到另一个进程的一个数据流称为一个“管道”
2.2.1管道的5种特征4种情况 5种特征:
- 1.管道内部已经自动提供了同步与互斥机制(读和写只能一个进行另一个等待,不能同时进行)
- 2.如果打开文件的进程退出了,文件也会被释放掉(所有打开了某一文件的进程全部退出了,该文件的资源才会彻底释放掉)
- 3.管道是提供流式服务的(本文只是提及,知道有这么个东西即可,后续的博客应该会讲)
- 4.管道是半双工通信的(管道只能是单向通信的,例如父进程在进行写入数据到匿名管道,那么子进程就不能进行读取管道信息,必须等父进程写完了才可以进行读操作,也就是父子进程只能是其中一个对管道进行读或写操作,不能父子进程同时对管道进行读或者写操作!!!)
- 5.匿名管道适合具有血缘关系的进程进行进程间通信,常用于父子
- 1.(子或父)不write,(父或子)一直read,read阻塞
- 2.(子或父) 不read ,(父或子)一直write,write阻塞
- 3.write写完后关闭,read返回值为0
- read关闭,一直写,写方会被操作系统杀掉,写入无意义
文章图片
通过pipe(int fd[2])创建了管道后,再通过fork()创建子进程
错误读写:
文章图片
正确读写:
文章图片
父进程读,子进程写的相关代码(本文主要代码,举例也是这在这段代码的基础上变换来的):
#include
#include
#include
#include
#include
#includeint 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
#include
#include
#include
#include
#include
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
#include
#include
#include
#include
#includeint 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
#include
#include
#include
#include
#includeint 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
#include
#include
#include
#include
#includeint 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.管道提供流式服务且管道是半双工通信
【Linux|进程间通信之匿名管道】7.匿名管道适合具有血缘关系的进程进行进程间通信,常用于父子
推荐阅读
- Linux|管道实现进程间通信之命名管道
- ubuntu系统相关|Ubuntu截图工具-flameshot
- 网络|苹果市值迈入两万亿,库克终于走出乔布斯的阴影()
- Python|Python从入门到实战(四)——函数
- 机器学习|Spark ALS 协同过滤算法实践
- 大数据|新型存算一体芯片诞生,利好人工智能应用~
- 大数据|C语言每日题目练习与巩固
- JVM|11、摸清JVM对象分布
- java开发工程师|centos7安装打印机 cups页面管理 java程序驱动打印程序