核心目标:打破进程独立性,让不同进程访问同一份资源
实现方式:通过操作系统提供的第三方资源(如管道、共享内存等)
#include
int pipe(int pipefd[2]); // 成功返回0,失败返回-1
pipefd[0]
:读端文件描述符()pipefd[1]
:写端文件描述符(✏️)#include
#include
#include
#define NUM 1024
void Write(int wfd) {
std::string content = "hello world";
pid_t pid = getpid();
int number = 0;
while (true) {
char buffer[NUM] = {0};
snprintf(buffer, sizeof(buffer), "%s-%d-%d",
content.c_str(), pid, ++number);
ssize_t n = write(wfd, buffer, strlen(buffer)); // ✨关键写入操作
sleep(1);
}
}
void Read(int rfd) {
while (true) {
char buffer[NUM] = {0};
ssize_t n = read(rfd, buffer, sizeof(buffer)-1);
if (n > 0) {
buffer[n] = '\0';
std::cout << "Parent[" << getpid() << "] received: "
<< buffer << std::endl;
}
else if (n == 0) break; // 检测写端关闭
}
}
int main() {
int pipefd[2];
pipe(pipefd); // 创建管道
if (fork() == 0) { // 子进程
close(pipefd[0]);
Write(pipefd[1]);
exit(0);
}
else { // 父进程
close(pipefd[1]);
Read(pipefd[0]);
wait(nullptr);
}
return 0;
}
代码段 | 功能说明 |
---|---|
pipe(pipefd) |
创建读端pipefd[0]和写端pipefd[1],返回内核缓冲区文件描述符 |
close(pipefd[0]) |
子进程关闭读端,确保单向通信(数据只能从子→父) |
write(wfd, buffer, len) |
原子性写入(≤PIPE_BUF时保证完整性) |
read(rfd, buffer, len) |
阻塞读取(管道空时自动挂起) |
# 编译运行
➜ make && ./pipe
Parent[1234] received: hello world-5678-1
Parent[1234] received: hello world-5678-2
...
Write end closed
原子性测试(修改为固定长度写入):
snprintf(buffer, sizeof(buffer), "%04d", number); // 4字节定长
验证小数据包完整性(无半包/粘包现象)
阻塞特性验证:
sleep(1)
观察写入速度write()
自动阻塞异常处理验证:
// 父进程提前关闭读端
close(pipefd[0]);
子进程触发SIGPIPE信号导致终止(默认行为)
CC = g++
CFLAGS = -std=c++11 -Wall
pipe: pipe.cpp
$(CC) $(CFLAGS) -o $@ $^
.PHONY: clean
clean:
rm -f pipe
血缘关系限制
匿名管道仅允许具有父子、兄弟等亲缘关系的进程通信,通过fork()
继承文件描述符实现资源共享。
单向通信模式
管道本质是半双工通信,数据只能单向流动(父→子或子→父)。双向通信需创建两个独立管道。例如:
# 双向通信示例
mkfifo pipe1 pipe2
./process1 < pipe1 > pipe2
./process2 < pipe2 > pipe1
协同与同步机制 ⚙️
内核通过互斥锁和环形缓冲区实现同步:
atomic_t
计数器保证读写操作的原子性⚛️固定容量限制
缓冲区大小由内核参数PIPE_BUF
定义(默认4096字节),可通过ulimit -p
查看:
➜ ulimit -a | grep pipe
pipe size (512 bytes, -p) 8 # 512*8=4096
字节流特性
数据以无边界字节序列传输,需应用层协议解决粘包问题。例如添加消息头:
struct Message {
uint32_t length; // 消息体长度
char data[0]; // 柔性数组
};
生命周期管理 ⏳
管道文件存在于内核空间,随进程退出自动销毁:
read()
返回0 SIGPIPE
信号 常规阻塞场景
场景 | 行为特征 | 内核实现原理 |
---|---|---|
读空管道 | 读进程进入TASK_INTERRUPTIBLE 状态,挂起等待 |
通过wait_queue_head_t 实现阻塞队列 |
写满管道 | 写进程触发pipe_write() 的wait_event_interruptible 等待 ⏳ |
缓冲区剩余空间不足时挂起 |
边界异常处理 ⚠️
read()
返回0,类似文件EOF标记 while ((n = read(pipefd, buf, BUF_SIZE)) > 0) {
// 处理数据
}
if (n == 0) printf("Writer closed\n");
SIGPIPE
信号(默认终止),可通过signal(SIGPIPE, SIG_IGN)
忽略 ️原子性写入 ⚡
单次write()
操作在满足以下条件时具有原子性:
PIPE_BUF
(通常4096字节)O_NONBLOCK
非阻塞模式时自动放弃原子性保证