1. 信号量基础概念解析
信号量(Semaphore)是Linux系统中实现进程间同步与互斥的核心机制之一。1965年由荷兰计算机科学家Dijkstra提出,如今已成为操作系统课程中的经典案例。简单来说,信号量就像十字路口的交通信号灯——它通过计数器的增减来控制多个执行流(进程/线程)对共享资源的访问权限。
信号量的核心是一个整型变量,支持两种原子操作:
- P操作(Proberen,荷兰语的"尝试"):当信号量值>0时减1,否则阻塞等待
- V操作(Verhogen,"增加"):总是将信号量值加1,并唤醒等待进程
在Linux中,信号量主要分为:
- 内核信号量(kernel semaphore):由内核维护,用于内核线程同步
- System V信号量:通过IPC机制实现的持久化信号量
- POSIX信号量:更轻量级的线程间同步工具
关键区别:System V信号量支持信号量集操作,而POSIX信号量通常性能更好且接口更简洁。实际开发中,POSIX信号量在新项目中更常用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. System V信号量实战指南
2.1 创建与初始化
使用semget()系统调用创建信号量集:
c复制#include <sys/sem.h>
// 创建包含1个信号量的新集合
int sem_id = semget(IPC_PRIVATE, 1, 0666 | IPC_CREAT);
if (sem_id == -1) {
perror("semget failed");
exit(EXIT_FAILURE);
}
// 初始化信号量值为1(二进制信号量)
union semun arg;
arg.val = 1;
if (semctl(sem_id, 0, SETVAL, arg) == -1) {
perror("semctl SETVAL failed");
exit(EXIT_FAILURE);
}
2.2 P/V操作实现
通过semop()执行原子操作:
c复制struct sembuf sem_ops;
// P操作(获取资源)
sem_ops.sem_num = 0; // 信号量编号
sem_ops.sem_op = -1; // 操作值
sem_ops.sem_flg = SEM_UNDO; // 进程崩溃时自动释放
if (semop(sem_id, &sem_ops, 1) == -1) {
perror("semop P failed");
}
// 临界区代码...
// V操作(释放资源)
sem_ops.sem_op = 1;
if (semop(sem_id, &sem_ops, 1) == -1) {
perror("semop V failed");
}
重要细节:SEM_UNDO标志确保进程异常终止时不会死锁,这在生产环境中尤为重要。
3. POSIX信号量开发实践
3.1 命名信号量示例
适用于无关进程间的同步:
c复制#include <fcntl.h>
#include <semaphore.h>
// 创建/打开命名信号量
sem_t *sem = sem_open("/my_sem", O_CREAT, 0666, 1);
if (sem == SEM_FAILED) {
perror("sem_open failed");
exit(EXIT_FAILURE);
}
// 使用信号量
sem_wait(sem); // P操作
/* 临界区 */
sem_post(sem); // V操作
// 最后需要关闭和删除
sem_close(sem);
sem_unlink("/my_sem");
3.2 匿名信号量示例
适用于线程间同步:
c复制sem_t sem;
if (sem_init(&sem, 0, 1) != 0) { // pshared=0表示线程共享
perror("sem_init failed");
exit(EXIT_FAILURE);
}
pthread_t thread1, thread2;
pthread_create(&thread1, NULL, thread_func, &sem);
pthread_create(&thread2, NULL, thread_func, &sem);
// 线程函数示例
void* thread_func(void *arg) {
sem_t *s = (sem_t
