Linux——匿名管道

时间:2024-04-26 07:33:33

为什么要有进程间通信?

在操作系统中,进程是独立运行的程序,多个进程之间要想互相协作完成任务,就需要进程间通信。

什么是进程间通信?

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程间共享同样的资源
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知他们发送了某些事情,后者发送任务让他们执行
  • 进程控制:有些进程希望完全控制另一个进程的执行,此时进程希望能够拦截另一个进程的所有陷入和异常,并且能够及时通知它的状态改变。

进程间通信的目的

为了实现资源的共享,协作和同步,从而提高系统的效率和可靠性。

进程间通信的种类

1.管道

  • 匿名管道
  • 命名管道

2.System V

  • 消息队列
  • 共享内存
  • 信号量

3.POSIX V

  • 消息队列
  • 共享内存
  • 信号量
  • 互斥量
  • 条件变量
  • 读写锁

管道的特点

  1. 管道是用来进行某种血缘关系的进程间通信的一种手段 ——一般用于父子进程
  2. 管道具有让进程间协同、提供了访问控制。(访问控制就是当一个文件里面没有数据的时候,另一方不会去读,只会阻塞等待。当文件满的时候,不能再写入了。)
  3. 管道提供了面向流式的通信服务——面向字节流
  4. 管道是基于文件的,声明周期随进程
  5. 管道是单向通信的。

如何看到同一份资源?

进程间通信的本质是实现资源的共享,那么最重要的一点就是如何看到同一份资源。
先聊一聊匿名管道,匿名管道是通过父子进程后,写时拷贝后,看到的同一份资源,然后让父进程关闭读端,子进程关闭写端,这样就可以让两个进程看到同一份资源了。
以前说过,每一个进程都有一个PCB,而每个PCB结构体中都有一个指针指向了struct file*struct,这个结构体里面就有文件描述符表,每一个文件描述表中的指针指向了每个文件。

在这里插入图片描述

所以父子进程通过fork后,就看到了同一份资源,如下图所示,又因为管道是单向的,关闭对应的读写端,就实现了通信。

在这里插入图片描述

匿名管道接口

  • pipe
    在这里插入图片描述

成功返回0,失败返回-1,错误码被设置。pipefd是输出型参数,期望调用它,可以得到被打开的文件的fd。

实验

#include <iostream>
#include <unistd.h>
#include <assert.h>
#include <sys/types.h>
#include <stdlib.h>
#include <cstring>
#include <wait.h>
using namespace std;

char buffer[1024*8];

int main()
{
    //创建管道
    int pipefd[2] = {0};
    int ret = pipe(pipefd);
    assert(ret != -1);
    (void)ret;

    pid_t id = fork();
    assert(id != -1);
    if(id == 0)
    {
        //child
        //子进程去读,关闭写端
        close(pipefd[1]);
        // char buffer[1024*8];
        while(true)
        {
            //s == 0 是读到了文件末尾
            // s > 0 说明在有数据,还在读取
            ssize_t s = read(pipefd[0],buffer,sizeof(buffer));
            if(s > 0)
            {
                buffer[s] = 0;
                cout << "child get a message[" << getpid() << "] Father# " << buffer << endl;
            }
            else if(s == 0)
            {
                cout << "write quit(father),me quit!" << endl;
                exit(2);
            }
        }
    }

    //father
    //关闭读端
    close(pipefd[0]);
    string message = "我是父进程,我正在给你发信息";
    int count = 0;
    // char send_buffer[1024*8];
    while(true)
    {
        snprintf(buffer,sizeof(buffer),"%s[%d] : %d",message.c_str(),getpid(),count++);
        //向send_buffer中写入
        write(pipefd[1],buffer,strlen(buffer));
        
        sleep(1);

        //cout << count << endl;
        if(count == 5)
        {
            cout << "writer quit(father)" << endl;
            break;
        }
    }

    close(pipefd[1]);
    pid_t n = waitpid(id,nullptr,0);
    cout << "id : " << id << " "<<  "n : " << n << endl;//这里pid结果一样,说明通信成功
    assert(n > 0);
    (void)n;


    return 0;
}

实验现象: 父进程向管道里面写入,子进程读,当5s后,关闭管道。