进程间通信

本章重点:

• 进程间通信介绍
• 掌握匿名命名管道原理操作
• 理解内核管理IPC资源的⽅式,了解C实现多态

什么是进程间通信:

进程间通信包含了:1.数据传输;2.资源共享;3.通知事件;4.进程控制。而实现进程间通信的本质是需要让不同的进程看到同一份资源,再通过OS实现通信,资源的共享是实现进程间通信的绝对基础。

但,父子进程虽然可以共享内容,但因为其特殊的写时拷贝,并不能实现数据层面 的通信

管道:

基于文件进行的通信方式称为管道,父子进程虽然不难以数据的方式进行通信,但可以以基于文件的"管道"方式进行通信,父子间进程的管道通信称之为"匿名管道"(系统中无法通过文件名称找到,也就是匿名)

匿名管道:

匿名管道只能用于父子间进程进行通信,没有磁盘级文件,只能存在内核内存中,用完即消失

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

具体实现,只需close关闭某进程的某权限就可以;

匿名管道的特性和情况:

特性:

1.管道只能单向通信

2.匿名管道只能进行具有血缘关系的进程间通信

3.管道面向字节流,不关心传输的数据类型

情况:

父子进程之间要同步

拓展

一个进程控制多个进程(进程池)

Master Slaver模式:

Master Slaver(主从模式)本质是通过匿名管道的特性和情况来制作的,通过同步特性让子进程等待父进程的写入,父进程给谁的管道写,谁才能被唤醒,通过这一模式,父进程就可以控制多个子进程(也就可以给子进程派发任务了),再引入我们的任务数组,提前给这个任务数组写好任务,在父进程给子进程派发任务时父进程就可以快速调用任务数组,提高任务的派发效率:

一、核心前提:匿名管道的关键特性(主从模式的基石)

匿名管道是半双工、面向进程、具有同步阻塞特性的通信机制,这 3 个特性是主从模式能实现的根本:

  1. 半双工:数据只能单向流动(父写 → 子读)
  2. 同步阻塞
    • 读端:管道空时,读进程自动阻塞等待,直到有数据写入
    • 写端:管道满时,写进程阻塞等待(本模式用不到)
  3. 独立通信 :父进程给指定子进程的管道写入数据,只有这个子进程会被唤醒,其他子进程继续阻塞

二、主从模式完整工作流程

父进程(Master):指挥官,负责创建子进程、管理管道、派发任务子进程(Slaver):打工人,默认阻塞等待任务,收到指令后执行

步骤 1:父进程批量创建子进程 + 独立管道

  1. 父进程循环创建 N 个子进程
  2. 每个子进程单独创建一个匿名管道(父写、子读)
  3. 父进程保存所有子进程 PID + 对应管道的写端文件描述符

步骤 2:子进程初始化:阻塞等待任务

子进程启动后,直接从自己的管道读端读取数据 → 管道初始为空 → 子进程自动阻塞休眠,不占用 CPU 资源

步骤 3:父进程派发任务(核心逻辑)

  1. 父进程维护一个预定义的任务数组(提前存储所有要执行的任务)
  2. 父进程选择一个空闲子进程,向它的管道写入任务编号 / 任务参数
  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相关技术问题,后续还会持续分享更多操作系统与网络编程干货~

相关推荐
DLYSB_14 小时前
存储运维实战:基于 Ceph Event 监听与 Python 适配器的分布式存储健康度物理声光响应架构
运维·ceph·python·报警灯
爱丶狸15 小时前
Grafana_Zabbix_ImageRenderer_部署与前端操作手册
linux·前端·zabbix·grafana·kylin
阿里云云原生15 小时前
可用性从 99.9% 跃升至 99.995%:畅捷通如何用 AI 重塑运维底座?
运维·网络·人工智能
k4m7v2pz16 小时前
把 Bevy 0.14 游戏移植到 R36S 掌机:一场与“无窗口系统“的搏斗
linux·rust·bevy·r36s·rk3326·开源掌机
阿童木写作16 小时前
Python实现Temu图片批量翻译自动化教程
运维·人工智能·python·自动化
iori97king17 小时前
库存不足自动通知:第一次使用自动化
运维·自动化
@insist12317 小时前
信息系统管理工程师-信息系统运维资源、技术与智能运维核心考点解析
运维·软考·信管·软件水平考试·信息系统管理工程师
木子欢儿17 小时前
Intel 架构的 MacBook Pro 运行 Linux 发热量高解决
linux·运维·服务器
KKKlucifer19 小时前
运营商全域安全运维支撑体系落地实践
运维·安全
EAIReport20 小时前
工业电气自动化数据治理破局:多Agent大模型赋能全景智能决策
大数据·运维·自动化