Linux 管道
通信的本质
- 一般而言,进程之间通过OS看到同一份资源,通过OS传递信息
匿名管道
特点
- 只允许单向通信,主要是为了便于读取
- 管道文件是一个内存级的文件
基本原理
- 通过关闭读写fd,实现单向信道
- 关闭的操作结合 count (文件计数器)
示意图
- 磁盘中的文件加载到内存上只有一份内容和属性,这里的内容就是文件缓冲区的形式存储
- file对象里只有写的属性和内容的指针
实现
新系统调用pipe
函数原型
#include <unistd.h>int pipe(int pipefd[2]);
输出形参数
- 整形数组,包含两个文件描述符
- pipefd[0]:用于读取数据的文件描述符
pipefd[1]:用于写入数据的文件描述符
返回值
- 成功时,pipe 返回 0,并在 pipefd 数组中填充两个文件描述符
失败时,返回 -1,并设置 errno 以指示错误原因
本质:创建出一个内核缓冲区,让fd[1]写进去,df[0]去里面读
注意:不是0号fd和1号fd,而是两个新的fd,用户不同或相同进程之间的通信,
和之前不一样的是,我们虽然创建出两个fd,这两个fd,指向同一个file对象
复习C语言接口perror
函数原型
#include <stdio.h>void perror(const char *s);
参数
- s:这是一个自定义的字符串
输出格式
- <传入的字符串>: <errno 错误描述>
工作原理
当一个系统调用(如 open、read、write)或库函数出错时,通常会返回 -1 或 NULL 并设置 errno。errno 是一个全局整数变量,它保存了错误的代码,指示出错原因。perror 会读取 errno 并根据其值查找相应的错误消息,然后将 s 和错误消息一起打印出来
代码实验过程
#include <iostream>
#include <unistd.h>
#include <cstring>
#include <sys/wait.h>
#include <sys/types.h>using namespace std;
#define MAX 1024
int main()
{int pipefd[2] = {0};if(pipe(pipefd) == -1){perror("pipe");return 1;}int id = fork();if(id < 0){perror("fork");return 1;}// 子进程写if (id == 0){close(pipefd[0]);int cnt = 10;while (cnt){char message[MAX];snprintf(message, sizeof(message), "send message cnt: %d to father", cnt);write(pipefd[1], message, strlen(message));cnt--;sleep(1);}exit(0);}close(pipefd[1]);char receive[MAX]; // 这里需要初始化吗while (true){int n = read(pipefd[0], receive, sizeof(receive) - 1);if (n > 0) //没有我就不写,不然会一直刷新空行{receive[n] = 0;//printf("%s", receive); // 因为没有立即刷新到屏幕上,所以看不到cout << receive << endl;}}waitpid(id, nullptr, 0);return 0;
}
代码目的
- 子进程写到管道缓冲区里,父进程从管道缓冲区读
- 实验仅读、仅写、写频率大于读频率、读频率大于写频率4种情况下特点,总结管道特性
情况1:读快于写
代码改动
- 红框实现读大于写
- 蓝框里是代码的细节,就是为了留一个\0,不然cout会出问题
结论
- 正常情况:管道里没有数据,读端就会等待,进入睡眠状态,一旦有数据我就读出来
情况2:写快于读
代码改动
实验现象
总结
- 正常情况:从实验结果里看:管道被写满了,要被读到一定大小才会去写,管道当然有大小
测试管道大小
代码更改
- 注意32行往里面写的是一个字符a,虽然传的是两个字符,但是最后一个参数锁定了1
发现跑不起来,更改如下:
效果如下
- 大小是64KB
ulimit -a
- 512 bytes是单位,8是个数 ,就是4KB
- 但是测出来是64KB,具体为什么不清楚
情况3:写端关闭
代码改动
代码效果
目前不知道-1
结论
- 写端关闭,只要管道里有数据,读端就可以读取数据,读完后read返回0,表示读到文件结尾
情况4:只写不读
代码改动
注意
- 我的代码子进程应该要有一个等待回收的过程,但是没有出现反而是直接被回收了,GPT说这是OS直接调用waitpid给回收了
结论
- 关闭读端时,就没法写了,被信号13结束进程了,
总结
4种情况
- 正常情况:管道里没有数据,读端就会等待的阻塞状态,进入睡眠状态,一旦有数据就读出来
- 正常情况:从实验结果里看:管道被写满了,要被读到一定大小才会去写,管道当然有大小
- 写端关闭,只要管道里有数据,读端就可以读取数据,读完后read返回0,表示读到文件结尾
- 关闭读端时,就没法写了,被信号13
SIGPIPE
结束进程了
5种特性
- 常用于父子进程之间进程进程间通信
- 读写端同步机制
- 面向字节流
- 管道也是文件,生命周期随进程
- 单向通信,半双工通信的一种特殊情况
4种情况都有一个规则:匿名管道的使用是以读为目的,不管你写端是否关闭,你有我就可以读;但是读端关闭,你就不准写了,会终止写端
补充观点
- 管道文件和磁盘文件的file对象结构一样,但是指向的方法集一定不一样
- 方法集:有一些用于刷新文件缓冲区到磁盘上;
- OS一定有办法检测,是否是单向信道
要点
- pipe函数的使用
- 原理图,通信的本质
- 4种情况,5种特性,都指向一种规则