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

Linux System V 信号量详解与进程同步实战

一、前言

在 Linux 多进程编程中,当多个进程需要访问临界资源(同一时刻只能被一个进程访问的资源)时,就必须引入同步机制来避免竞争条件(race condition)。信号量(Semaphore)是经典的进程同步工具之一,它可以实现进程间的互斥(Mutex)和同步(Synchronization)。

本文将详细讲解System V 信号量的核心概念、相关 API,以及一个简单却实用的示例:两个进程通过信号量互斥地向屏幕输出字符“A”和“B”,保证输出不会交错混乱。

二、信号量基础概念
  • 信号量(Semaphore):本质上是一个整数计数器,用于控制对共享资源的访问。
    • P 操作(wait / down):申请资源,信号量值减 1。若值变为负数(或 0),则进程阻塞等待。
    • V 操作(signal / up):释放资源,信号量值加 1。若有进程在等待,则唤醒其中一个。
  • 临界资源:同一时刻只允许一个进程访问的资源(如共享内存、文件、终端输出等)。
  • 临界区:访问临界资源的代码段。

本示例中,我们使用二元信号量(初始值为 1),实现互斥访问终端输出,确保每个进程的 “打印-睡眠-打印” 操作是原子性的。

三、System V 信号量核心函数

主要涉及三个系统调用(头文件 <sys/sem.h>):

  1. semget():创建或获取信号量集。
  2. semctl():控制信号量(如初始化值、删除信号量)。
  3. semop():执行 P/V 操作。

此外,还需要联合体 union semun 来传递参数。

四、代码实现
1. 信号量封装头文件 sem.h

C

// sem.h #ifndef SEM_H #define SEM_H #include <sys/sem.h> union semun { // 用于 semctl 的联合体 int val; }; void sem_init(); // 初始化信号量 void sem_p(); // P 操作(申请资源) void sem_v(); // V 操作(释放资源) void sem_destroy(); // 删除信号量 #endif
2. 信号量实现 sem.c(或直接放在主文件中)

C

// sem.c 或直接包含在 main 前 #include "sem.h" #include <stdio.h> #include <sys/sem.h> static int semid = -1; void sem_init() { semid = semget((key_t)1234, 1, IPC_CREAT | IPC_EXCL | 0600); // 尝试全新创建 if (semid == -1) { // 已存在,则直接获取 semid = semget((key_t)1234, 1, 0600); if (semid == -1) { perror("semget err"); return; } } else { // 首次创建,初始化为 1(二元信号量,实现互斥) union semun a; a.val = 1; if (semctl(semid, 0, SETVAL, a) == -1) { perror("semctl init err"); } } } void sem_p() { struct sembuf buf = {0, -1, SEM_UNDO}; // sem_num=0, sem_op=-1 (P), SEM_UNDO 保证进程异常退出时自动释放 if (semop(semid, &buf, 1) == -1) { perror("semop P err"); } } void sem_v() { struct sembuf buf = {0, 1, SEM_UNDO}; // sem_op=1 (V) if (semop(semid, &buf, 1) == -1) { perror("semop V err"); } } void sem_destroy() { if (semctl(semid, 0, IPC_RMID) == -1) { perror("semctl destroy err"); } }
3. 进程 A(输出 A)

C

// processA.c #include "sem.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { sem_init(); srand(getpid()); // 随机种子更好 for (int i = 0; i < 5; i++) { sem_p(); // 进入临界区 printf("A"); fflush(stdout); int n = rand() % 3; sleep(n); printf("A"); fflush(stdout); sem_v(); // 离开临界区 n = rand() % 3; sleep(n); // 非临界区睡眠,模拟其他工作 } return 0; }
4. 进程 B(输出 B,最后销毁信号量)

C

// processB.c #include "sem.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main() { sem_init(); srand(getpid()); for (int i = 0; i < 5; i++) { sem_p(); printf("B"); fflush(stdout); int n = rand() % 3; sleep(n); printf("B"); fflush(stdout); sem_v(); n = rand() % 3; sleep(n); } sleep(10); // 等待足够时间让 A 进程完成 sem_destroy(); // 销毁信号量 return 0; }
五、编译与运行

