当前位置:首页 > 技术 > 正文内容

深入理解进程间通信:信号量机制

访客 技术 2026年8月8日 1

信号量概述

信号量(Semaphore)是进程间通信(IPC)机制中的一种同步工具,其本质是一个整型计数器,用于控制多个并发进程对共享资源的访问。它通过维护一个可用的资源计数,实现对共享资源的互斥访问和进程间的同步协调。当信号量的值大于零时,表示有可用资源;当值为零时,则无可用资源。

在操作信号量时,通常涉及两种原子操作:

  • P 操作(Wait / Decrement): 尝试获取资源。如果信号量值大于0,则将其减1并继续执行;否则,进程将被阻塞,直到有资源可用。
  • V 操作(Signal / Increment): 释放资源。将信号量值加1。如果有进程因等待该信号量而被阻塞,则唤醒其中一个。

为了更有效地管理多个相关共享资源,操作系统引入了信号量集的概念。一个信号量集可以包含一个或多个信号量,允许应用程序对多个信号量进行一次性原子操作。

信号量集属性结构

每个信号量集在系统内部都有一个关联的控制结构,通常是 struct semid_ds,它包含了信号量集的元数据信息:

#include <sys/sem.h> // 注意:原始文章中的 <sysm.h> 应为 <sys/sem.h>

struct semid_ds
{
    struct ipc_perm sem_perm;  // IPC权限结构体
    unsigned short  sem_nsems; // 信号量集中信号量的数量
    time_t          sem_otime; // 最后一次 semop 操作的时间
    time_t          sem_ctime; // 最后一次 semctl 修改的时间
};

其中,sem_perm 成员是一个 struct ipc_perm 结构体,用于定义信号量集的访问权限。

创建或获取信号量集

使用 semget 函数可以创建一个新的信号量集,或者获取一个已存在的信号量集的标识符(ID):

#include <sys/sem.h>

int semget(key_t ipc_key, int num_semaphores, int access_flags);
// 返回值:成功返回信号量集ID,失败返回 -1
  • ipc_key:用于标识信号量集的键值。可以是应用程序指定的非零值(通过 ftok 生成),也可以是 IPC_PRIVATE(0),表示创建一个只对当前进程及其子进程可见的私有信号量集。
  • num_semaphores:如果正在创建新的信号量集,此参数指定集中信号量的数量。如果获取已存在的信号量集,此参数通常设为 0。
  • access_flags:权限标志位,用于控制信号量集的访问权限。例如,IPC_CREAT 用于创建信号量集,IPC_EXCLIPC_CREAT 结合使用可确保创建新的信号量集(如果已存在则报错),以及读写权限如 0666

控制信号量集

semctl 函数提供了对信号量集进行各种控制操作的能力,包括查询状态、设置属性或直接删除信号量集:

#include <sys/sem.h>

int semctl(int sem_id, int sem_index, int command, ... /* union semun arg */);
// 返回值:成功返回 0,失败返回 -1

semctl 函数通常与一个联合体 union semun 配合使用,这个联合体在不同的命令下用于传递不同类型的数据:

// 在某些系统上,union semun 需要在调用代码中定义
union semun {
    int              val;    // 用于 SETVAL 命令
    struct semid_ds *buf;    // 用于 IPC_STAT, IPC_SET 命令
    unsigned short  *array;  // 用于 GETALL, SETALL 命令
    struct seminfo  *__buf;  // 用于 IPC_INFO 命令
};
  • sem_id:要操作的信号量集ID。
  • sem_index:信号量集中的某个信号量的索引(从 0 开始)。如果命令作用于整个信号量集,此参数通常设为 0。
  • command:指定要执行的操作,常见的命令包括:
    • IPC_STAT:获取信号量集的 semid_ds 结构,并将其内容存储在 arg.buf 指向的结构中。
    • IPC_SET:根据 arg.buf 指向的结构中的值来设置信号量集的 sem_perm 字段。
    • IPC_RMID:从系统中删除指定的信号量集及其所有关联数据。
    • GETVAL:获取指定信号量(由 sem_index 标识)的当前值。
    • SETVAL:设置指定信号量(由 sem_index 标识)的值为 arg.val
    • GETALL:获取信号量集中所有信号量的值,并存储到 arg.array 指向的数组中。
    • SETALL:设置信号量集中所有信号量的值,从 arg.array 指向的数组中读取。
  • ... /* union semun arg */:这是一个可选参数,根据 command 的不同,它被解释为 union semun 类型的变量,用于传递或接收数据。

信号量集操作

semop 函数用于对信号量集中的一个或多个信号量执行 P 或 V 操作,这些操作是原子性的:

#include <sys/sem.h>

int semop(int sem_id, struct sembuf *operation_list, size_t num_operations);
// 返回值:成功返回 0,失败返回 -1

semop 函数通过一个 struct sembuf 数组来定义要执行的单个或批量操作:

struct sembuf
{
    unsigned short sem_num; // 信号量集中目标信号量的编号(从 0 开始)
    short          sem_op;  // 对信号量执行的操作值
    short          sem_flg; // 操作标志
};
  • sem_id:要操作的信号量集ID。
  • operation_list:指向 struct sembuf 结构体数组的指针,每个结构体描述一个对信号量的操作。
  • num_operationsoperation_list 数组中包含的操作数量。
  • sem_num:数组中每个结构体用于指定操作的目标信号量在信号量集中的索引。
  • sem_op:定义具体操作:
    • 正数:执行 V 操作,将信号量值增加 sem_op
    • 负数:执行 P 操作,将信号量值减少 -sem_op。如果当前值小于 -sem_op,进程将被阻塞。
    • 零:测试信号量值是否为 0。如果为 0,进程继续;否则,进程将被阻塞直到信号量变为 0。
  • sem_flg:操作行为标志,可选值为:
    • IPC_NOWAIT:如果操作会导致阻塞,则立即返回 EAGAIN 错误,而不是阻塞进程。
    • SEM_UNDO:如果设置了此标志,当进程异常终止时,系统会自动撤销该进程对信号量所做的所有修改,恢复信号量的原始状态,防止资源死锁。

semop 提供的原子性操作和 SEM_UNDO 标志,使得信号量成为实现进程间互斥和同步的强大且可靠的机制。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。