news 2026/3/5 16:14:26

Linux信号量

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux信号量
1. 为什么要发明信号量?

这种多进程争抢访问的共享资源(如共享内存、打印机),被称为临界资源 (Critical Resource)。访问这些资源的代码段,叫临界区 (Critical Section)

我们面临的问题是:原子性 (Atomicity)

  • 你在 C++ 里写count++,汇编层面其实是 3 条指令(读入寄存器、加1、写回内存)。
  • 如果进程 A 执行了一半被切走了,进程 B 来了,数据就会乱套。

信号量就是为了解决这个问题而生的。它本质上是一个内核中的计数器,但它的增减操作是原子的(要么全做完,要么不做,不会被打断)。

2. 核心原理:PV 原语

这是荷兰计算机科学家 Dijkstra(迪杰斯特拉)提出的概念,是所有并发编程的基石。

假设我们需要一把“锁”(互斥量),信号量的初始值设为1

  • P 操作 (Proberen, 测试/申请)
    • 逻辑:sem--(计数器减 1)。
    • 判断
      • 如果减完后值 >= 0:申请资源成功,进程继续执行(拿到锁了)。
      • 如果减完后值 < 0(或者减之前是0):资源没了,进程挂起阻塞,放入等待队列。
  • V 操作 (Verhogen, 增加/释放)
    • 逻辑:sem++(计数器加 1)。
    • 动作
      • 如果加完后值 <=0:说明等待队列里还有人,唤醒一个等待的进程。
      • 进程继续执行。

3. 复杂的 System V 接口

Linux 的 System V 信号量设计得比较复杂(它设计的初衷是让你可以一次操作一组信号量),所以接口参数很多。我们要学会把复杂变简单。

我们要用的三个核心函数:

A.semget—— 创建/获取

int semget(key_t key, int nsems, int semflg);
  • key:和共享内存一样,用ftok生成。
  • nsems你要申请几个信号量?通常我们只需要1个(作为互斥锁)。
  • semflgIPC_CREAT | 0666等。

B.semctl—— 控制/初始化

int semctl(int semid, int semnum, int cmd, ...);
  • semnum:操作第几个信号量?(下标从 0 开始)。
  • cmd
    • SETVAL:设置信号量的初始值(比如设为 1)。
    • IPC_RMID:删除信号量集。

C.semop—— 核心 PV 操作

int semop(int semid, struct sembuf *sops, size_t nsops);

这是最难用的函数,我们需要定义一个结构体:

