文章目录
- 序言
- 目的
- 进程间通信的理解
- 进程间通信的发展历史
- 管道创建
- 验证管道的大小
- 管道的4种情况
- 管道的5种特征
序言
通过该命令计算了在当前路径下一共有多少个文件夹的任务
进程虽然有独立性,但是进程并不孤僻,他们之间也会相互进行协作共同完成一件事
这个前提是他们之间的信息能传递(进程间通信)
目的
数据传输:进程将数据发送给另一个进程
资源共享:进程间共享同样的资源
通知事件:一个进程向另一个或一组进程发送信息,通知他们完成了某事(如子进程终止通知父进程)
进程控制:有些进程希望完全控制另一个进程(如Debug进程),此时控制进程就希望能够
拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变
进程间通信的理解
例如
爸妈吵架,分房睡觉了,互相不说话,相互独立.但是又要交流,所以我成为了他们交流的枢纽
爸妈就是两个进程, 我就是OS
我妈让我叫我爸吃饭
我爸说不吃顺便告诉我妈 就相当于进程间通信
进程间通信本质:让不同的进程看到同一份资源(比如爸妈吵架交流,我还没回家,他们就不相往来)
这一份资源,不能由俩进程单独提供,但是可以申请(比如,我爸可以叫我回来)
通常由OS提供
进程间通信的发展历史
一个程序被加载成进程就有了缓冲区,所以早期设置进程间通信的时候就想着复用以前的代码,所以有了进程间通信的几种方式,其中一种就是管道,还有
system V IPC, POSIX IPC
system V—进程间本地通信
POSIX – 跨网络,实现跨主机通信
管道创建
在这一步,父进程创建子进程后,子进程的files_struct也有内容指向管道文件
然后父进程关闭读端,子进程关闭写端
这样就创建了一个管道
那么,具体情况是怎样的呢?
当一个进程被创建时:
当创建子进程后,文件描述符表也会浅拷贝给子进程
而文件部分不会拷贝给子进程
所以父子进程的打印打印信息都能在屏幕上显示
这个文件页缓冲区:
就是管道
父进程向子进程发送消息
管道文件是纯内存的文件
这个文件不需要向磁盘写入,也不需要路径和文件名
所以叫做匿名管道
如何让不同的进程看到同一份资源文件管道?
创建子进程时,复用同一套代码,让父子进程读到同一个文件
总结上图就是:
代码创建管道:
#include<iostream>
#include<unistd.h>
#include<cassert>
#include<sys/types.h>
#include<sys/wait.h>
#include<cstring>
#define MAX 1024
using namespace std;
int main()
{
//第一步,建立管道
int pipefd[2] = {0};
int n = pipe(pipefd);
assert(n == 0);//debug下才存在,在release模式下这条语句会不存在,这样导致的问题是,n在后续的release中会未被使用
(void)n;//这样会导致一些编译器的报错,所以加此语句的目的是防止这样的报错发生
cout<<"pipe[0]:"<<pipefd[0]<<",pipe[1]"<<pipefd[1]<<endl;
//第二步,创建子进程
pid_t id = fork();
if(id<0)
{
perror("fork");
return 1;
}
//第三步,形成单向通信的管道,子写,父读
//父子进程关闭不需要的fd,即可形成单行通信的管道
//形成单行管道后,可以不用手动关闭,因为进程退出后会自动释放当前进程对应的文件描述符表
//所以手动关闭不关闭看情况,这边是不需要手动关闭
if(id == 0)
{
//child 子进程写入
close(pipefd[0]);
//向管道写入信息,只有写入,没有打印
int cnt = 0;
while(true)
{
// char c = 'a';
// write(pipefd[1],&c,1);
// cout<<"write :" << ++cnt <<endl;
cnt++;
char message[MAX];
snprintf(message,sizeof(message),"i'm child,my pid is %d , cnt %d",getpid(),cnt);
//snprintf,向指定的对象中写入,写入多少个字节,写入什么内容...
write(pipefd[1],message,strlen(message));
sleep(1);
if(cnt > 3) break;
}
//close(pipefd[1]);
cout << "child close w point" << endl;
exit(0);
}
//father,读取
close(pipefd[1]);
//用来从管道当中读取
char buf[MAX];
while(true)
{
ssize_t n = read(pipefd[0],buf,sizeof(buf)-1);//减一的目的是万一缓冲区读满了,可以留出空位置来给\0
if(n > 0)//读取成功,切返回读取到的字符串的长度
{
buf[n] = 0;//当做字符串
cout << "my pid:"<<getpid()<<",child says:" << buf << "to me!" << endl;//向屏幕打印信息
}
else if(n == 0)
{
cout << "child is quit, me too!" << endl;
break;
}
sleep(1);
cout << "father return val(n): " << n << endl;
break;
}
cout<<"read is closed"<<endl;
close(pipefd[0]);
sleep(5);
int status = 0;
pid_t rid = waitpid(id,&status,0);
if(rid==id)
{
cout<<"wait successful,child exit sig: "<<(status&0x7F)<<endl;//低7为该进程退出时收到的信号,次低8位退出码
}
return 0;
}
问题:
1.父进程创建子进程不是数据共享的吗?不是能直接访问吗?为什么要用这样的方式呢?
是能数据共享,能访问,但是不能访问动态的数据,管道可以实现动态数据的父子进程的相互访问,而不用管道只能访问静态的数据
2.利用管道还能实现子进程向父进程数据的写入,这时就不仅限于单独的父进程向子进程的写入数据
运行演示:
验证管道的大小
验证管道的大小:
结果:
进行换算:管道的大小为64字节
ulimit -a查看管道大小open files指可以打开的文件的个数
管道大小为8*512/1024==4,并不是64,这边的管道大小并不是实际大小
管道的4种情况
1.如果管道中没有数据了,读段必须进行等待,直到有数据为止
2.管道也有大小,写端不会一直写下去,写满后会阻塞等待,直到等到读端读取数据后(管道有空间),写端才会再继续写入数据
1,2两点对应管道的同步机制(管道特征第二特征)
3.写端关闭,读端一直读,读端会读到read的返回值为0表示读到文件的结尾
结果演示:
4.读端关闭,写端一直写,写入没有意义,OS会杀掉写端进程,进程异常(向目标进程发送13号信号),终止目标进程
管道的5种特征
1.匿名管道
在一个进程体系当中,爷孙,兄弟之间都能通过管道来进行通信,常用于父子
2.匿名管道,默认给读写端提供同步机制 这个同步机制体现在下述现象
将代码进行细节处修改
读了一部分再写
3.面向字节流-----了解现象即可
读取的时候是按照缓冲区的大小进行读取的,能读多少读多少
4.管道的生命周期是随进程的周期的
当不显示关闭文件,进程退出会有什么影响?
可以看到,管道的写端是被关闭的,可是管道也是文件
所以,文件描述符表全称进程文件描述符表是有原因
管道会跟随进程一起关闭
5.管道是单向通信的,半双工通信的一种特殊情况
半双工:
任何时候一个人说,一个人在听
全双工:
你说的时候对方也再说,你说的时候对方还在听,对方也是如此
未来的网络通信都是全双工
实现命令行管道
像这样的怎么实现呢?
具体代码,下篇见详情!!
有所帮助希望三连,谢谢支持~~~