需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。
目录
四、命名管道(open打开相同文件实现,可用于非血缘关系的进程间通信)
3.3关联/卸载共享内存(shmat/shmdt)(关联类似malloc)
一、通信的相关概念
进程之间具有独立性,进程间如果要发生通信,就需要打破这种独立性。进程间通信必定需要一块公共的区域用来作为信息的存放点,操作系统需要直接的或间接给通信进程双方提供内存空间,例如这块内存空间是文件系统提供的,那么就是管道通信,通信的本质就是让不同的进程看到同一份内存空间。
进程间通信是为了完成:
1、数据传输:一个进程需要将它的数据发送给另一个进程;
2、资源共享:多个进程之间共享相同的资源;
3、事件通知:一个进程需要向另一个或另一组进程发送消息,通知他们发送了某种事件(例如子进程终止时要通知父进程)
4、进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。
二、管道(半双工)
1、管道的概念
管道是基于文件系统的通信方式,那么就从底层的角度看一下管道通信的原理:
管道文件是内存级文件,不用访问磁盘进行文件加载,操作系统直接创建结构体对象及内核缓冲区。如上图例子,管道文件不必使用open进行打开,操作系统会创建文件结构体对象及其内核缓冲区,并将其放入父进程的文件描述符表中,父进程创建子进程后,父子进程便能基于管道这个内存级文件进行通信。
管道只能单向通信。
三、匿名管道(fork实现,用于父子及血缘关系进程间通信)
通过上方管道的概念可知,通过父进程fork创建子进程,让子进程拷贝父进程中管道文件的地址,两个进程便能看到同一个管道文件,这个管道文件是一个内存级文件,并没有名字,所以被称为匿名管道。
所以对待管道和对待文件一样,体现Linux一切皆文件的思想。
1、匿名管道的使用
#include <unistd.h>
int pipe(int pipefd[2]);//pipefd[2]是输出型参数
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
成功时返回零,错误时返回 -1,并适当地设置 errno。
pipefd[2]是输出型参数,外边搞个pipefd[2]数组传进去,系统调用pipe结束后这个数组中存放的就是读/写的fd。
子进程写入数据到管道,父进程读取管道数据代码。
#include <iostream>
#include <unistd.h>
#include <cassert>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
#include <cstdlib>
using namespace std;
//父进程读取,子进程写入
int main()
{
//第一步父进程创建管道
int fds[2];
int n=pipe(fds);
assert(n==0);
//第二步父进程fork()创建子进程
pid_t id =fork();//fork之后,父进程返回值>0,子进程返回值==0
assert(id>=0);
const char* s="我是子进程,我的pid是:";
int cnt=0;
if(id==0)
{
close(fds[0]);//子进程关闭读取的fd
//子进程的通信代码
while(true)
{
char buffer[1000];//这个缓冲区只有子进程能看到
snprintf(buffer,sizeof(buffer),"子进程第%d次向父进程发送:%s%d",++cnt,s,getpid());//向缓冲区buffer中打印
write(fds[1],buffer,strlen(buffer));//子进程将缓冲区数据写入管道
sleep(1);//每隔1秒写一次
//break;
}
close(fds[1]);//如果break跳出循环,子进程将关闭写端
exit(0);
}
close(fds[1]);//父进程关闭写入
//父进程的通信代码
while(true)
{
char buffer[1000];//这个缓冲区只有父进程能看到
//如果管道中没有数据,读取端再读,默认会阻塞当前读取的进程
ssize_t s=read(fds[0],buffer,sizeof(buffer)-1);
if(s>0)//s是read读取成功字节数
{
buffer[s]='\0';
cout << "父进程的pid是:"<<getpid()<<" "<<buffer<<endl;
}
else if(s==0)//如果子进程关闭写端,父进程将会输出“读完了”
{
//读到文件结尾
cout<<"读完了"<<endl;
break;
}
}
n=waitpid(id,nullptr,0);
assert(n==id);
close(fds[0]);//父进程读取fd用完记得关一下
return 0;
}
2、匿名管道的读写情况
1、如果管道中没有数据,读取端进程再进行读取,会阻塞当前正在读取的进程;
2、如果写入端写满了,再写就会对该进程进行阻塞,需要等待对方对管道内数据进行读取;
3、如果写入进程关闭了写入fd,读取端将管道内的数据读完后read的返回值为0,和谐退出;
4、如果读关闭,操作系统会给写端发送13号信号SIGPIPE,终止写端。
3、管道的特征
1、只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道;
2、管道提供流式服务 ;
3、进程退出,管道释放,所以管道的生命周期随进程
4、内核会对管道操作进行同步与互斥
5、管道是半双工。需要双方通信时,需要建立起两个管道