📝前言:
这篇文章我们来讲讲进程间通信——认识管道
🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀CSDN主页 愚润求学
🌄其他专栏:C++学习笔记,C语言入门基础,python入门基础,C++刷题专栏
这里写目录标题
- 一,认识进程通信
- 1. 进程间通信的意义
- 2. 进程通信本质
- 二. 匿名管道
- 1. 认识管道
- 2. 匿名管道原理
- 3. 使用示例
- pipe
- 示例
- 4. 五种特性
- 5. 四种通信情况
- 6. 其他小知识
一,认识进程通信
1. 进程间通信的意义
我们都知道进程间具有独立性,那如果我们想要让进程间协作,就涉及到进程间通信。
进程间通信的目的:
- 数据传输:⼀个进程需要将它的数据发送给另⼀个进程
- 资源共享:多个进程之间共享同样的资源。
- 通知事件:⼀个进程需要向另⼀个或⼀组进程发送消息,通知它(它们)发⽣了某种事件(如进程终⽌时要通知⽗进程)。
- 进程控制:有些进程希望完全控制另⼀个进程的执⾏(如Debug进程),此时控制进程希望能够拦截另⼀个进程的所有陷⼊和异常,并能够及时知道它的状态改变。
2. 进程通信本质
- 进程间通信的本质是:先让不同的进程看到同一份资源[ “内存”](然后才有通信条件)
- 同一份资源:不能由任何一个进程提供(因为进程具有独立性,写时会发生写时拷贝),必须由OS提供。
- 既然是由OS提供,就必须有对应的系统调用(而这个通信接口具有统一的设计标准)
二. 匿名管道
1. 认识管道
管道是单向的通信通道,我们把从一个进程连接到另一个进程的一个数据流称为⼀个“管道”。
2. 匿名管道原理
- 匿名管道是基于内存级文件实现的(即:这个文件不涉及缓冲区往磁盘的刷新)【因为这个文件是内存级的,所以叫匿名】
- 通过系统调用
pipe
,打开管道文件(这是一个可读,可写的文件),会返回两个文件描述符fd
(一端是读,一端是写,通常fds[0]
:读段,fds[1]
:写端) - 子进程继承父进程的文件描述表,就会和父进程同时指向一个管道文件【即:父子进程看到同一个文件缓冲区,这就是管道】
- 再关闭子进程的一个端和父进程的一个端(如父进程关写端,子进程关读端),就实现了一个父读子写的单向通信
文件描述符角度看管道:
3. 使用示例
pipe
- 打开一读一写端口
- 因为是内存级文件,所以不需要传入要打开的文件的路径和名称
pipefd[2]
:为输出型参数,返回两个fd
,pipefd[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;// childclose(fds[0]); // 子进程把读端口关了while (true){snprintf(buffer, sizeof(buffer), "我是子进程, PID: %d, 信息序列: %d\n", getpid(), ++i);write(fds[1], buffer, strlen(buffer)); // strlen不计算 \0sleep(1);}exit(0);}// fatherclose(fds[1]); // 把写端口关了// waitpid(id, nullptr, 0);while(true){// 清空缓冲区memset(buffer, 0, sizeof(buffer)); // 同时也进行了 \0 的设置int bytes = read(fds[0], buffer, sizeof(buffer) - 1); // 预留空间给 \0printf("父进程读取成功 (%d 字节): %s\n", bytes, buffer);sleep(1);}return 0;
}
运行结果:
可见:匿名管道适用于父子进程,且父进程来打开管道文件,这样子进程继承父进程的文件描述表,才能和父进程看到同一个文件
4. 五种特性
- 匿名管道,只能用来进行具有血缘关系的进程进行进程间通信(常用于父子)
- 管道文件自带同步机制
- 管道是面向字节流的
- 管道是单向通信的
- (管道)文件的声明周期是随进程的
下面我依次简单说明一下:
- 第一点:子进程继承父进程文件描述符表(不多说了)
- 第二点:同步怎么理解?
- 对于普通文件(比如
stdout
),父子进程同时往显示器上写是不会相互影响的,都能写出来 - 但是对于管道(假如是:子写父读,且写慢,读快),则父进程要等子进程写完,才能读到数据
- 对于普通文件(比如
- 第三点:暂时不做解释
- 提一下:写快,读慢的时候,读这一行为不依赖与写的行为,而是由读自己决定的(自己读几个字节,怎么读),和曾经怎么写没有必然关系。【所以这时候读是面向字节流的】
- 第四点:单向通信(半双工的一种特殊情况)
- 半双工:任意时刻,只能一读,一写【可能两边都可以读写,只是强调任意时刻,每边只能有一个行为】
- 全双工:任意时刻,两边可以同时读写
- 第五种:当我们打开一个文件后,在进程结束前,没有
close(fd)
,进程退出时,会自动把对应文件的引用计数--
5. 四种通信情况
感兴趣的可以自己做测试,这里就不做了。
- 写慢,读快 → 读端要阻塞【等待写端】
- 写快,读慢 → 写满了缓冲区的时候,写端要阻塞【等待读端】
- 写关,继续读 → 把缓冲区读完以后,读不到信息了就会
read
返回0
,即:读到文件结尾了 - 写继续,读关 → 此时写入的行为是没有意义的,OS会杀掉进程【发送异常信号 13】(因为OS本来就是管理资源的,这种没有意义的行为在浪费资源)
6. 其他小知识
- 管道容量:管道是文件,缓冲区也有容量(不同操作系统的管道容量可能不同)
- 管道写入有原子性【当写入数据的大小不超过缓冲区大小时】:即,要么不写,要么写完。如:写入
hello world
,不会出现只写入了一半hello
,然后把world
丢掉的情况
🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!