当前位置: 首页 > news >正文

进程通信————system V 消息队列 信号量

1.消息队列

1.1 原理

1.1.1 核心背景:进程通信的基础

进程通信的前提是让不同进程看到同一份资源,资源的形式决定了具体的通信方式。例如:

  • 资源为文件缓冲区时,对应特定通信方式
  • 资源为内存块时,对应另一种通信方式

1.1.2 消息队列的定义与特点

资源形式:由操作系统提供的消息队列

  • 功能 1:使进程双方能看到同一个队列,为通信提供基础
  • 功能 2:允许不同进程向内核发送带类型(用于区分发送方)的数据块

1.1.3 消息队列的工作流程

进程将经系统处理好的数据块通过操作系统列入队列,其他进程则可从队列中获取发给自己的数据块,以此实现进程间以数据块形式发送数据的通信。

1.1.4 消息队列的管理方式

操作系统通过 “先描述,再组织” 的方式对消息队列进行管理(即先对消息队列的属性等进行描述,再通过特定结构组织这些描述信息,实现有效管理)。

1.2 消息队列的属性

1.2.1 struct msqid_ds 结构体

专门用于描述消息队列的属性,是操作系统管理消息队列的 “描述” 基础。

struct msqid_ds {struct ipc_perm msg_perm;   /* 消息队列的权限信息(ipc_perm 结构体) */time_t          msg_stime;  /* 最后一次调用 msgsnd 发送消息的时间 */time_t          msg_rtime;  /* 最后一次调用 msgrcv 接收消息的时间 */time_t          msg_ctime;  /* 最后一次修改队列属性的时间(如 msgctl(IPC_SET)) */unsigned long   __msg_cbytes;/* 队列中所有消息的总字节数(系统内部维护) */msgqnum_t       msg_qnum;   /* 队列中当前的消息数量 */msglen_t        msg_qbytes; /* 队列允许的最大字节数(容量上限) */pid_t           msg_lspid;  /* 最后一次调用 msgsnd 的进程 ID */pid_t           msg_lrpid;  /* 最后一次调用 msgrcv 的进程 ID */unsigned long   __unused4;  /* 未使用 */unsigned long   __unused5;  /* 未使用 */
};

1.2.2 struct ipc_perm 结构体

存储所有 System V IPC 对象(包括消息队列、共享内存、信号量)的通用权限信息,是struct msqid_ds的成员之一。

struct ipc_perm {key_t          __key;       /* 消息队列的键值(由 msgget 传入) */uid_t          uid;         /* 所有者的用户 ID */gid_t          gid;         /* 所有者的组 ID */uid_t          cuid;        /* 创建者的用户 ID */gid_t          cgid;        /* 创建者的组 ID */unsigned short mode;        /* 权限模式(类似文件权限,如 0666) */unsigned short __seq;       /* 序列号(系统内部用于标识对象) */unsigned long  __unused1;   /* 未使用(预留字段) */unsigned long  __unused2;   /* 未使用(预留字段) */
};

1.3 消息队列相关命令

1.查看消息队列信息:

ipcs -q
  • 功能:显示当前系统中存在的消息队列相关信息。

2.删除指定消息队列:

ipcrm -q [msgid]
  • 功能:根据消息队列的 ID(msgid),删除对应的消息队列。
  • 说明:需将命令中的msgid替换为实际要删除的消息队列的 ID。

2.信号量

2.1 原理

2.1.1 共享内存的局限

共享内存通过让多个进程直接访问同一块物理内存实现高效通信,但因缺乏同步机制,可能导致数据不一致。

例如:多个进程同时读写时,A 正在写入且未完成,部分数据被 B 读取,导致双方收发的数据一致。

2.1.2 重要概念

  1. 共享资源及其问题:多个进程能看到的同一份资源,若不保护会导致数据不一致。
  2. 加锁(互斥):解决共享资源问题的办法,保证任何时候只允许一个执行流访问共享资源。
  3. 临界资源:任何时候只允许一个执行流访问(执行访问代码)的共享资源,通常是一段由操作系统或用户维护的内存空间。
  4. 临界区:访问临界资源的代码(例如 100 行代码中,可能专门用于访问临界资源的代码就几行)。

2.1.3 现象解释:多进程 / 线程并发打印内容错乱

