恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

TLPI 第45章 读书笔记:Introduction to System V IPC

  • 首页
  • 资讯中心
  • /
  • TLPI 第45章 读书笔记:Introduction to System V IPC

相关资讯

开源AI代码仓库潜伏式污染攻击:手法拆解与防御指南 2026/10/17 2:39:16
GPU加速破解RAR/ZIP压缩包密码:从原理到实战 2026/10/17 2:39:16
PM Skills Marketplace 完整指南:3 步装好 9 个产品插件,把 68 个结构化技能装进你的 AI 2026/10/17 2:34:16

最新资讯

AI Agent开源项目实战:从入门跑通到改造提升的完整路径
动态场景与大规模三维重建:从NeRF到3D高斯泼溅的工程实践
复杂任务下AI Agent编排层设计:职责、模式与工程实践
STM32F4xx_DSP_StdPeriph_Lib_V1.7.1配置详解:从解压到点灯+FFT验证
从Fork到PR:开源贡献完整实操指南
国际高校数学建模竞赛Problem B实战:用熵权TOPSIS与蒙特卡洛预测全球AI竞争格局

今日推荐

Qwen3.5-122B-A10B-singprobe幻觉检测实战:AUC 0.7806,如何实时揪出大模型一本正经的胡话?
NetSpot 实战:用信道分析与频宽优化解决 Wi-Fi 卡顿
C++ split() 字符串分割的五种实现与性能实测

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

TLPI 第45章 读书笔记:Introduction to System V IPC

