了解了匿名管道的五种特性后,我们必须得谈谈匿名管道的四种情况--快读慢写,快写慢读,只写不读,只读不写,接下来我将一一讲述,话不多说现在开始
1.快读慢写
cpp
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
void ChildWrite(int wfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
buffer[0] = '\0';
snprintf(buffer, sizeof(buffer), "我是子进程, pid : %d, cnt : %d", getpid(), cnt++);
write(wfd, buffer, strlen(buffer));
sleep(1);
}
}
void FutherRead(int rfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
buffer[0] = '\0';
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
std::cout << buffer << std::endl;
}
}
}
int main()
{
// 创建管道
int pfd[2] = {0};
int n = pipe(pfd);
if (n == -1)
{
std::cerr << "pipe error!" << std::endl;
return 1;
}
std::cout << "pfd[0] : " << pfd[0] << std::endl;
std::cout << "pfd[1] : " << pfd[1] << std::endl;
// 一般来说,pfd[0] -> 读端 pfd[1] -> 写端
// 创建子进程
pid_t id = fork();
if (id == 0)
{
// 关闭子进程读端
close(pfd[0]);
// 子进程向管道写入内容
ChildWrite(pfd[1]);
// 关闭子进程写端
close(pfd[1]);
}
// 关闭父进程写端
close(pfd[1]);
// 父进程读取管道内容
FutherRead(pfd[0]);
// 等待子进程结束
waitpid(id, nullptr, 0);
// 关闭父进程读端
close(pfd[0]);
return 0;
}

实验结果--读端阻塞,等待写端
2.快写慢读
cpp
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
void ChildWrite(int wfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
buffer[0] = '\0';
snprintf(buffer, sizeof(buffer), "我是子进程, pid : %d, cnt : %d", getpid(), cnt++);
write(wfd, buffer, strlen(buffer));
//sleep(1);
}
}
void FutherRead(int rfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
sleep(5);
buffer[0] = '\0';
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
std::cout << buffer << std::endl;
}
}
}
int main()
{
// 创建管道
int pfd[2] = {0};
int n = pipe(pfd);
if (n == -1)
{
std::cerr << "pipe error!" << std::endl;
return 1;
}
std::cout << "pfd[0] : " << pfd[0] << std::endl;
std::cout << "pfd[1] : " << pfd[1] << std::endl;
// 一般来说,pfd[0] -> 读端 pfd[1] -> 写端
// 创建子进程
pid_t id = fork();
if (id == 0)
{
// 关闭子进程读端
close(pfd[0]);
// 子进程向管道写入内容
ChildWrite(pfd[1]);
// 关闭子进程写端
close(pfd[1]);
}
// 关闭父进程写端
close(pfd[1]);
// 父进程读取管道内容
FutherRead(pfd[0]);
// 等待子进程结束
waitpid(id, nullptr, 0);
// 关闭父进程读端
close(pfd[0]);
return 0;
}

实验结果--写端阻塞,等待读端
3只写不读
cpp
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
void ChildWrite(int wfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
buffer[0] = '\0';
snprintf(buffer, sizeof(buffer), "我是子进程, pid : %d, cnt : %d", getpid(), cnt++);
write(wfd, buffer, strlen(buffer));
//sleep(1);
//break;
}
}
void FutherRead(int rfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
//sleep(5);
buffer[0] = '\0';
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
std::cout << buffer << std::endl;
}
/*
else if (n == 0)
{
std::cout << "没有内容 : " << n << std::endl;
std::cout << "子进程退出,我也退出" << n << std::endl;
break;
}
else
{
break;
}*/
break;
}
}
int main()
{
// 创建管道
int pfd[2] = {0};
int n = pipe(pfd);
if (n == -1)
{
std::cerr << "pipe error!" << std::endl;
return 1;
}
std::cout << "pfd[0] : " << pfd[0] << std::endl;
std::cout << "pfd[1] : " << pfd[1] << std::endl;
// 一般来说,pfd[0] -> 读端 pfd[1] -> 写端
// 创建子进程
pid_t id = fork();
if (id == 0)
{
// 关闭子进程读端
close(pfd[0]);
// 子进程向管道写入内容
ChildWrite(pfd[1]);
// 关闭子进程写端
close(pfd[1]);
}
// 关闭父进程写端
close(pfd[1]);
// 父进程读取管道内容
FutherRead(pfd[0]);
//验证只写不读,提前关闭读端
close(pfd[0]);
// 等待子进程结束
waitpid(id, nullptr, 0);
// 关闭父进程读端
close(pfd[0]);
return 0;
}