问:多进程/多线程并发循环打印时,为什么显示器上的内容会错乱,有时还和命令行混合在一起?
答:因为显示器可看作一个文件,数据先写入其缓冲区再刷新显示。多进程/线程会同时访问作为共享资源的显示器,若没有互斥或保护机制,各进程/线程的输出数据会在缓冲区相互干扰,导致内容错乱,甚至与命令行内容混合。

2.1.4 信号量的理解

  • 本质:计数器(类似int cnt;)。
  • 作用:描述临界资源中资源的数量。
  • 工作逻辑:在计算机中,临界资源被划分为多个小块。并发执行访问时,若多个执行流的数量超过临界资源小块的数量,就可能出现多个执行流访问同一小块资源的情况,进而导致数据不一致。为避免这种问题,引入计数器cnt:每当一个执行流要访问资源时,cnt就减1(表示申请资源);当cnt为0时,说明资源已被申请完毕,后续执行流需等待,直到有执行流释放资源后再进行分配。
  • 结论
    1. 权限标识:计数器申请成功,即表示该执行流获得了访问资源的权限。
    2. 预定机制:申请计数器资源后,执行流并未立即访问共享资源,因此计数器本质是对资源的一种预定机制。
    3. 数量控制:计数器能有效限制进入共享资源的执行流数量,避免因并发访问导致的数据不一致问题。
    4. 访问前提:每个执行流若要访问共享资源的一部分,不能直接访问,必须先申请计数器。

2.1.5 二元信号量

  • 当临界资源只有一份时,计数器未被申请时为 1,申请后为 0,只能取 1 和 0 两态,称为二元信号量,本质是一把锁。
  • 此时临界资源作为整体被申请和释放,而非分成多块。

2.1.6 信号量计数器的安全问题

要访问临界资源需申请信号量计数器资源,这意味着信号量计数器本身也是共享资源,而信号量要保证自身不被同时申请,关键在于确保计数器的减减操作安全——整数减减操作并不安全,因为C语言中一条cnt减减语句在汇编层面会分解为三步:

  1. 将cnt从内存移到 CPU 寄存器
  2. 在 CPU 内执行减减操作
  3. 将结果写回内存中的cnt

进程运行中可能随时切换,若在上述步骤中切换,会导致多个执行流同时访问cnt,引发减减操作异常。

2.1.7 信号量操作(PV 操作)的原子性

  • P 操作:申请信号量,本质是对计数器做减减操作。
  • V 操作:释放资源,本质是对计数器做加加操作。
  • 原子性:操作只有 “未做” 和 “做完” 两种状态,没有 “正在做” 的中间状态(技术上,单条语句执行具有原子性)。

2.1.8 信号量作为进程通信方式的原因

  • 通信不仅包括数据传递,进程间的协同也是一种通信。
  • 协同的本质是通信,因此信号量需被所有通信进程可见。

2.2 信号量属性

2.2.1 struct semid_ds 结构体

专门用于描述 System V 信号量集的属性信息,包含信号量集的权限、操作时间、信号量数量等关键信息,是系统管理信号量集的核心数据结构。

struct semid_ds {struct ipc_perm sem_perm;  /* 信号量集的通用权限信息(继承自 ipc_perm) */time_t          sem_otime; /* 最后一次执行 semop 操作(信号量操作)的时间 */time_t          sem_ctime; /* 最后一次修改信号量集属性的时间(如 semctl 操作) */unsigned short  sem_nsems; /* 信号量集中包含的信号量数量 *//* 可能包含其他系统预留字段(如 __unused 等) */
};

2.2.2 struct ipc_perm 结构体

存储所有 System V IPC 对象(包括信号量、消息队列、共享内存)的通用权限信息,是 struct semid_ds(以及 struct msqid_dsstruct shmid_ds)的成员之一,用于统一管理 IPC 对象的所有权和访问权限。

struct ipc_perm {key_t          __key;       /* IPC 对象的键值(由创建时传入,如 semget 的 key 参数) */uid_t          uid;         /* 所有者的用户 ID */gid_t          gid;         /* 所有者的组 ID */uid_t          cuid;        /* 创建者的用户 ID */gid_t          cgid;        /* 创建者的组 ID */unsigned short mode;        /* 权限模式(类似文件权限,如 0666 表示所有者、组、其他用户均可读写) */unsigned short __seq;       /* 序列号(系统内部用于唯一标识 IPC 对象) *//* 可能包含预留的未使用字段(如 __unused1、__unused2 等) */
};

