进程间通信

本章重点:

• 进程间通信介绍
• 掌握匿名命名管道原理操作
• 理解内核管理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相关技术问题,后续还会持续分享更多操作系统与网络编程干货~

相关推荐
悠悠121381 小时前
从0到1掌握Ansible:让自动化运维不再是梦想
运维·自动化·ansible
璞华Purvar1 小时前
VC PE投资管理系统选型的核心考量因素有哪些?(2026选型指南)
大数据·运维·人工智能
b55t4ck1 小时前
Linux CVE-2026-31431(Copy Fail)漏洞深入复现分析(待完善).md
linux·运维·服务器
前端老曹1 小时前
Linux 指令完整版
linux·运维·服务器
ChaoFeiLi1 小时前
Linux离线安装NVIDIA Container Toolkit
linux·服务器
funnycoffee1231 小时前
cisco Firepower 4110 9300 FXOS set chassis hostname
java·服务器·数据库
广州服务器托管1 小时前
[2026.5.12][IT工坊]WIN11.26300.8376专业工作站版[PIIS]中简 深度优化
运维·人工智能·windows·计算机网络·可信计算技术
草莓熊Lotso1 小时前
【Linux网络】从 0 到 1 实现高性能 UDP 聊天室:深入拆解 Linux 网络编程与线程池架构
linux·运维·服务器·网络·数据库·c++·udp
05候补工程师1 小时前
ROS 2 入门:从零实现小海龟 (Turtlesim) 的手动控制与自动化绘圆
运维·经验分享·python·ubuntu·机器人·自动化