【Linux】进程间通信(一)---- 匿名管道

一.序

1什么是进程间通信

进程间通信
通信我们大致知道是啥,就是互相传递信息
那进程间通信,就是进程间相互传递信息了

我们知道信息就是就是数据

我们都知道进程具有独立性
所以我们想要让进程中的一个,将信息直接传递给另一个进程
虽然从理论上讲确实可以,但这样无疑是破坏了进程的独立性

所以操作系统会给在内存中开辟一个公共空间
让这两个进程来进行通信

但是我们想想
进程访问这个空间,进行通信,本质就是访问操作系统
进程对于系统来说就是用户,因为进程是用户来控制的

我们曾经多次提到:操作系统是不会信任用户的
所以操作系统从开辟公共内存到进程的相互通信,都是通过调用系统自己设计的接口来进行的

所以:
一般操作系统会有一个独立的通信模块,隶属于文件系统,叫做IPC通信模块

2.进程间通信的标准

我们知道在计算机行业中,**标准是很重要的,**就像5G的行业标准是华为制定的。

所以为了让不同计算机的不同操作系统的不同文件系统,都能进行通信,所以进程间的通信标准是很重要的。
进程间通信的定制标准system Vposix

3.为什么需要进程通信

1.基本数据
2.发送命令
3.某种协同
4.通知

基于这几种需求,所以创造出了进程通信

其实以前进程间是不会进行通信的
但是人们发现进程通信还挺有必要的
所以进程间通信的产生是历史的必然

二.匿名管道

这个匿名管道就是进程间通信的一种方式,但是也有局限

现在先为大伙带来原理,再带大家来使用

1.原理

这里就得用到博主曾经在FD的图了
在这里插入图片描述
这个是以前在讲文件FD时用到的图

那如果我们这个时候fork一个子进程会怎么样呢

我们知道子进程fork的时候,子进程会获得与父进程相同的虚拟地址空间的副本,使子进程拥有与父进程相同的代码、数据和堆栈。

这个时候看看上面的图:
我们知道这个struct taskstruct files_struct

所以他们也会被子进程进行复制

在这里插入图片描述
我们知道左边的部分是这样被子进程复制的

那么问题来了,右边的打开的文件需不需要被复制呢

在这里插入图片描述

这个答案肯定是否定的,因为右边两个被子进程复制走
因为他们都是父进程的一部分,但是打开的文件就不一样了
因为打开的文件和进程是平级的,并不是与进程的一部分

所以是这样的
在这里插入图片描述

所以,利用这个特性,我们就能来创建我们的匿名管道了

在这里插入图片描述

我们打开一个buffer缓冲区(实际上就是一个内存级文件)

这个时候如果fork一下呢?

在这里插入图片描述

就会变成这样

这个时候其实我们就能发现了,这两个进程已经能进行双向通信了

但是看看我们这个叫啥
匿名管道,管道两个字就注定了,它只进不出

因为如果子进程和父进程都同时进行读和写的话
谁能保证父进程写完,会被父进程读走呢?
所以为了确保通信的稳定性和便于实现性
所以这里就要:
关掉父进程的读或者子进程的写
或者关掉父进程的写或者子进程的读

让它变成单向通信。

在这里插入图片描述
在这里插入图片描述

这里就是匿名管道的原理了

2.使用

这里我们已经把管道的原理给讲明白了
接下来就是使用了

之前提过:操作系统是不会信任用户的
所以操作系统从开辟公共内存到进程的相互通信,都是通过调用系统自己设计的接口来进行的

所以这里就要解释接口了

int pipe(int pipefd[2]);

在这里插入图片描述

pipe是起这个作用的

接下来就带大伙来试一下匿名管道通信

#include<unistd.h>
#include<iostream>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cerrno>
using namespace std;
void child_write(int pipefd[2])
{
    const char * buffer="my dad";
    int cnt=2;
    while(cnt--)
    {
       write(pipefd[1],buffer,sizeof(buffer));
       cout<<"write success"<<endl;
    }
    

}
void father_read(int pipefd[2])
{
    char buffer[1024];
    while (true)
    {
        ssize_t bytesRead = read(pipefd[0], buffer, sizeof(buffer) - 1); // 留出一个字节存放字符串结束符
        if (bytesRead == 0)
            break;
        buffer[bytesRead] = '\0'; // 手动添加字符串结束符
        cout << buffer << endl;
    }
}
int main()
{
    int pipefd[2];
    if(pipe(pipefd)==-1)
    {
        perror("pipe失败");
        return 0;
    }
    pid_t id=fork();
    if (id < 0) 
    {
    perror("fork");
    return 1;
    } 
    // 子进程
    else if(id == 0) 
    {
       //这里就实现子进程写,父进程读把
        close(pipefd[0]);
        //子进程读
        child_write(pipefd);
        //写完,关闭通道
        close(pipefd[1]);
    } 
    // 父进程
    else 
    {
        close(pipefd[1]);
        //父进程写
        father_read(pipefd);
    }


        //读完,关闭通道
        close(pipefd[0]);
        
        //回收子进程
        int status;
        if(wait(&status)>0)
        cout<<"wait success\n"<<endl;

}

在这里插入图片描述

这样就完成了通信

3.四种情况

管道的四种情况:
1.读写端正常,管道如果为空,读端就要阻塞
管道没有接受到数据,读端就会一直等待数据

2.读写端正常,管道如果满,写端就要阻塞
管道满了,读端没有读数据,数据就堵在管道了,写端无法继续写

3.写端退出,读端正常,读端读到0,不会阻塞
这是因为读端正常,写端没数据了,这个时候代表是正常读写完成。

4.读端退出,写端正常,会把写端关闭
因为操作系统不让做一些浪费性能的事情,已经没有人读取你的数据了,那你就没有继续写的必要了,系统会自己把写端关闭

4.五个特点

匿名管道的五个特点;

1.管道本质是内存级文件,寿命周期是进程
这个是肯定的,管道本来就是为了通信存在的,父子进程通信完毕,自然也就关闭了

2.管道只能单向通信
这个最前面我们也提到过

3.具有血缘关系(常见的是父子)的进行进程间通信
因为我们利用的就是父子进程fork后复制files struct的原理
其实爷孙进程也可以用,只不过常见的是用在父子而已

4.管道是面向字节流的
这个还不太好解释,之后的博客会有解释

5.父子进程会进程协同,同步与互斥的
这个也不太好解释,之后博客也会进行解释

  • 13
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

想学c啊啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值