Bash

gcc -o sem.o sem.c -c # 如果分开编译 gcc -o A processA.c sem.o gcc -o B processB.c sem.o ./A & ./B &

运行效果: 你会看到类似 AABB AABB AA BB ... 的交替输出,而不会出现 ABAB 这样的字符交错(因为 printf("A"); sleep(); printf("A"); 被信号量保护成了原子操作)。

六、注意事项与扩展
  • key 值:使用固定 key(如 1234)便于两个进程共享。实际项目中推荐使用 ftok() 生成唯一 key。
  • SEM_UNDO标志:防止进程异常退出导致信号量死锁。
  • 二元信号量 vs 计数信号量:本例初始值为 1,实现互斥;若初始值为 N,则允许多个进程同时进入临界区。
  • 局限性:System V 信号量是内核对象,需要手动删除(IPC_RMID),否则会残留。可用 ipcs -s 查看,ipcrm -s semid 删除。
  • 现代替代:POSIX 信号量(sem_open / sem_init)更简单,但 System V 信号量在老项目和共享内存同步中仍很常见。
七、总结

通过这个简单示例,我们可以看到信号量如何优雅地解决进程互斥问题。掌握 semget、semop、semctl 三个函数,是深入理解 Linux 进程间通信(IPC)的重要一步。

http://www.jsqmd.com/news/633484/

相关文章:

  • html-docx-js:浏览器端HTML到DOCX转换的架构实现与深度集成方案
  • 药用级环拉酸钠哪家便宜 高性价比供应商推荐 - 品牌推荐大师
  • 终极指南:如何用sndcpy实现Android音频无线转发到电脑
  • Qwen3.5-9B企业应用:HR招聘JD生成+候选人简历匹配度分析案例
  • Janus-Pro-7B开发环境配置详解:从IDEA安装到调试插件集成
  • 3分钟快速上手:免费在线3D模型查看器完整指南
  • 天孚通信冲刺港股:年营收51亿 利润20亿,派息5.4亿 市值2790亿
  • 2026市场地位证明全流程技术解析:从申请到落地指南 - 速递信息
  • 2026微信编辑器品牌推荐,亲测8款究极神器,图文编辑质感起飞 - 鹅鹅鹅ee
  • StarWind V2V Image Converter实战指南:轻松实现IMG到VMDK的高效转换
  • ExDark低光照数据集:解锁夜间视觉AI的终极工具包
  • 图解二叉树的四种遍历:前序、中序、后序、层序,看完这篇别再搞混了(含递归与非递归实现)
  • Flowise入门必看:Flowise权限管理与多租户隔离配置指南
  • 基于DeepChat的MathType公式编辑助手
  • 3步快速搭建缠论可视化分析平台:基于TradingView的终极解决方案
  • PVE 9.1.5 ISO 资源下载与安装全攻略(含 9.1.0 升级 9.1.5 教程)
  • FanControl完整指南:5步实现Windows风扇智能控制与静音优化
  • AlienFX Tools终极指南:如何完全掌控你的Alienware设备灯光和风扇
  • iOS 15-16设备激活锁终极绕过指南:5步解锁完整实践方案
  • Tiny11Builder终极指南:让你的老旧电脑重获新生!
  • HTML 教程- (HTML5 标准)
  • Ostrakon-VL-8B智能作业批改系统:图文混合题目的自动化评估
  • Go语言的runtime.CallersFrames调用栈帧迭代与程序计数器解析
  • Arduino嵌入式放射性衰变建模库RadioactiveSpaceData
  • 告别AI失忆症:手把手教你用Cursor Skills打造团队专属的智能编程助手
  • 低代码AI开发平台对决:Dify与Coze的技术架构与实战应用
  • Ai2Psd终极指南:5分钟掌握AI到PSD无损转换技巧
  • 基于STM32的音乐播放器设计
  • 0.96寸OLED显示原理深度解析:从像素排列到汉字渲染
  • Qwen2.5-7B-Instruct效果展示:29+语言互译质量与专业术语一致性实测