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

POSIX 信号量(Semaphore)


一、POSIX 信号量基础

1. 什么是信号量?
  • 信号量 是一种同步机制,用于控制对共享资源的访问。它通过一个整数值表示可用资源的数量,支持两种原子操作:
    • P操作(Wait):尝试减少信号量值(若值>0,则减1;否则阻塞)。
    • V操作(Post):增加信号量值(唤醒等待的线程/进程)。
2. POSIX 信号量的两种类型
类型应用场景特点
命名信号量进程间同步通过文件名全局标识
未命名信号量线程间同步(或进程内)需通过共享内存传递

二、命名信号量(进程间同步)

1. 核心函数
  • sem_open():创建或打开命名信号量
  • sem_wait():P操作(阻塞)
  • sem_post():V操作(释放)
  • sem_close():关闭信号量
  • sem_unlink():销毁信号量
2. 示例:两个进程同步访问文件

进程A(写入数据)

#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>int main() {sem_t *sem = sem_open("/my_named_sem", O_CREAT, 0666, 1); // 初始值为1if (sem == SEM_FAILED) {perror("sem_open failed");return -1;}sem_wait(sem); // 获取信号量(P操作)FILE *fp = fopen("shared.txt", "a");fprintf(fp, "Process A writes.\n");fclose(fp);sem_post(sem); // 释放信号量(V操作)sem_close(sem);sem_unlink("/my_named_sem"); // 注意:仅在最后一个进程调用后销毁return 0;
}

进程B(读取数据)

sem_t *sem = sem_open("/my_named_sem", 0); // 打开已有信号量
sem_wait(sem);
FILE *fp = fopen("shared.txt", "r");
char buf[100];
fgets(buf, 100, fp);
printf("Read: %s", buf);
fclose(fp);
sem_post(sem);
sem_close(sem);

三、未命名信号量(线程间同步)

1. 核心函数
  • sem_init():初始化信号量(需指定线程共享标志)
  • sem_destroy():销毁信号量
2. 示例:多线程任务池
#include <pthread.h>
#include <semaphore.h>
#define MAX_TASKS 5sem_t task_sem; // 未命名信号量
int task_queue[MAX_TASKS];
int task_count = 0;void* worker_thread(void* arg) {while (1) {sem_wait(&task_sem); // 等待任务// 取出任务并处理int task = task_queue[--task_count];printf("Processing task: %d\n", task);}return NULL;
}void add_task(int task) {task_queue[task_count++] = task;sem_post(&task_sem); // 发布新任务
}int main() {sem_init(&task_sem, 0, 0); // 初始值为0(无任务)pthread_t tid;pthread_create(&tid, NULL, worker_thread, NULL);// 添加任务for (int i = 0; i < 10; i++) {add_task(i);sleep(1);}sem_destroy(&task_sem);return 0;
}

四、关键注意事项

1. 信号量 vs 互斥锁
特性信号量互斥锁
资源数量可设置初始值(N个资源)仅1(互斥)
所有者无所有者概念锁定者必须负责解锁
跨进程支持(命名信号量)需进程共享的互斥锁
2. 常见陷阱
  • 死锁:多个信号量未按顺序获取。
    • 解决:统一资源申请顺序。
  • 资源泄漏:未正确调用 sem_close()sem_unlink()
    • 命名信号量会残留于 /dev/shm(Linux)。
  • 虚假唤醒sem_wait() 可能被信号中断。
    • 建议配合循环检查实际条件。

五、高级应用场景

1. 有限资源池(如数据库连接)
sem_t db_conn_sem;
sem_init(&db_conn_sem, 0, 10); // 最多10个连接void query_database() {sem_wait(&db_conn_sem); // 获取连接// 执行查询...sem_post(&db_conn_sem); // 释放连接
}
2. 多进程生产者-消费者
// 共享内存中定义循环队列和信号量
struct {int buffer[BUFFER_SIZE];int in, out;sem_t empty, full;
} *shared;// 生产者进程
sem_wait(&shared->empty);
shared->buffer[shared->in] = data;
shared->in = (shared->in + 1) % BUFFER_SIZE;
sem_post(&shared->full);// 消费者进程
sem_wait(&shared->full);
data = shared->buffer[shared->out];
shared->out = (shared->out + 1) % BUFFER_SIZE;
sem_post(&shared->empty);

六、代码实战:跨进程聊天程序

1. 设计思路
  • 使用 命名信号量 控制消息队列的访问。
  • 共享内存存储消息缓冲区。
  • 两个进程交替发送和接收消息。
2. 核心代码片段
// 共享内存结构
struct chat_buffer {char message[256];sem_t send_sem, recv_sem;
};// 进程A(先发送)
struct chat_buffer *buf = mmap(...);
sem_init(&buf->send_sem, 1, 1);  // 初始可发送
sem_init(&buf->recv_sem, 1, 0);  // 初始不可接收while (1) {sem_wait(&buf->send_sem);fgets(buf->message, 256, stdin);sem_post(&buf->recv_sem);
}// 进程B(先接收)
sem_wait(&buf->recv_sem);
printf("Received: %s", buf->message);
sem_post(&buf->send_sem);

七、总结

  • POSIX 信号量 是强大的同步工具,适用于线程和进程间的复杂协调。
  • 命名信号量 通过文件系统标识,适合进程间同步。
  • 未命名信号量 更轻量,但需手动管理内存共享。
  • 始终注意 资源释放死锁预防,结合日志或调试工具验证同步逻辑。

相关文章:

  • 深入解析 Python 中的装饰器 —— 从基础到实战
  • 第六章 进阶04 尊重
  • 【Contiki】Contiki process概述
  • oasys 打开慢的问题解决
  • 2025年03月中国电子学会青少年软件编程(Python)等级考试试卷(四级)真题
  • Spring Boot 3 + SpringDoc:打造接口文档
  • FPGA学习——DE2-115开发板上设计波形发生器
  • 软件项目验收报告模板
  • 算法备案的审核标准是什么?
  • 全国青少年信息素养大赛 C++算法创意实践挑战赛初赛 集训模拟试卷《七》及详细答案解析
  • chkconfig指令
  • Windows程序包管理器WinGet实战
  • HarmonyOS 基础语法概述 UI范式
  • cmd查询占用端口并查杀
  • 推荐一款Umi-OCR_文字识别工具
  • 一个好用的高性能日志库——NanoLog
  • 【学习笔记】Py网络爬虫学习记录(更新中)
  • 【深度学习—李宏毅教程笔记】Self-attention
  • Selenium无法定位元素的几种解决方案
  • 柴油机气缸体顶底面粗铣组合机床总体及夹具设计
  • 中共中央办公厅、国务院办公厅印发《农村基层干部廉洁履行职责规定》
  • 日本长野一夜连震47次,当局呼吁警惕更大地震
  • 观察|美军在菲律宾部署新导弹,试图继续构建“导弹链”
  • 一图看懂|特朗普政府VS美国顶尖高校:这场风暴如何刮起?
  • 核观察|为核潜艇打造“安全堡垒”,印度系统性提升海基核威慑力
  • 阿坝州市监局公布一批典型案例,有加油站篡改加油枪计量器