OS始终想着怎么优化,所以如果只写不读,那么就没有任何意义,操作系统不会做这种没有意义的事情,会直接杀死进程,下面我们来验证一下
cpp
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
void ChildWrite(int wfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
buffer[0] = '\0';
snprintf(buffer, sizeof(buffer), "我是子进程, pid : %d, cnt : %d", getpid(), cnt++);
write(wfd, buffer, strlen(buffer));
//sleep(1);
//break;
}
}
void FutherRead(int rfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
//sleep(5);
buffer[0] = '\0';
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
std::cout << buffer << std::endl;
}
/*
else if (n == 0)
{
std::cout << "没有内容 : " << n << std::endl;
std::cout << "子进程退出,我也退出" << n << std::endl;
break;
}
else
{
break;
}*/
break;
}
}
int main()
{
// 创建管道
int pfd[2] = {0};
int n = pipe(pfd);
if (n == -1)
{
std::cerr << "pipe error!" << std::endl;
return 1;
}
std::cout << "pfd[0] : " << pfd[0] << std::endl;
std::cout << "pfd[1] : " << pfd[1] << std::endl;
// 一般来说,pfd[0] -> 读端 pfd[1] -> 写端
// 创建子进程
pid_t id = fork();
if (id == 0)
{
// 关闭子进程读端
close(pfd[0]);
// 子进程向管道写入内容
ChildWrite(pfd[1]);
// 关闭子进程写端
close(pfd[1]);
}
// 关闭父进程写端
close(pfd[1]);
// 父进程读取管道内容
FutherRead(pfd[0]);
//验证只写不读,提前关闭读端
close(pfd[0]);
// 等待子进程结束
//waitpid(id, nullptr, 0);
int status = 0;
waitpid(id, &status, 0);
std::cout << "exit code : " << ((status >> 8) & 0xFF) << std::endl;
std::cout << "exit signal : " << (status & 0x7F) << std::endl;
// 关闭父进程读端
close(pfd[0]);
return 0;
}

此时退出码为0,表示无意义,退出信号为13,符合预期
4.只读不写
cpp
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <cstdio>
#include <cstring>
void ChildWrite(int wfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
buffer[0] = '\0';
snprintf(buffer, sizeof(buffer), "我是子进程, pid : %d, cnt : %d", getpid(), cnt++);
write(wfd, buffer, strlen(buffer));
sleep(1);
break;
}
}
void FutherRead(int rfd)
{
char buffer[1024];
int cnt = 0;
while (true)
{
//sleep(5);
buffer[0] = '\0';
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
std::cout << buffer << std::endl;
}
else if (n == 0)
{
std::cout << "没有内容 : " << n << std::endl;
std::cout << "子进程退出,我也退出" << n << std::endl;
break;
}
else
{
break;
}
}
}
int main()
{
// 创建管道
int pfd[2] = {0};
int n = pipe(pfd);
if (n == -1)
{
std::cerr << "pipe error!" << std::endl;
return 1;
}
std::cout << "pfd[0] : " << pfd[0] << std::endl;
std::cout << "pfd[1] : " << pfd[1] << std::endl;
// 一般来说,pfd[0] -> 读端 pfd[1] -> 写端
// 创建子进程
pid_t id = fork();
if (id == 0)
{
// 关闭子进程读端
close(pfd[0]);
// 子进程向管道写入内容
ChildWrite(pfd[1]);
// 关闭子进程写端
close(pfd[1]);
}
// 关闭父进程写端
close(pfd[1]);
// 父进程读取管道内容
FutherRead(pfd[0]);
// 等待子进程结束
waitpid(id, nullptr, 0);
// 关闭父进程读端
close(pfd[0]);
return 0;
}

只读不写场景是指读进程持续读取数据,但写进程停止写入且关闭了写端 ,此时读端会读取完管道中剩余数据,随后检测到 "文件结束(EOF)",read() 返回 0,读进程可正常退出
5.四种场景核心结论与特性总结
通过对四种场景的分析,我们可以归纳出匿名管道的三大核心特性,以及对应的场景体现:
| 匿名管道特性 | 对应场景 | 核心表现 |
|---|---|---|
| 自带同步机制 | 快读慢写、快写慢读 | 读端空则阻塞读进程,写端满则阻塞写进程 |
| 内核缓冲区有容量限制 | 快写慢读 | 写端写入速度过快时,缓冲区满后阻塞写进程 |
| 半双工 + 面向字节流 | 所有场景 | 数据单向传输,无消息边界,读端按字节读取 |
| EOF 检测机制 | 只读不写 | 所有写端关闭后,读端 read() 返回 0 |
| 异常场景信号终止 | 只写不读 | 读端关闭后,写端收到 SIGPIPE 信号 |