【Linux】进程间通信(一):认识管道

发布于:2025-05-18 ⋅ 阅读:(13) ⋅ 点赞:(0)

📝前言:

这篇文章我们来讲讲进程间通信——认识管道

🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记C语言入门基础python入门基础C++刷题专栏


一,认识进程通信

1. 进程间通信的意义

我们都知道进程间具有独立性,那如果我们想要让进程间协作,就涉及到进程间通信。

进程间通信的目的:

  • 数据传输:⼀个进程需要将它的数据发送给另⼀个进程
  • 资源共享:多个进程之间共享同样的资源。
  • 通知事件:⼀个进程需要向另⼀个或⼀组进程发送消息,通知它(它们)发⽣了某种事件(如进程终⽌时要通知⽗进程)。
  • 进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变。

2. 进程通信本质

  • 进程间通信的本质是:先让不同的进程看到同一份资源[ “内存”](然后才有通信条件)
  • 同一份资源:不能由任何一个进程提供(因为进程具有独立性,写时会发生写时拷贝),必须由OS提供。
  • 既然是由OS提供,就必须有对应的系统调用(而这个通信接口具有统一的设计标准)

二. 匿名管道

1. 认识管道

管道是单向的通信通道,我们把从一个进程连接到另一个进程的一个数据流称为⼀个“管道”

在这里插入图片描述

2. 匿名管道原理

  • 匿名管道是基于内存级文件实现的(即:这个文件不涉及缓冲区往磁盘的刷新)【因为这个文件是内存级的,所以叫匿名】
  • 通过系统调用pipe,打开管道文件(这是一个可读,可写的文件),会返回两个文件描述符fd(一端是读,一端是写,通常fds[0]:读段,fds[1]:写端)
  • 子进程继承父进程的文件描述表,就会和父进程同时指向一个管道文件【即:父子进程看到同一个文件缓冲区,这就是管道】
  • 再关闭子进程的一个端和父进程的一个端(如父进程关写端,子进程关读端),就实现了一个父读子写的单向通信

文件描述符角度看管道:
在这里插入图片描述

3. 使用示例

pipe

在这里插入图片描述

  • 打开一读一写端口
  • 因为是内存级文件,所以不需要传入要打开的文件的路径和名称
  • pipefd[2]:为输出型参数,返回两个fdpipefd[0]:读端,pipefd[1]:写端

示例

#include <stdio.h>
#include <unistd.h>
#include <cstring>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main()
{
    // 让子进程写,父进程读
    int fds[2];
    if(pipe(fds) == -1)
    {
        perror("make pipe");
        exit(1);
    }

    char buffer[100];
    int id = fork();

    if (id == 0)
    {
        int i = 0;
        // child
        close(fds[0]); // 子进程把读端口关了
        while (true)
        {
            snprintf(buffer, sizeof(buffer), "我是子进程, PID: %d, 信息序列: %d\n", getpid(), ++i);
            write(fds[1], buffer, strlen(buffer)); // strlen不计算 \0
            sleep(1);
        }
        exit(0);
    }
    // father
    close(fds[1]); // 把写端口关了
    // waitpid(id, nullptr, 0);

    while(true)
    {
        // 清空缓冲区
        memset(buffer, 0, sizeof(buffer)); // 同时也进行了 \0 的设置
        int bytes = read(fds[0], buffer, sizeof(buffer) - 1); // 预留空间给 \0
        printf("父进程读取成功 (%d 字节): %s\n", bytes, buffer);
        sleep(1);
    }
    return 0;
}

运行结果:
在这里插入图片描述
可见:匿名管道适用于父子进程,且父进程来打开管道文件,这样子进程继承父进程的文件描述表,才能和父进程看到同一个文件

4. 五种特性

  1. 匿名管道,只能用来进行具有血缘关系的进程进行进程间通信(常用于父子)
  2. 管道文件自带同步机制
  3. 管道是面向字节流的
  4. 管道是单向通信的
  5. (管道)文件的声明周期是随进程的

下面我依次简单说明一下:

  • 第一点:子进程继承父进程文件描述符表(不多说了)
  • 第二点:同步怎么理解?
    • 对于普通文件(比如stdout),父子进程同时往显示器上写是不会相互影响的,都能写出来
    • 但是对于管道(假如是:子写父读,且写慢,读快),则父进程要等子进程写完,才能读到数据
  • 第三点:暂时不做解释
    • 提一下:写快,读慢的时候,读这一行为不依赖与写的行为,而是由读自己决定的(自己读几个字节,怎么读),和曾经怎么写没有必然关系。【所以这时候读是面向字节流的】
  • 第四点:单向通信(半双工的一种特殊情况)
    • 半双工:任意时刻,只能一读,一写【可能两边都可以读写,只是强调任意时刻,每边只能有一个行为】
    • 全双工:任意时刻,两边可以同时读写
  • 第五种:当我们打开一个文件后,在进程结束前,没有close(fd),进程退出时,会自动把对应文件的引用计数--

5. 四种通信情况

感兴趣的可以自己做测试,这里就不做了。

  • 写慢,读快 → 读端要阻塞【等待写端】
  • 写快,读慢 → 写满了缓冲区的时候,写端要阻塞【等待读端】
  • 写关,继续读 → 把缓冲区读完以后,读不到信息了就会read返回0,即:读到文件结尾了
  • 写继续,读关 → 此时写入的行为是没有意义的,OS会杀掉进程【发送异常信号 13】(因为OS本来就是管理资源的,这种没有意义的行为在浪费资源)

6. 其他小知识

  • 管道容量:管道是文件,缓冲区也有容量(不同操作系统的管道容量可能不同)
  • 管道写入有原子性【当写入数据的大小不超过缓冲区大小时】:即,要么不写,要么写完。如:写入hello world,不会出现只写入了一半hello,然后把world丢掉的情况

🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!


网站公告

今日签到

点亮在社区的每一天
去签到