2.3 IPC 在内核中的数据结构设计笔记

2.3.1 IPC 资源的整合与核心管理结构

操作系统将所有 IPC 资源(共享内存、消息队列、信号量等)整合在 IPC 模块中,管理这些资源实际是管理对应的结构体(semid_ds、msqid_ds、shmid_ds等)。

核心管理方式:通过struct ipc_perm* array[]数组实现管理,对 IPC 资源的增删查改操作均转化为对该数组的相应操作。

2.3.2 数组的资源存储与唯一性确认

  • 创建资源时,对应结构体的第一个字段均为struct ipc_perm类型(如共享内存shmid_dsshm_perm),该字段的地址会存入数组,数组下标即为资源的 ID(shmidmsgidsemid)。
  • 对 IPC 资源的增删查改,实际转化为对该数组的相应操作。

资源定位与唯一性确认:进程通过用户层的_key定位资源,遍历数组并比较每个资源ipc_perm字段中的_key,确认是否为目标资源。

2.3.3 资源访问与类型区分机制

  • 字段访问方式:不同资源类型的ID可能会出现冲突,当要访问某个资源时,例如以ipc_perm array[0]为例,若想访问shmid_ds里的shm_atime,由于ipc_perm array[0]中存放了对应资源第一个字段(即struct ipc_perm类型)的地址,而该字段是shmid_ds结构的第一个字段,因此可以通过将该地址强转为struct shmid_ds*类型的指针,进而访问到其中的shm_atime字段。
  • 类型区分依据:数组之所以能知道要强转成什么类型,是因为ipc_perm在内核层对应的kern_ipc_perm结构中包含mode选项,通过在ipc_perm中添加类型标志,代码就能区分它所代表的是哪种IPC资源,因此msgget、shmget、semget返回的其实是ipc_perm指针数组的下标,这类似于C++中的多态机制。

2.3.4 IPC 资源 ID 的特点

IPC资源返回的ID与文件描述符不同,其数值可大可小,这是因为ID来源于操作系统维护的一个独立数组(不隶属于进程,无法与进程强关联),该数组的下标呈线性递增,当大到一定程度时会绕回零。

http://www.dtcms.com/a/303285.html

相关文章:

  • 卡内基梅隆大学提出Human2LocoMan:基于人类预训练的四足机器人「多功能操作学习框架」
  • sqlite3学习---基础知识、增删改查和排序和限制、打开执行关闭函数
  • AAAI 2025多模态重大突破:SENA框架重塑多模态学习,零标注实现自进化
  • 【Python】—— 语法糖
  • 求两数之和
  • R语言与作物模型(以DSSAT模型为例)融合应用高级实战技术
  • window显示驱动开发—Direct3D 11 视频设备驱动程序接口 (DDI)
  • 图片上传 el+node后端+数据库
  • 数据库事务中的陷阱:脏读、幻读与不可重复读
  • 第四章:分析 Redis 性能高原因和核心字符串类型命令
  • 特性阻抗的近似计算
  • 【Linux】协议——TCP/IP协议
  • PTX指令集基础以及warp级矩阵乘累加指令介绍
  • 5G MBS(组播广播服务)深度解析:从标准架构到商用实践
  • 机器学习(重学版)基础篇(算法与模型一)
  • Ansible提权sudo后执行报错
  • 大语言模型(LLM):重塑AI未来的技术革命
  • 11-大语言模型—Transformer 盖楼,BERT 装修,RoBERTa 直接 “拎包入住”|预训练白话指南
  • 【笔记】Handy Multi-Agent Tutorial 第四章 CAMEL框架下的RAG应用(实践)
  • 四、计算机组成原理——第5章:存储系统
  • C++20实战FlamingoIM开发
  • Vue插件与组件核心区别详解
  • 重复文件清理工具,附免费链接
  • 1.2.3 混凝土结构设计构造要求
  • Cesium大气散射效果
  • Datawhale AI夏令营:大模型理解列车排期表
  • Vue组件通信的终极指南
  • Spark SQL 聚合函数:collect_list 与 collect_set 用法详解
  • ADAS测试:如何用自动化手段提升VV效率
  • 04动手学深度学习(下)