进程间的通信--IPC
目录前言1. 管道解决“数据流”问题2. 信号解决“事件通知”问题3. 其他IPC共享内存、消息队列等解决“高效/灵活通信”问题1.2 基本使用流程一、消息队列Message Queue1. 概念2. 常用结构3. 常用函数(1) msgget —— 创建/获取消息队列参数解释常用写法说明(2) msgsnd —— 发送消息参数示例(3) msgrcv —— 接收消息参数msgtyp 规则重点(4) msgctl —— 控制/删除参数常用 cmd二、共享内存Shared Memory1. 概念2. 常用函数(1) shmget —— 创建共享内存参数示例(2) shmat —— 连接共享内存参数示例(3) shmdt —— 解除映射(4) shmctl —— 控制/删除cmd三、消息队列 vs 共享内存重点对比前言1. 管道解决“数据流”问题它是什么内核里的一段临时缓冲区像个单向水管。能干嘛把一个进程输出的字节流原封不动地灌给另一个进程。局限性为什么不够用只能单向数据只能从一个口进从另一个口出。无边界传过去就是一串连续的字节接收方不知道哪里是头哪里是尾得自己想办法切分。依赖亲缘关系传统管道只能在父子进程间用有名管道FIFO虽然能突破这个但其他局限仍在。结论管道只适合“一个进程生产数据另一个进程消费数据”这种流水线场景。一旦通信不是这种模式管道就废了。2. 信号解决“事件通知”问题它是什么一种软件中断内核给进程发一个编号比如9、15。能干嘛告诉进程“出事了”或“你该干嘛了”比如“立刻死掉”“暂停执行”“重新读取配置”。为什么管道不行管道传的是数据不是事件。如果要用管道通知进程退出进程得一直读管道、解析内容才知道“哦这是让我退出”这浪费CPU。信号是异步的进程正在执行任何代码时信号都能随时插入打断前提是没屏蔽管道做不到这种“即时打断”能力。局限性为什么不够用信息量极少信号只能带一个整数值传不了复杂结构体。不可靠信号可能被丢弃或合并比如连续发多个同样的信号进程可能只收到一个。结论信号只适合发“简单指令”不适合传数据。3. 其他IPC共享内存、消息队列等解决“高效/灵活通信”问题共享内存物理内存里划一块公共区域多个进程的虚拟地址都映射到它。为什么管道不行管道要经过内核拷贝进程A→内核→进程B共享内存直接读写那块区域零拷贝速度是管道的好几倍甚至几十倍。代消息队列内核里维护的一个个消息包每个包有类型、长度、内容。为什么管道不行管道只能顺序读取而消息队列可以按类型挑着读而且每个消息有边界不用自己切分字节流。System V IPC 包含三种进程间通信机制有消息队列信号灯也叫信号量共享内存。此外还有 System V IPC 的补充版本 POSIX IPC这两组 IPC 的通信方法基本一致本章以 System V IPC 为例介绍 Linux 进程通信 机制。1.2 基本使用流程// SystemV IPC 包含三种内核对象 1. 消息队列 (Message Queue) 2. 共享内存 (Shared Memory) 3. 信号量 (Semaphore) // 查看当前系统的IPC对象 $ ipcs -a ------ Message Queues -------- key msqid owner perms used-bytes messages 0x12345678 0 root 666 0 0 ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x12345679 0 root 666 1024 0 ------ Semaphore Arrays -------- key semid owner perms nsems 0x1234567a 0 root 666 1一、消息队列Message Queue1. 概念shmget(IPC_CREAT)的行为是有就返回已有的没有才创建不会重复创建多个同 key 的共享内存。消息队列是一种内核维护的链式队列进程 → 发消息 → 内核队列另一个进程 → 从队列取消息异步通信按消息类型过滤2. 常用结构struct msgbuf { long mtype; // 消息类型必须 0 char mtext[128]; // 消息内容 };3. 常用函数(1) msgget —— 创建/获取消息队列int msgget(key_t key, int msgflg);参数解释参数含义keyIPC keyftok生成或IPC_PRIVATEmsgflg权限 创建标志常用写法msgget(key, IPC_CREAT | 0666);说明IPC_CREAT不存在就创建0666权限读写(2) msgsnd —— 发送消息int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);参数参数含义msqidmsgget返回的IDmsgp消息结构体指针msgsz消息正文长度不包括mtypemsgflg控制阻塞方式示例msgsnd(msqid, msg, strlen(msg.mtext)1, 0);(3) msgrcv —— 接收消息ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);参数参数含义msqid队列IDmsgp接收缓冲区msgsz最大接收长度msgtyp消息类型过滤msgflg控制阻塞msgtyp 规则重点值含义0接收第一条消息0接收指定 type0接收 ≤ 绝对值的最小 type(4) msgctl —— 控制/删除int msgctl(int msqid, int cmd, struct msqid_ds *buf);参数参数含义msqid队列IDcmd操作命令buf状态结构可为NULL常用 cmd命令含义IPC_RMID删除队列IPC_STAT获取状态IPC_SET修改属性二、共享内存Shared Memory1. 概念共享内存 最快 IPC多进程共享同一块物理内存不需要拷贝数据零拷贝需要自己加同步信号量2. 常用函数(1) shmget —— 创建共享内存int shmget(key_t key, size_t size, int shmflg);参数参数含义keyftok生成或IPC_PRIVATEsize共享内存大小shmflg权限 创建标志示例shmget(key, 1024, IPC_CREAT | 0666);(2) shmat —— 连接共享内存void *shmat(int shmid, const void *shmaddr, int shmflg);参数参数含义shmidshmget返回IDshmaddr通常 NULL系统自动分配shmflg0 或 SHM_RDONLY示例char *p shmat(shmid, NULL, 0);(3) shmdt —— 解除映射int shmdt(const void *shmaddr);参数含义shmaddrshmat返回的地址(4) shmctl —— 控制/删除int shmctl(int shmid, int cmd, struct shmid_ds *buf);cmd命令含义IPC_RMID删除共享内存IPC_STAT查看信息IPC_SET修改三、消息队列 vs 共享内存重点对比对比项消息队列共享内存速度中等拷贝最快零拷贝通信方式内核转发直接内存访问结构有序消息原始内存同步自动需要自己做复杂度简单较复杂使用场景通信控制大数据交换