信号量实现同步
2026/8/25大约 3 分钟
Linux 进程间通信:System V 信号量实现进程同步
1. 什么是进程同步
进程同步解决的是:
多个进程之间的执行先后关系。
例如:
子进程先完成
↓
通知父进程
↓
父进程再继续信号量可以用来完成这种“等待—通知”。
2. 核心思想
本例将信号量初始化为:
init_sem(sem_id, 0);所以:
信号量 = 0父进程执行:
sem_p(sem_id);因为信号量为 0,父进程阻塞。
子进程完成任务后:
sem_v(sem_id);信号量加 1,父进程被唤醒。
所以:
父进程 子进程
P()
│
阻塞
│
│ sleep(3)
│ ↓
│ V()
│ ↓
└────── 被唤醒 ───────┘
↓
继续执行3. 创建信号量
int sem_id = semget(ftok(".", 'a'),
1,
0666 | IPC_CREAT);
ftok() → 生成 Key
semget() → 创建/获取信号量
1 → 创建 1 个信号量
sem_id → 信号量标识符4. 初始化信号量
init_sem(sem_id, 0);内部:
sem_union.val = 0;
semctl(sem_id, 0, SETVAL, sem_union);作用:
把信号量初值设置为 0。
5. P 操作:等待
struct sembuf p_op = {0, -1, SEM_UNDO};
semop(sem_id, &p_op, 1);其中:
0 → 操作第 0 个信号量
-1 → 信号量减 1
SEM_UNDO → 进程异常退出时撤销操作当:
信号量 = 0执行:
P()
↓
无法减 1
↓
阻塞等待注意:
信号量不会真的变成 -1。
6. V 操作:通知
struct sembuf v_op = {0, 1, SEM_UNDO};
semop(sem_id, &v_op, 1);作用:
信号量 +1如果父进程正在 P() 中等待:
子进程 V()
↓
信号量变为可用
↓
父进程被唤醒
↓
P() 成功
↓
继续执行7. 完整执行过程
init_sem(sem_id, 0);
pid = fork();fork() 后:
fork()
/ \
父进程 子进程父进程:
sem_p(sem_id);此时:
信号量 = 0
↓
P()
↓
阻塞子进程:
sleep(3);
sem_v(sem_id);3 秒后:
V()
↓
信号量 +1
↓
父进程被唤醒父进程继续:
printf("父进程...");最后:
sem_v(sem_id);并删除信号量:
del_sem(sem_id);8. 一张图看懂
信号量初值 = 0
fork()
/ \
↓ ↓
父进程 子进程
│ │
sem_p() sleep(3)
│ │
阻塞 │
│ │
│ sem_v()
│ │
└──────←───┘
被唤醒
↓
继续执行9. 一句话记忆
进程同步 = 一个进程等待,另一个进程完成后通过 V 操作发出通知。
本例只需要记住:
semget() → 创建/获取信号量
semctl() → 初始化/删除
sem_p() → 等待
sem_v() → 通知以及最核心的关系:
初值 0
↓
P() 等待
↓
另一个进程 V()
↓
等待进程继续这就是 System V 信号量实现进程同步。
完整代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define DELAY_TIME 3
union semun {
int val; /* value for SETVAL */
struct semid_ds *buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* array for GETALL, SETALL */
struct seminfo *__buf;
};
int init_sem(int sem_id, int init_value){
union semun sem_union;
sem_union.val = init_value;
if(semctl(sem_id, 0, SETVAL, sem_union)==-1){
perror("Initialize semaphore");
return -1;
}
return 0;
}
int del_sem(int sem_id){
union semun sem_union;
if(semctl(sem_id, 0, IPC_RMID, sem_union)==-1){
perror("Delete semaphore");
return -1;
}
}
int sem_p(int sem_id){
struct sembuf p_op = {0, -1, SEM_UNDO};
if(semop(sem_id, &p_op, 1)==-1){
perror("P operation");
return -1;
}
return 0;
}
int sem_v(int sem_id){
struct sembuf v_op = {0, 1, SEM_UNDO};
if(semop(sem_id, &v_op, 1)==-1){
perror("V operation");
return -1;
}
return 0;
}
int main(){
int sem_id = semget(ftok(".", 'a'), 1, 0666|IPC_CREAT);
init_sem(sem_id, 0); //信号量初值设为0
pid_t pid = fork();
if(pid==0){ //子进程
printf("子进程将等待若干秒...\n");
sleep(DELAY_TIME);
printf("子进程id=%d, 其fork返回值=%d\n", getpid(), pid);
sem_v(sem_id);
}
else if(pid>0){ //父进程
sem_p(sem_id);
printf("父进程id=%d, 其fork返回值=%d\n", getpid(), pid);
sem_v(sem_id);
del_sem(sem_id); //由父进程删除信号量
}
return 0;
}
/* 该程序利用信号量实现进程间的同步 */
