当前位置: 首页 > 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 信号量 是强大的同步工具,适用于线程和进程间的复杂协调。
  • 命名信号量 通过文件系统标识,适合进程间同步。
  • 未命名信号量 更轻量,但需手动管理内存共享。
  • 始终注意 资源释放死锁预防,结合日志或调试工具验证同步逻辑。
http://www.xdnf.cn/news/12583.html

相关文章:

  • MacOS怎么显示隐藏文件
  • Vue3 实战:打造多功能旅游攻略选项卡页面
  • 记录学习的第二十九天
  • unity TEngine学习记录3
  • 精准计量+AI管控——安科瑞助力高校水电管理数字化转型
  • C#插件与可扩展性
  • 闲来无事,用HTML+CSS+JS打造一个84键机械键盘模拟器
  • 优化自旋锁的实现
  • pdfjs库使用3
  • Linux内核机制——内存管理
  • C++ 迭代器失效详解:如何避免 vector 操作中的陷阱
  • 数控铣床自动上下料机械手控制装置设计
  • IDEA 2025.1更新-AI助手试用和第三方模型集成方案
  • C++类和对象上
  • 00.IDEA 插件推荐清单(2025)
  • Jenkins 简易使用记录
  • 从零到一:管理系统设计新手如何快速上手?
  • MATLAB 控制系统设计与仿真 - 37
  • package.json 里面出现 workspace:*,关于工作区的解释
  • 极狐GitLab 账号限制有哪些?
  • 使用MetaGPT 创建智能体(2)多智能体
  • 抽象类和接口的区别
  • 基于X86/RK/全志+FPGA+AI工业一体机在电力接地系统中的应用方案
  • 【人力资源管理系统】C#实现
  • 国产品牌芯洲科技100V降压芯片系列
  • vscode 红色波浪线问题
  • YOLOv8 Bug 及解决方案汇总 【2024.1.24更新】【环境安装】【训练 断点续训】OMPError / KeyError
  • 深度学习3.1 线性回归
  • vcpkg缓存问题研究
  • volatile 和 memory barrier 的组合用法