struct sembuf { unsigned short sem_num; // 操作第几个信号量 (0) short sem_op; // -1 是 P操作,+1 是 V操作 short sem_flg; // 通常设为 0,或 SEM_UNDO };

SEM_UNDO是个很重要的标志:如果进程崩溃了没来得及释放锁,操作系统会自动帮你“撤销”之前的 P 操作,防止死锁。

4.信号量实现进程通信(代码)
common
#pragma once #include <iostream> #include <string> #include<cstring> #include <sys/types.h> #include <sys/ipc.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ipc.h> #include <sys/shm.h> #include<unistd.h> #include<fcntl.h> #include<sys/sem.h> // 生成key的路径和ID const std::string PATH_NAME = "."; const int PROJ_ID = 6666; // 设置共享内存的大小 const int MEM_SIZE = 4096; // 用于同步的命名管道 const std::string FIFO_NAME = "./my_pipe"; // 获取唯一的key key_t GetKey() { key_t key = ftok(PATH_NAME.c_str(), PROJ_ID); if (key < 0) { std::cout << "创建共享key获取失败" << std::endl; exit(1); } return key; } //信号量创建 int CreateSem(int nsems) { int key=GetKey(); int sem=semget(key,nsems,IPC_CREAT|0666); if(sem<0) { std::cout<<"信号量创建失败"<<std::endl; exit(1); } return sem; } //信号量获取 int GetSem() { int key=GetKey(); int sem=semget(key,0,0); if(sem<0) { std::cout<<"信号量获取失败"<<std::endl; exit(1); } return sem; } //信号量初始化 void InitSem(int semid, int which, int value) { union semun { int val; struct semid_ds *buf; unsigned short *array; }; union semun se; se.val=value; semctl(semid,which,SETVAL,se); } //申请资源 void P(int semid, int which) { //int semop(int semid, struct sembuf *sops, size_t nsops); sembuf se; se.sem_num=which; se.sem_op=-1; se.sem_flg=0; semop(semid,&se,1); } //释放资源 void V(int semid, int which) { //int semop(int semid, struct sembuf *sops, size_t nsops); sembuf se; se.sem_num=which; se.sem_op=1; se.sem_flg=0; semop(semid,&se,1); } //信号量删除 void DleSem(int semid) { //int semop(int semid, struct sembuf *sops, size_t nsops); semctl(semid,0,IPC_RMID); }
server.cc
#include "common.hpp" // 创建共享内存,读取数据 class Init { public: Init() { // 共享内存 key_t k = GetKey(); std::cout << "server key: " << std::hex << k << std::dec << std::endl; // 不存在就创建,存在就报错 _shmid = shmget(k, MEM_SIZE, IPC_CREAT | IPC_EXCL | 0666); if (_shmid < 0) { perror("创建共享内存失败"); exit(2); } std::cout << "创建共享内存成功:" << _shmid << std::endl; // 挂载 _start = (char *)shmat(_shmid, nullptr, 0); if (_start == (void *)(-1)) { perror("挂载失败"); exit(3); } // 创建信号量 (申请1个) _semid = CreateSem(1); if (_semid < 0) { perror("信号量创建失败"); exit(4); } // 3. 初始化信号量为 0 // 含义:目前没有资源(数据),消费者必须等 InitSem(_semid, 0, 0); } ~Init() { // 去关联 shmdt(_start); // 删除共享内存 shmctl(_shmid, IPC_RMID, nullptr); // 删除信号量 DleSem(_semid); std::cout << "资源清理完毕...." << std::endl; } public: int _shmid; int _semid; char *_start; }; int main() { Init init; std::cout << "server ready...." << std::endl; while (true) { P(init._semid, 0); // 收到信号 std::cout << "客户端说:" << init._start << std::endl; if (strcmp(init._start, "quit") == 0) { break; } } return 0; }
client.cc
#include"common.hpp" int main() { //获取key key_t k=GetKey(); //获取共享内存ID int shmid=shmget(k,MEM_SIZE,IPC_CREAT); if(shmid<0) { perror("共享内存获取失败"); return 1; } //挂接 char* start=(char*)shmat(shmid,nullptr,0); if(start==(char*)(-1)) { perror("共享内存挂接失败"); return 2; } int semid = GetSem(); //开始写入数据 while(true) { std::cout<<">"; std::string buffer; std::getline(std::cin,buffer); //直接把数据写到共享内存 snprintf(start,MEM_SIZE,"%s",buffer.c_str()); //通知server V(semid,0); if(buffer=="quit") { break; } } //去关联,但是不需要删除共享内存 shmdt(start); return 0; }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/4 8:42:20

17、OS X 系统中的多任务处理与进程管理

OS X 系统中的多任务处理与进程管理 1. 多任务处理概述 OS X 具备强大的多任务处理能力,它能迅速地在运行的应用程序和系统进程之间分配处理器时间,让用户感觉所有任务都在同时运行。当新应用启动、进程开始,或者其他进程闲置或完全关闭时,系统会实时监控这些任务,并动态…

作者头像 李华
网站建设 2026/3/5 14:56:57

从零构建多语言AI应用:Klavis国际化实战指南 [特殊字符]

面对全球化用户群体时&#xff0c;AI应用常常遭遇语言障碍、文化差异和区域适配等挑战。Klavis开源MCP基础设施为您提供了完整的解决方案&#xff0c;让您的AI应用轻松跨越语言边界&#xff0c;服务全球用户。 【免费下载链接】klavis Klavis AI (YC X25): Open Source MCP Inf…

作者头像 李华
网站建设 2026/3/4 21:33:09

Easy Effects社区预设使用指南:3步解锁专业级音效体验

Easy Effects社区预设使用指南&#xff1a;3步解锁专业级音效体验 【免费下载链接】easyeffects Limiter, compressor, convolver, equalizer and auto volume and many other plugins for PipeWire applications 项目地址: https://gitcode.com/gh_mirrors/ea/easyeffects …

作者头像 李华
网站建设 2026/3/4 2:48:25

LangChain 1.1 版本“中间件“特性解读与实战运用

LangChain v1.1.0 于 2025 年 11 月 25 日发布&#xff0c;中间件&#xff08;Middleware&#xff09;作为 create_agent 的核心特性&#xff0c;为 Agent 开发带来了前所未有的灵活性和可扩展性。本文将深入解读中间件机制&#xff0c;并通过实战示例帮助你快速上手。 一、什么…

作者头像 李华
网站建设 2026/3/4 21:32:24

Qwen3-4B:重新定义智能对话体验的新一代语言模型

Qwen3-4B&#xff1a;重新定义智能对话体验的新一代语言模型 【免费下载链接】Qwen3-4B Qwen3-4B&#xff0c;新一代大型语言模型&#xff0c;集稠密和混合专家&#xff08;MoE&#xff09;模型于一体。突破性提升推理、指令遵循、代理能力及多语言支持&#xff0c;自如切换思维…

作者头像 李华
网站建设 2026/3/4 13:37:18

Factorio智能体5000步规划:从认知断裂到空间推理突破

Factorio智能体5000步规划&#xff1a;从认知断裂到空间推理突破 【免费下载链接】factorio-learning-environment A non-saturating, open-ended environment for evaluating LLMs in Factorio 项目地址: https://gitcode.com/GitHub_Trending/fa/factorio-learning-environ…

作者头像 李华