本章重点:
• 进程间通信介绍
• 掌握匿名命名管道原理操作
• 理解内核管理IPC资源的⽅式,了解C实现多态
什么是进程间通信:
进程间通信包含了:1.数据传输;2.资源共享;3.通知事件;4.进程控制。而实现进程间通信的本质是需要让不同的进程看到同一份资源,再通过OS实现通信,资源的共享是实现进程间通信的绝对基础。
但,父子进程虽然可以共享内容,但因为其特殊的写时拷贝,并不能实现数据层面 的通信
管道:

基于文件进行的通信方式称为管道,父子进程虽然不难以数据的方式进行通信,但可以以基于文件的"管道"方式进行通信,父子间进程的管道通信称之为"匿名管道"(系统中无法通过文件名称找到,也就是匿名)
匿名管道:
匿名管道只能用于父子间进程进行通信,没有磁盘级文件,只能存在内核内存中,用完即消失

管道只能是半双工,也就是一个传数据的时候另一个进程就只能读,在匿名管道中,就可以通过控制父子进程的权限来控制方向:写 ---> 读
具体实现,只需close关闭某进程的某权限就可以;


匿名管道的特性和情况:
特性:
1.管道只能单向通信
2.匿名管道只能进行具有血缘关系的进程间通信
3.管道面向字节流,不关心传输的数据类型

情况:
父子进程之间要同步

拓展
一个进程控制多个进程(进程池)
Master Slaver模式:
Master Slaver(主从模式)本质是通过匿名管道的特性和情况来制作的,通过同步特性让子进程等待父进程的写入,父进程给谁的管道写,谁才能被唤醒,通过这一模式,父进程就可以控制多个子进程(也就可以给子进程派发任务了),再引入我们的任务数组,提前给这个任务数组写好任务,在父进程给子进程派发任务时父进程就可以快速调用任务数组,提高任务的派发效率:
一、核心前提:匿名管道的关键特性(主从模式的基石)
匿名管道是半双工、面向进程、具有同步阻塞特性的通信机制,这 3 个特性是主从模式能实现的根本:
- 半双工:数据只能单向流动(父写 → 子读)
- 同步阻塞 :
- 读端:管道空时,读进程自动阻塞等待,直到有数据写入
- 写端:管道满时,写进程阻塞等待(本模式用不到)
- 独立通信 :父进程给指定子进程的管道写入数据,只有这个子进程会被唤醒,其他子进程继续阻塞
二、主从模式完整工作流程
父进程(Master):指挥官,负责创建子进程、管理管道、派发任务子进程(Slaver):打工人,默认阻塞等待任务,收到指令后执行
步骤 1:父进程批量创建子进程 + 独立管道
- 父进程循环创建 N 个子进程
- 为每个子进程单独创建一个匿名管道(父写、子读)
- 父进程保存所有子进程 PID + 对应管道的写端文件描述符
步骤 2:子进程初始化:阻塞等待任务
子进程启动后,直接从自己的管道读端读取数据 → 管道初始为空 → 子进程自动阻塞休眠,不占用 CPU 资源
步骤 3:父进程派发任务(核心逻辑)
- 父进程维护一个预定义的任务数组(提前存储所有要执行的任务)
- 父进程选择一个空闲子进程,向它的管道写入任务编号 / 任务参数
- 管道一旦有数据:对应子进程被唤醒,其他子进程继续阻塞
步骤 4:子进程执行任务
子进程读取到任务数据 → 从任务数组中找到对应任务 → 执行任务 → 执行完成后,再次回到阻塞状态,等待下一次派发

以下是有AI生成的一段极简实现Master Slever的代码:
cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
// 1. 定义任务数组(提前预加载任务)
void task1() { printf("子进程[%d]执行:打印任务\n", getpid()); }
void task2() { printf("子进程[%d]执行:计算任务\n", getpid()); }
void task3() { printf("子进程[%d]执行:IO任务\n", getpid()); }
// 函数指针数组:存储所有任务(核心:快速派发)
void (*task_list[])() = {task1, task2, task3};
#define TASK_COUNT 3
// 子进程核心逻辑:阻塞等待 + 执行任务
void slaver(int read_fd) {
int task_idx;
while (1) {
// 关键:管道空 → 子进程阻塞等待父进程写入
read(read_fd, &task_idx, sizeof(task_idx));
// 唤醒后:直接从任务数组执行任务
if (task_idx >= 0 && task_idx < TASK_COUNT) {
task_list[task_idx]();
}
printf("子进程[%d]任务执行完毕,继续等待...\n\n", getpid());
}
}
int main() {
int pipe_fd[2]; // 管道:[0]读端(子) [1]写端(父)
pid_t pid;
// 2. 创建管道 + 子进程
if (pipe(pipe_fd) == -1) { perror("pipe failed"); exit(1); }
pid = fork();
if (pid < 0) { perror("fork failed"); exit(1); }
else if (pid == 0) {
// 子进程:关闭写端,执行slaver逻辑
close(pipe_fd[1]);
slaver(pipe_fd[0]);
exit(0);
}
// 父进程:关闭读端,作为Master派发任务
close(pipe_fd[0]);
printf("父进程[%d]创建子进程成功,开始派发任务\n\n", getpid());
// 3. 父进程通过管道派发任务(写数据唤醒子进程)
int tasks[] = {0, 1, 2, 0}; // 要派发的任务序列
for (int i = 0; i < 4; i++) {
printf("父进程派发任务:%d\n", tasks[i]);
write(pipe_fd[1], &tasks[i], sizeof(tasks[i]));
sleep(1); // 模拟任务执行间隔
}
// 回收子进程
close(pipe_fd[1]);
waitpid(pid, NULL, 0);
return 0;
}
结语:
本文主要围绕Linux进程间通信展开,重点剖析了++匿名管道的底层特性,并结合经典的Master-Slaver主从模式,讲解了如何依托管道同步阻塞机制实现父子进程通信、多子进程管控与任务派发,同时借助任务数组优化任务调度效率++ ,让大家理解管道不只是简单的数据传输工具,更是多进程任务调度的核心基础。 进程间通信是Linux后台开发、高并发服务的必备知识点 ,弄懂主从模式原理也能为后续学习网络编程、服务架构打下扎实根基。如果本篇内容对你有帮助,欢迎**点赞收藏**,也欢迎评论区交流探讨Linux相关技术问题,后续还会持续分享更多操作系统与网络编程干货~