发布时间:2026/10/17 2:39:16
TLPI 第45章 读书笔记:Introduction to System V IPC 笔记和练习博客总目录见开始读TLPI。System V IPC 是用来指三种不同进程间通信机制的术语消息队列可以用来在进程之间传递消息。消息队列有点像管道但有两个重要区别。首先消息边界会被保留所以读写操作是以消息为单位进行的而不是通过无界的字节流。其次每条消息都包含一个整数类型字段可以按类型选择消息而不是按写入顺序读取。信号量允许多个进程同步它们的操作。信号量是由内核维护的整数值对具有必要权限的所有进程可见。一个进程通过对信号量的值进行适当修改向其他进程表示它正在执行某些操作。共享内存使多个进程能够共享同一块内存区域称为段即同一页面框架被映射到多个进程的虚拟内存中。由于访问用户空间内存的操作速度很快共享内存是最快的 IPC 方法之一一旦一个进程更新了共享内存其他共享同一段的进程马上就能看到变化。虽然这三种 IPC 机制在功能上相当多样但将它们放在一起讨论是有充分理由的。一个原因是它们是在同一时期开发的最早出现在 1970 年代末的 Columbus UNIX 中。这是贝尔内部用于电话公司记录管理和事务处理系统的 UNIX 实现。大约在 1983 年这些 IPC 机制通过出现在 System V 中进入主流 UNIX因此得名 System V IPC。讨论 System V IPC 机制的一个更重要原因是它们的编程接口具有许多共同特性因此很多概念都适用于所有这些机制。由于 System V IPC 是 SUSv3 为了 XSI 符合性而要求的它有时也被称作 XSI IPC。本章提供了对 System V IPC 机制的概述并详细介绍了三种机制共有的特性。随后几章将分别讨论这三种机制。System V IPC 是一个内核选项可以通过 CONFIG_SYSVIPC 选项进行配置。 System V IPC 通常已在内核中配置以下命令可确认系统是否支持System V IPC# 法1$grepCONFIG_SYSVIPC /boot/config-$(uname-r)CONFIG_SYSVIPCyCONFIG_SYSVIPC_SYSCTLyCONFIG_SYSVIPC_COMPATy# 法2$ ipcs ------ Message Queues -------- key msqid owner perms used-bytes messages ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status ------ Semaphore Arrays -------- key semid owner perms nsems# 法3$ls/proc/sysvipc msg sem shm# 法4$sysctlkernel.msgmax kernel.msgmni kernel.shmmax kernel.shmall kernel.msgmax8192kernel.msgmni32000kernel.shmmax18446744073692774399kernel.shmall1844674407369277439945.1 API Overview表 45-1 总结了用于处理 System V IPC 对象的头文件和系统调用。 Sys V IPC的帮助可参见sysvipc(7)某些实现要求在包含表 45-1 中显示的头文件之前先包含 sys/types.h。一些较老的 UNIX 实现可能还需要包含 sys/ipc.h。单一 UNIX 规范的任何版本都不要求这些头文件。在大多数实现了 Linux 的硬件架构上单个系统调用 (ipc(2)) 充当所有 System V IPC 操作进入内核的入口点而表 45-1 中列出的所有调用实际上都是作为库函数实现的它们建立在这个系统调用之上。有两个例外是 Alpha 和 IA-64在这些架构上表中列出的函数确实是作为单独的系统调用实现的。这种有点不寻常的方法其实是 System V IPC 最初作为可加载内核模块实现的产物。虽然在大多数 Linux 架构上它们实际上是库函数但在本章中我们仍将表 45-1 中的函数称为系统调用。只有 C 库的实现者才需要使用 ipc(2)任何其他在应用程序中的使用都是不具备可移植性的。Table 45-1: Summary of programming interfaces for System V IPC objectsInterfaceMessage queuesSemaphoresShared memoryHeader filesys/msg.hsys/sem.hsys/shm.hAssociated data structuremsqid_dssemid_dsshmid_dsCreate/open objectmsgget()semget()shmget()shmat()Close object(none)(none)shmdt()Control operationsmsgctl()semctl()shmctl()Performing IPCmsgsnd()—write messagemsgrcv()—read messagesemop()—test/adjust semaphoreaccess memory in shared regionCreating and opening a System V IPC object每个 System V IPC 机制都有一个相关的 get 系统调用msgget()、semget() 或 shmget()这类似于用于文件的 open() 系统调用。给定一个整数键类似于文件名get 调用会创建一个具有给定键的新 IPC 对象并返回该对象的唯一标识符或者返回具有给定键的现有 IPC 对象的标识符。我们宽泛地将第二种用法称为打开现有 IPC 对象。在这种情况下get 调用所做的只是将一个数字键转换为另一个数字标识符。在 System V IPC 的上下文中对象并不带有任何与面向对象编程相关的含义。这个术语仅用于区分 System V IPC 机制和文件。虽然文件和 System V IPC 对象之间有一些类比但 IPC 对象的使用在几个重要方面与标准 UNIX 文件 I/O 模型不同这在使用 System V IPC 机制时会带来一些麻烦。IPC 标识符类似于文件描述符因为它在后续所有系统调用中都用于引用 IPC 对象。不过它们之间有一个重要的语义区别。文件描述符是进程的属性而 IPC 标识符是对象本身的属性并且在整个系统中可见。所有访问同一对象的进程使用相同的标识符。这意味着如果我们知道一个 IPC 对象已经存在并且有其他方式知道该对象的标识符就可以跳过 get 调用。例如创建对象的进程可以将标识符写入一个文件然后其他进程可以读取该文件。下面的例子展示了如何创建一个 System V 消息队列idmsgget(key,IPC_CREAT|S_IRUSR|S_IWUSR);if(id-1)errExit(msgget);像所有 get 调用一样第一个参数是 key函数返回值是标识符。我们在 get 调用的最后一个参数flags中指定新对象的权限使用的位掩码常量和文件相同表 15-4第 295 页。在上面的例子中只有对象的拥有者被允许读写消息。进程 umask第 15.4.6 节不会应用到新创建的 IPC 对象的权限上。一些 UNIX 实现定义了以下 IPC 权限位掩码常量MSG_R、MSG_W、SEM_R、SEM_A、SHM_R 和 SHM_W。这些对应于每种 IPC 机制的拥有者用户读写权限。要获取相应的组和其他权限位掩码可以将这些常量分别右移 3 和 6 位。这些常量未由 SUSv3 指定SUSv3 使用和文件相同的位掩码并且在 glibc 头文件中未定义。每个希望访问同一 IPC 对象的进程都需要调用 get指定相同的 key以获取该对象的相同标识符。我们将在第 45.2 节讨论如何为应用程序选择 key。如果当前没有与给定键对应的 IPC 对象并且 flags 参数中指定了 IPC_CREAT类似于 open() 的 O_CREAT 标志那么 get 调用会创建一个新的 IPC 对象。如果当前没有对应的 IPC 对象并且没有指定 IPC_CREAT并且该键也不是指定为 IPC_PRIVATE如第 45.2 节所述那么 get 调用会因 ENOENT 错误而失败。进程可以通过指定 IPC_EXCL 标志类似于 open() 的 O_EXCL 标志来确保自己是创建该 IPC 对象的进程。如果指定了 IPC_EXCL而与给定键对应的 IPC 对象已经存在那么 get 调用会因 EEXIST 错误而失败。IPC object deletion and object persistence每种 System V IPC 机制的 ctl 系统调用msgctl()、semctl()、shmctl()都对对象执行一系列控制操作。这些操作中有很多是特定于 IPC 机制的但也有一些是所有 IPC 机制通用的。一个通用控制操作的例子是 IPC_RMID它用于删除对象。例如我们可以使用以下调用删除一个共享内存对象if(shmctl(id,IPC_RMID,NULL)-1)errExit(shmctl);对于消息队列和信号量IPC 对象的删除是立即生效的对象中的任何信息都会被销毁无论是否还有其他进程正在使用该对象。这是 System IPC 对象的操作与文件操作不完全类似的诸多方面之一。在第 18.3 节中我们看到如果删除文件的最后一个链接文件实际上只有在所有引用该文件的打开文件描述符关闭后才会被真正移除。共享内存对象的删除方式有所不同。在调用 shmctl(id, IPC_RMID, NULL) 后只有当所有使用该段的进程都分离它使用 shmdt()之后共享内存段才会被移除。这更类似于文件删除的情况。System V IPC 对象在内核中是持久存在的。一旦创建它会一直存在直到被显式删除或系统关闭。这一特性对 System V IPC 对象来说有其优势。一个进程可以创建一个对象修改其状态然后退出让该对象供后续启动的进程访问。但是这也有一些潜在的劣势系统对每种类型的 IPC 对象数量都有限制。如果我们未能删除未使用的对象可能会因为达到限制而遇到应用错误。当删除消息队列或信号量对象时多进程应用可能不容易确定哪个进程将是最后一个需要访问该对象的进程也就无法安全地删除对象。问题在于这些对象是无连接的——内核不记录哪些进程打开了对象。这个劣势对共享内存段不适用因为它们的删除语义不同如上所述。45.2 IPC KeysSystem V IPC 键是用数据类型 key_t 表示的整数值。IPC 的 get 调用会将一个键转换为对应的整数 IPC 标识符。这些调用确保如果我们创建一个新的 IPC 对象那么该对象将具有唯一的标识符如果我们指定一个现有对象的键我们总是能获得该对象的相同标识符。在内部内核维护数据结构将每种 IPC 机制的键映射到标识符如第 45.5 节所述。那么我们如何提供一个唯一的键——保证不会意外地获得被其他应用程序使用的现有 IPC 对象的标识符呢有三种可能的方法随机选择一个整数键值通常放在所有使用该 IPC 对象的程序都会包含的头文件中。这种方法的难点在于我们可能会不小心选择一个被其他应用使用的值。在创建 IPC 对象时指定 IPC_PRIVATE 常量作为 get 调用的键值这总是会创建一个新的 IPC 对象并保证其键是唯一的。使用 ftok() 函数生成一个可能唯一的键。通常的做法是使用 IPC_PRIVATE 或 ftok()。Generating a unique key with IPC_PRIVATE在创建一个新的 IPC 对象时可以将 key 指定为 IPC_PRIVATE如下所示idmsgget(IPC_PRIVATE,S_IRUSR|S_IWUSR);在这种情况下不需要指定 IPC_CREAT 或 IPC_EXCL 标志。这种方法在多进程应用中尤其有用比如父进程在执行 fork() 之前创建 IPC 对象这样子进程就会继承该 IPC 对象的标识符。我们也可以在客户端-服务器应用中使用这种方法即涉及不相关进程的应用但客户端必须有办法获取服务器创建的 IPC 对象的标识符反之亦然。例如在创建 IPC 对象后服务器可以将其标识符写入一个文件客户端可以读取该文件。Generating a unique key with ftok()ftok()文件到键函数返回一个适合在后续调用 System V IPC 获取系统调用中使用的键值。#includesys/ipc.hkey_tftok(char*pathname,intproj);Returns integer key on success,or –1on error这个键值是通过使用实现定义的算法从提供的路径名和 proj 值生成的。SUSv3 对此有以下要求算法只使用 proj 的最低有效8位。应用程序必须确保路径名指向一个存在的文件以便可以对其使用 stat()否则ftok() 返回 –1。如果提供给 ftok() 的不同路径名链接指向同一个文件即同一个 i-node并且 proj 值相同那么必须返回相同的键值。换句话说ftok() 使用 i-node 号码而不是文件名来生成键值。因为 ftok() 的算法依赖于 i-node 号码所以在应用程序运行期间不应删除并重新创建该文件因为文件可能会被重新创建为不同的 i-node 号码。proj 值的用途只是为了允许我们从同一个文件生成多个键这在应用程序需要创建多个相同类型的 IPC 对象时很有用。历史上proj 参数的类型是 char且在 ftok() 调用中通常也是这样指定的。SUSv3 对 proj 为 0 时 ftok() 的行为没有规定。在 AIX 5.1 上如果 proj 被指定为 0ftok() 会返回 -1。在 Linux 上这个值没有特殊含义。不过为了可移植性应用程序还是应该避免将 proj 指定为 0这样仍然可以选择其他 255 个值。通常传给 ftok() 的路径名是应用程序中创建或者使用的文件或目录之一协作的进程会传相同的路径名给 ftok()。在 Linux 上ftok() 返回的 key 是一个 32 位的值它是通过取 proj 参数的最低 8 位、包含文件的文件系统设备号即次设备号的最低 8 位以及路径名所指文件的 i-node 编号的最低 16 位组成的。最后两项信息是通过对路径名调用 stat() 获得的。glibc 的 ftok() 算法和其他 UNIX 实现使用的算法类似也有类似的限制两个不同的文件有极小的可能会生成相同的 key 值。这可能发生的原因是不同文件系统上的两个文件的 i-node 号的最低有效位可能相同再加上在一个有多个磁盘控制器的系统上不同磁盘设备可能有相同的次设备号。然而实际上对于不同应用程序来说生成相同 key 值的可能性非常小因此使用 ftok() 来生成 key 仍然是可行的。ftok() 的典型用法如下key_tkey;intid;keyftok(/mydir/myfile,x);if(key-1)errExit(ftok);idmsgget(key,IPC_CREAT|S_IRUSR|S_IWUSR);if(id-1)errExit(msgget);45.3 Associated Data Structure and Object Permissions内核为每个 System V IPC 对象的实例维护一个相关的数据结构。这个数据结构的形式根据 IPC 机制消息队列、信号量或共享内存不同而有所变化并在对应的 IPC 机制的头文件中定义见表 45-1。我们将在接下来的章节中讨论这些数据结构的机制特定细节。当通过适当的 get 系统调用创建 IPC 对象时会初始化该对象的相关数据结构。一旦对象创建完成程序可以使用相应的 ctl 系统调用通过指定操作类型为 IPC_STAT 来获取该数据结构的副本。相反某些数据结构的部分内容可以通过 IPC_SET 操作来修改。除了特定 IPC 对象类型的数据外所有三种 IPC 机制的相关数据结构都包含一个子结构 ipc_perm用于保存用于确定对象权限的信息structipc_perm{key_t__key;/* 由 get 调用提供的键 */uid_tuid;/* 所有者的用户 ID */gid_tgid;/* 所有者的组 ID */uid_tcuid;/* 创建者的用户 ID */gid_tcgid;/* 创建者的组 ID */unsignedshortmode;/* 权限 */unsignedshort__seq;/* 序列号 */};SUSv3 要求显示的所有 ipc_perm 字段除了 __key 和 __seq。然而大多数 UNIX 实现都提供了这些字段的某种版本。uid 和 gid 字段指定 IPC 对象的所有权。cuid 和 cgid 字段保存创建该对象的进程的用户和组 ID。最初相应的用户 ID 和创建者 ID 字段具有相同的值这些值取自调用进程的有效 ID。创建者 ID 是不可变的但所有者 ID 可以通过 IPC_SET 操作更改。下面的代码演示了如何更改共享内存段的 uid 字段其关联的数据结构类型为 shmid_dsstructshmid_dsshmds;if(shmctl(id,IPC_STAT,shmds)-1)/* 从内核获取 */errExit(shmctl);shmds.shm_perm.uidnewuid;/* 更改所有者 UID */if(shmctl(id,IPC_SET,shmds)-1)/* 更新内核副本 */errExit(shmctl);ipc_perm 子结构的 mode 字段保存 IPC 对象的权限掩码。这些权限是通过用于创建对象的 get 系统调用中指定的标志的低 9 位初始化的但随后可以使用 IPC_SET 操作进行更改。和文件一样权限分为三类——所有者也称为用户、组和其他人——并且可以为每类指定不同的权限。不过与文件使用的权限方案有一些显著的不同对于 IPC 对象来说只有读和写权限是有意义的。对于信号量写权限通常被称为更改权限。执行权限没有意义并且在执行大多数访问检查时会被忽略。权限检查是根据进程的有效用户 ID、有效组 ID 以及补充组 ID 来进行的。这与 Linux 中文件系统的权限检查不同文件系统是使用进程的文件系统 ID 来检查的如第 9.5 节所述。关于一个进程在 IPC 对象上被授予权限的具体规则如下如果进程有特权CAP_IPC_OWNER那么这个进程在该 IPC 对象上拥有所有权限。如果进程的有效用户 ID 与 IPC 对象的所有者或创建者用户 ID 匹配那么进程会获得对象为所有者用户定义的权限。如果进程的有效组 ID 或任何附加组 ID 与 IPC 对象的所有者组 ID 或创建者组 ID 匹配那么进程会获得对象为组定义的权限。否则进程会获得对象为其他用户定义的权限。在内核代码中上述测试的构建方式是只有当进程没有通过其他测试获得所需权限时才会执行判断进程是否有特权的测试。这样做是为了避免不必要地设置 ASU 进程计数标志该标志表明进程使用了超级用户权限见第 28.1 节。注意使用 IPC_PRIVATE 键值或存在 IPC_EXCL 标志都不会影响哪些进程可以访问 IPC 对象访问权限仅由对象的所有权和权限决定。如何解释对象的读写权限以及是否需要这些权限取决于对象的类型和正在执行的操作。当执行 get 调用以获取已存在 IPC 对象的标识符时会进行初步权限检查以确定 flags 参数中指定的权限是否与现有对象上的权限兼容。如果不兼容则 get 调用会失败并返回错误 EACCES。除非另有说明这个错误码在下面列出的每种权限被拒绝的情况下都会返回。举个例子假设同一组中有两个不同用户其中一个用户使用下面的调用创建了一个消息队列msgget(key,IPC_CREAT|S_IRUSR|S_IWUSR|S_IRGRP);/* rw-r----- */第二个用户尝试通过以下调用获取这个消息队列的标识符会失败因为该用户没有消息队列的写权限msgget(key,S_IRUSR|S_IWUSR);第二个用户可以通过在 msgget() 调用的第二个参数中指定 0 来绕过这个检查这种情况下只有在程序尝试进行需要写权限的 IPC 操作时例如使用 msgsnd() 发送消息才会发生错误。get 调用是唯一一个不会忽略执行权限的情况。即使对于 IPC 对象来说执行权限没有意义如果在 get 调用中请求一个已存在对象的执行权限也会检查是否授予了该权限。其他常见操作所需的权限如下从对象中检索信息例如从消息队列读取消息、获取信号量的值或附加共享内存段以进行读取访问需要读取权限。更新对象中的信息例如向消息队列写入消息、更改信号量的值或附加共享内存段以进行写入访问需要写入权限。获取与 IPC 对象关联的数据结构的副本IPC_STAT 控制操作需要读取权限。删除 IPC 对象IPC_RMID 控制操作或更改其关联的数据结构IPC_SET 控制操作不需要读或写权限。调用进程必须要么拥有特权CAP_SYS_ADMIN要么其有效用户 ID 必须与对象的拥有者用户 ID 或创建者用户 ID 匹配否则会返回错误 EPERM。可以设置 IPC 对象的权限使得拥有者或创建者无法再使用 IPC_STAT 获取包含对象权限的关联数据结构这意味着该对象不会在第 45.6 节中描述的 ipcs(1) 命令中显示但仍然可以使用 IPC_SET 来更改它们。其他各种特定机制的操作可能需要读取或写入权限或者需要 CAP_IPC_OWNER 能力。我们会在接下来的章节中描述这些操作时注明所需权限。45.4 IPC Identifiers and Client-Server Applications在客户端-服务器应用程序中服务器通常负责创建 System V IPC 对象而客户端只是访问它们。换句话说服务器在执行 IPC 获取调用时会指定 IPC_CREAT 标志而客户端在其获取调用中会省略这个标志。假设客户端与服务器进行长时间对话每个进程都执行多次 IPC 操作例如交换多条消息、一系列信号量操作或多次共享内存更新。如果服务器进程崩溃或被人为停止然后重新启动会发生什么情况呢此时盲目地重用之前服务器进程创建的现有 IPC 对象毫无意义因为新服务器进程对当前 IPC 对象状态的历史信息一无所知。例如消息队列中可能有客户端针对旧服务器进程的早期消息发送的二次请求。在这种情况下服务器唯一的选择可能是放弃所有现有客户端删除前一个服务器进程创建的 IPC 对象并创建新的 IPC 对象实例。新启动的服务器会处理前一个服务器实例可能意外终止的情况方法是在 get 调用中同时指定 IPC_CREAT 和 IPC_EXCL 标志来尝试创建 IPC 对象。如果 get 调用失败因为指定键的对象已存在那么服务器会假设该对象是由旧服务器进程创建的因此它会使用 IPC_RMID 控制操作删除该对象然后再次执行 get 调用来创建对象。这可以与确保当前没有其他服务器进程正在运行的其他步骤结合使用例如第 55.6 节中描述的步骤。对于消息队列这些步骤可能如清单 45-1 所示。Listing 45-1: Cleanup of IPC objects within a server// svipc/svmsg_demo_server.c// 代码略即使重启后的服务器重新创建了 IPC 对象如果总是给 get 调用提供相同的 key 而生成相同的标识符仍然可能存在潜在问题。换个角度从客户端来看这个解决方案如果服务器重新创建的 IPC 对象使用相同的标识符那么客户端将无法知道服务器已经重启并且 IPC 对象不再包含期望的历史信息。为了解决这个问题内核采用了一种算法在下一节中描述通常可以确保即使使用相同的 key 创建新的 IPC 对象时该对象的标识符也会不同。因此任何试图使用旧标识符的旧服务器进程的客户端都会从相关的 IPC 系统调用收到错误。像清单 45-1 中显示的解决方案并不能完全解决使用 System V 共享内存时识别服务器重启的问题因为共享内存对象只有在所有进程都从其虚拟地址空间分离后才会被删除。不过通常共享内存对象会与 System V 信号量一起使用而信号量会在 IPC_RMID 操作时立即被删除。这意味着当客户端进程尝试访问被删除的信号量对象时就会知道服务器已经重启了。45.5 Algorithm Employed by System V IPC get Calls图45-1显示了内核用于表示 System V IPC 对象信息的一些内部结构在本例中是信号量但其他 IPC 机制的细节类似包括用于计算 IPC 键的字段。对于每种 IPC 机制共享内存、消息队列或信号量内核都会维护一个相关的 ipc_ids 结构用来记录该 IPC 机制所有实例的各种全局信息。这些信息包括一个动态大小的指针数组 entries用于指向每个对象实例的相关数据结构信号量情况下为 semid_ds 结构。entries 数组的当前大小记录在 size 字段中而 max_id 字段保存的是当前正在使用的最大元素的索引。Figure 45-1: Kernel data structures used to represent System V IPC (semaphore) objects当进行 IPC 获取调用时Linux 上使用的算法其他系统使用类似算法大致如下搜索与之关联的数据结构列表由 entries 数组的元素指向寻找 key 字段即sem_perm.__key与 get 调用中指定的匹配的数据结构。a) 如果未找到匹配项且未指定 IPC_CREAT则返回错误 ENOENT。b) 如果找到匹配项但同时指定了 IPC_CREAT 和 IPC_EXCL则返回错误 EEXIST。c) 否则如果找到匹配项则跳过下一步。如果未找到匹配项且指定了 IPC_CREAT则分配并初始化一个新的特定机制关联数据结构如图 45-1 中的 semid_ds。这还涉及更新 ipc_ids 结构的各个字段并可能涉及调整 entries 数组的大小。新结构的指针放置在 entries 的第一个空闲元素中。此初始化包括两个子步骤a) 将 get 调用中提供的 key 值复制到新分配结构的 xxx_perm.__key 字段中。b) 将 ipc_ids 结构中 seq 字段的当前值复制到关联数据结构的 xxx_perm.__seq 字段并将 seq 字段加1。IPC 对象的标识符使用以下公式计算identifierindexxxx_perm.__seq*SEQ_MULTIPLIER 这个identifier即IPC对象的ID也就是xxxget返回的值例如shmget。在用于计算 IPC 标识符的公式中index 是该对象实例在 entries 数组中的索引而 SEQ_MULTIPLIER 是一个常量定义为 32,768在内核源文件 include/linux/ipc.h 中对应 IPCMNI。例如在图 45-1 中具有键值 0x4b079002 的信号量生成的标识符为 (2 5 * 32,768) 163,842。关于 get 调用使用的算法有以下几点需要注意• 即使使用相同的键创建了一个新的 IPC 对象它几乎肯定会有不同的标识符因为标识符是基于保存在相关数据结构中的 seq 值计算的而每创建一个此类型的对象该值就会增加 1。内核中的算法会在 seq 值达到 (INT_MAX / IPCMNI)也就是 2,147,483,647 / 32,768 65,535 时将其回绕为 0。因此如果在此期间创建了 65,535 个对象并且新对象在 entries 数组中复用了之前对象的元素即该元素在此期间必须也被释放新创建的 IPC 对象可能会与之前的对象有相同的标识符。不过这种情况发生的几率很小。• 该算法为 entries 数组的每个索引生成一组独特的标识符值。• 由于常量 IPCMNI 定义了每种类型的 System V 对象数量上限该算法保证每个现有的 IPC 对象都有唯一的标识符。• 给定一个标识符值可以使用以下公式快速计算出对应的 entries 数组索引indexidentifier%SEQ_MULTIPLIER能够快速执行这个计算对于高效运行那些提供了 IPC 对象标识符的 IPC 系统调用是必要的也就是表 45-1 中除了 get 调用以外的那些调用。顺便提一下如果进程进行一个 IPC 系统调用例如 msgctl()、semop() 或 shmat()并指定了一个不存在的对象标识符可能会产生两种不同的错误。如果对应条目的索引为空就会出现 EINVAL 错误。如果索引指向一个关联的数据结构但该结构中存储的序列号没有生成相同的标识符值那么就认为这个数组索引指向的旧对象已经被删除并且索引被重用了。这种情况会产生 EIDRM 错误。45.6 The ipcs and ipcrm Commandsipcs 和 ipcrm 命令是 System V IPC 中对应于 ls 和 rm 文件命令的命令。使用 ipcs我们可以获取系统上 IPC 对象的信息。默认情况下ipcs 会显示所有对象如下面的例子所示$ ipcs ------ Message Queues -------- key msqid owner perms used-bytes messages ------ Shared Memory Segments -------- key shmid owner perms bytes nattch status 0x000000000oracle60010485760740x000000001oracle6001098907648740x000000002oracle6001505755136740x000000003oracle60012582912740x43b375cc4oracle6006144074------ Semaphore Arrays -------- key semid owner perms nsems 0x8d2a35345oracle6002500x8d2a35356oracle600250在 Linux 上ipcs(1) 只显示我们有读取权限的 IPC 对象的信息而不管我们是否拥有这些对象。在一些 UNIX 实现中ipcs 的行为与 Linux 相同。然而在其他实现中无论用户是否拥有读取权限ipcs 都会显示所有对象。默认情况下对于每个对象ipcs 会显示 key、标识符、所有者以及权限以八进制数表示然后是对象特定的信息• 对于共享内存ipcs 会显示共享内存区域的大小、当前有多少进程将该共享内存区域附加到它们的虚拟地址空间以及状态标志。状态标志表示该区域是否已锁定到 RAM 中以防止交换第 48.7 节以及该区域是否已被标记为在所有进程分离后销毁。• 对于信号量ipcs 会显示信号量集的大小。• 对于消息队列ipcs 会显示队列中数据的总字节数和消息数。ipcs(1) 手册页记录了显示 IPC 对象其他信息的各种选项。ipcrm 命令用于删除 IPC 对象。该命令的一般形式如下之一$ ipcrm-Xkey $ ipcrm-xid上述命令中我们可以指定 key 为 IPC 对象的 key或者指定 id 为 IPC 对象的标识符字母 x 被替换为大写或小写的 q针对消息队列、s针对信号量或 m针对共享内存。因此我们可以使用以下命令删除标识符为 65538 的信号量集$ ipcrm-s6553845.7 Obtaining a List of All IPC ObjectsLinux 提供了两种非标准方法来获取系统上所有 IPC 对象的列表/proc/sysvipc 目录下的文件列出了所有 IPC 对象使用 Linux 特有的 ctl 调用。我们将在这里介绍 /proc/sysvipc 目录下的文件并将在第 46.6 节讨论 ctl 调用在那里我们会提供一个示例程序用于列出系统上所有的 System V 消息队列。一些其他的 UNIX 实现也有自己非标准的方法来获取所有 IPC 标识符的列表例如Solaris 提供了 msgids()、semids() 和 shmids() 系统调用来实现这个目的。/proc/sysvipc 目录下的三个只读文件提供了与 ipcs 命令相同的信息/proc/sysvipc/msg 列出所有消息队列及其属性。/proc/sysvipc/sem 列出所有信号量集及其属性。/proc/sysvipc/shm 列出所有共享内存段及其属性。与 ipcs 命令不同这些文件总是显示对应类型的所有对象无论是否有对象的读取权限。/proc/sysvipc/sem 文件内容的一个示例如下$cat/proc/sysvipc/sem key semid perms nsems uid gid cuid cgid otime ctime-192661370856002505432154321543215432117919739551791973552-192661370766002505432154321543215432117919735221791973518三个 /proc/sysvipc 文件为程序和脚本提供了一种非可移植的方法用于遍历某种类型的所有现有 IPC 对象列表。通过可移植的方法获取某种类型的所有 IPC 对象列表我们能做到的最好方式就是解析 ipcs(1) 输出。45.8 IPC Limits由于 System V IPC 对象会消耗系统资源内核会对每类 IPC 对象设置各种限制以防止资源耗尽。SUSv3 并没有具体规定对 System V IPC 对象设置限制的方法但大多数 UNIX 实现包括 Linux遵循一个类似的框架来限制可施加的类型。在接下来的章节中当我们讨论每种 System V IPC 机制时会讲到相关的限制并指出与其他 UNIX 实现的不同之处。虽然各种 UNIX 实现对每类 IPC 对象可施加的限制类型大致相似但查看和修改这些限制的方法却不同。接下来的章节描述的方法是 Linux 特有的通常涉及使用 /proc/sys/kernel 目录下的文件其他实现的方法有所不同。在 Linux 上可以使用 ipcs -l 命令来列出每种 IPC 机制的限制。程序也可以使用 Linux 特有的 IPC_INFOctl操作来获取相同的信息。$ ipcs-l------ Messages Limits -------- max queues system wide32000max size of message(bytes)8192default max size of queue(bytes)16384------ Shared Memory Limits -------- max number of segments4096max seg size(kbytes)4294967296max total shared memory(kbytes)4294967296min seg size(bytes)1------ Semaphore Limits -------- max number of arrays128max semaphores per array250max semaphores system wide32000max ops per semop call100semaphore max value3276745.9 SummarySystem V IPC 是三种最早在 System V 中广泛出现的 IPC 机制的统称之后这些机制被移植到大多数 UNIX 系统实现中并被纳入各种标准。这三种 IPC 机制分别是消息队列它允许进程交换消息信号量它允许进程同步访问共享资源以及共享内存它允许两个或两个以上的进程共享相同的内存页。这三种 IPC 机制在 API 和语义上有许多相似之处。对于每种 IPC 机制get 系统调用用于创建或打开一个对象。给定一个整数键get 调用会返回一个整数标识符用于在后续的系统调用中引用该对象。每种 IPC 机制还有一个对应的 ctl 调用用于删除对象以及检索和修改对象关联数据结构中的各种属性例如所有权和权限。用于生成新 IPC 对象标识符的算法设计目的是最小化在对象被删除后标识符被立即重用的可能性即使使用相同的键来创建新对象。这使得客户端-服务器应用程序能够正常运行——重启的服务器进程能够检测并移除其前身创建的 IPC 对象这一操作会使之前服务器进程的客户持有的标识符失效。ipcs 命令列出当前系统上存在的 System V IPC 对象。ipcrm 命令用于删除 System V IPC 对象。在 Linux 上可以通过 /proc/sysvipc 目录下的文件获取系统上所有 System V IPC 对象的信息。每种 IPC 机制都有一组相关的限制可以用来避免由于创建过多 IPC 对象而耗尽系统资源。/proc/sys/kernel 目录下的各种文件可以用来查看和修改这些限制。Further information关于在 Linux 上实现 System V IPC 的信息可以在 [Maxwell, 1999] 和 [Bovet Cesati, 2005] 中找到。[Goodheart Cox, 1994] 描述了 System V Release 4 的 System V IPC 实现。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号