进程间通信:消息队列、信号量与内核管理

news2026/4/26 2:43:53
一、System V 消息队列消息队列 - OS维护了一个队列 - 通过队列的形式让不同进程之间看到同一份资源消息队列提供了一种一个进程给另一个进程发送有类型数据块的方式每个数据块都被认为是有一个类型接收者进程接收的数据块可以有不同的类型值OS需要对消息队列进行管理 -【先描述再组织】两个进程需要需要如何保证自己看到的是同一份资源 上层约定同一个keyIPC资源必须手动/代码删除否则不会自动清除除非重启System V IPC资源生命周期随内核1.1 理解消息队列管道和共享内存传输的都是无格式字节流就像你把一堆信扔进一个麻袋对方拿出来不知道哪封是先写的、哪封是重要的。消息队列则给每封信贴上了“类型标签”接收方可以按类型取信。特点数据有结构每条消息包含一个正整数的类型 任意字节的数据体。支持按类型接收进程可以只接收自己关心的消息。内核管理队列存在于内核中不随进程退出而消失除非手动删除或重启系统。1.2 核心数据结构与函数内核为每个消息队列维护一个msqid_ds结构里面记录了权限、时间戳、消息数量、字节数等。ftok—— 生成唯一 keykey_t ftok(const char *pathname, int proj_id);通过一个路径和项目ID生成一个几乎唯一的 key供msgget使用。️msgget—— 创建或获取消息队列int msgget(key_t key, int msgflg); // msgflg: IPC_CREAT | 0666 (不存在则创建权限 rw-rw-rw-)返回值消息队列标识符 msqidmsgsnd—— 发送消息int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); // msgp 指向一个结构体第一个成员必须是 long mtype struct msgbuf { long mtype; // 消息类型必须 0 char mtext[100]; // 消息数据可自定义大小 }; // msgsz 是 mtext 的字节数不含 mtypemsgrcv—— 接收消息ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); // msgtyp 0 : 取队列中第一条消息 // msgtyp 0 : 取类型等于 msgtyp 的第一条消息 // msgtyp 0 : 取类型小于等于 |msgtyp| 的最小类型的第一条消息️msgctl—— 控制消息队列int msgctl(int msqid, int cmd, struct msqid_ds *buf); // cmd: IPC_RMID 删除队列1.3 简单例子服务端与客户端comm.hpp公共头#pragma once #include sys/types.h #include sys/ipc.h #include sys/msg.h #include string.h #include iostream #include unistd.h #define PATHNAME /tmp #define PROJ_ID 0x66 #define CLIENT_TYPE 1 #define SERVER_TYPE 2 struct Message { long mtype; char mtext[256]; };server.cpp接收客户端消息#include comm.hpp int main() { key_t key ftok(PATHNAME, PROJ_ID); int msqid msgget(key, IPC_CREAT | 0666); if (msqid -1) { perror(msgget); exit(1); } Message msg; while (true) { // 接收类型为 CLIENT_TYPE 的消息 ssize_t s msgrcv(msqid, msg, sizeof(msg.mtext), CLIENT_TYPE, 0); if (s 0) { std::cout Server recv: msg.mtext std::endl; if (strcmp(msg.mtext, quit) 0) break; } } msgctl(msqid, IPC_RMID, nullptr); // 删除队列 return 0; }client.cpp发送消息#include comm.hpp int main() { key_t key ftok(PATHNAME, PROJ_ID); int msqid msgget(key, IPC_CREAT | 0666); // 获取已有队列 if (msqid -1) { perror(msgget); exit(1); } Message msg; msg.mtype CLIENT_TYPE; while (true) { std::cout Input: ; std::cin.getline(msg.mtext, sizeof(msg.mtext)); msgsnd(msqid, msg, strlen(msg.mtext) 1, 0); if (strcmp(msg.mtext, quit) 0) break; } return 0; }⚠️注意System V IPC 资源消息队列、共享内存、信号量的生命周期随内核如果不主动删除IPC_RMID即使所有进程退出它们依然存在直到系统重启。可以用ipcs -q查看ipcrm -q msqid删除。1.4 查看System V IPC二、信号量也称信号灯本质是一个计数器用来表明临界资源中资源的数量的多少信号量是 System V IPC 中最抽象但最核心的机制它不传递数据只负责解决多进程对共享资源的同步与互斥问题是实现进程间 “协作规则” 的关键。2.1 为什么需要信号量共享内存是最快的 IPC但它没有同步互斥机制。两个进程同时写同一块共享内存数据会乱掉 —— 这就是并发问题。2.2 核心概念概念通俗解释经典例子共享资源临界资源多个进程能同时访问的公共资源内存、文件、设备等多进程操作的同一块共享内存、同一个打印机临界区进程中访问共享资源的代码段进程中对共享内存写数据的那几行代码互斥任何时刻只允许一个进程进入临界区访问共享资源打印机同一时间只能给一个进程打印同步多个进程访问共享资源时必须按预定的顺序执行生产者进程生产数据后消费者进程才能读取不能反着来信号量本质上就是一个计数器记录当前还有多少个资源可用。进程访问资源前先“申请”P操作计数器减1用完“释放”V操作计数器加1。如果计数器为 0申请进程就会阻塞等待直到其他进程释放资源。核心原则对共享资源的保护本质是对访问共享资源的临界区代码的保护而非资源本身。2.3 理解信号量电影院买票的经典比喻放映厅有 100 个座位 → 信号量初值为 100。每个观众买票 → 信号量减 1预订一个座位。票卖光了信号量 0→ 后来的观众必须等待。观众离场 → 信号量加 1唤醒等待的人。买票就是“预订资源”而不是真的要坐上去的那一刻才占用。信号量的本质是内核维护的一个计数器结合P/V 原语操作原子操作不可被中断实现对临界区的进入控制。计数器的数值代表当前可用的共享资源数量P 操作申请资源计数器减 1若结果 0进程阻塞等待若≥0进程可进入临界区V 操作释放资源计数器加 1若结果≤0唤醒一个阻塞的等待进程。最经典的信号量二元信号量互斥锁当信号量的计数器只能取0 或 1时就是二元信号量对应我们常说的 “互斥锁”初始值为 1表示共享资源可用进程进入临界区前执行 P 操作计数器变为 0资源被占用进程退出临界区后执行 V 操作计数器变回 1资源释放若其他进程此时执行 P 操作计数器变为 - 1进程阻塞直到持有锁的进程执行 V 操作。通俗比喻信号量就像停车场的入口闸机计数器是停车场的剩余车位初始值为 1010 个车位每进一辆车P 操作车位减 1每出一辆车V 操作车位加 1车位为 0 时后续车辆进程只能排队等待阻塞直到有车开出V 操作。二元信号量就是只有 1 个车位的停车场同一时间只能停一辆车。2.4 二元信号量VS多元信号量原子操作P/V 操作是内核实现的原子操作不会被进程调度中断避免了多进程操作计数器时的竞态问题生命周期随内核与消息队列、共享内存一致进程退出后信号量不会自动释放需手动删除用于同步 / 互斥二元信号量实现互斥多元信号量计数器 1实现同步资源预订机制进程必须先通过 P 操作申请信号量才能访问共享资源本质是对共享资源的 “预订”避免冲突。2.5 信号量与共享内存的配合共享内存是最快的 IPC 方式但本身没有同步互斥机制多个进程同时读写会导致数据混乱比如一个进程写数据时另一个进程同时读得到残缺数据。因此实际开发中共享内存必然与信号量配合使用用信号量保护共享内存的临界区实现 “一人写多人读” 或 “一人读无人写” 的规则。通俗理解共享内存是一间公共书房共享资源信号量是书房的门钥匙二元信号量同一时间只有一个人能拿到钥匙进入书房临界区其他人只能在门口等待。2.6 相关函数semget—— 创建/获取信号量集int semget(key_t key, int nsems, int semflg); // nsems: 信号量集中信号量的个数一般写 1 // 返回值信号量集标识符 semidsemctl—— 控制信号量初始化、删除等int semctl(int semid, int semnum, int cmd, ...); // 初始化cmd SETVAL第四个参数是 union semun union semun { int val; struct semid_ds *buf; unsigned short *array; };// 删除cmd IPC_RMIDsemop—— P/V 操作int semop(int semid, struct sembuf *sops, size_t nsops); struct sembuf { unsigned short sem_num; // 信号量编号0 表示第一个 short sem_op; // 1 是 V操作释放-1 是 P操作申请 short sem_flg; // 一般 0阻塞或 IPC_NOWAIT };创建、初始化分离semget() 创建信号量只分配资源semctl() 初始化值SETVAL设置初始三、内核是组织管理IPC资源所有 System V IPC 资源消息队列、共享内存、信号量都有三个共同属性内核通过这三个属性实现统一管理键值key_tIPC 资源的 “全局唯一标识”由ftok函数生成用于进程间查找对应的 IPC 资源标识idIPC 资源的 “内核局部标识”由shmget/msgget/semget返回进程通过 id 操作具体的 IPC 资源权限结构kern_ipc_perm所有 IPC 资源的公共属性结构体包含资源的所有者uid/gid、创建者cuid/cgid、访问权限mode、键值key等是内核管理 IPC 资源的基础。3.1 三种 IPC 资源的统一抽象关键理解shmid,msgid,semid本质上都是数组下标它们不是指针不是句柄而是ipc_id_ary数组的索引通过下标找到kern_ipc_perm *再强制类型转换成具体结构3.2 用 C 实现多态Polymorphism// 1. 基类所有 IPC 资源共有的权限信息 struct kern_ipc_perm { key_t key; // IPC键值 uid_t uid; // 所有者 gid_t gid; // ... 其他权限字段 }; // 2. 柔性数组技巧结构体内部带数组 struct ipc_id_ary { int size; // 数组大小 struct kern_ipc_perm *p[0]; // 柔性数组 }; // 3. 具体 IPC 资源结构以消息队列为例 struct msg_queue { struct kern_ipc_perm q_perm; // 放在第一个兼容基类 long q_stime; // 发送时间 long q_rtime; // 接收时间 struct list_head q_messages; // 消息链表 // ... 其他特有字段 }; struct sem_array { struct kern_ipc_perm sem_perm; // 同样放在第一个 struct sem *sem_base; // 信号量数组 int sem_nsems; // ... }; struct shmid_kernel { struct kern_ipc_perm shm_perm; // 同样放在第一个 struct file *shm_file; // 共享内存文件 int id; // ... };所有结构体以相同字段开头强制类型转换实现继承3.3 什么是柔性数组// 传统固定数组浪费空间或不够 struct fixed { int cnt; char buffer[100]; // 固定100字节可能浪费或不够 }; // 柔性数组运行时决定大小 struct flexible { int cnt; char buffer[0]; // 或 char buffer[]; (C99标准) // 不占空间只是一个标记 };char buffer[0];不占空间只是一个标记IPC 用柔性数组节省内存不需要指针间接跳转数组和结构体在一起缓存友好访问p[i]时数组数据在附近命中率高动态扩容可以realloc重新分配更大的空间总结对比IPC 类型主要作用是否传输数据同步机制生命周期典型函数消息队列有类型的数据块是自带类型随内核msgget/msgsnd/msgrcv共享内存最快的数据交换是需额外锁随内核shmget/shmat/shmdt信号量同步与互斥否原子 P/V随内核semget/semop/semctl匿名管道亲缘进程流式数据是自动同步随进程pipe命名管道任意进程流式数据是自动同步随文件系统mkfifo/open/read/write

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2482752.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…