求助:使用Semaphore实现Linux C死锁的正确示例代码
用Semaphore实现死锁的Linux C示例
嘿,我来帮你搞定这个死锁实现的问题!先看看你现有代码的核心问题:
- 你只创建了一个信号量,死锁需要至少两个独立资源才能形成循环等待的必要条件
- 父进程调用了
wait(NULL),会一直阻塞等待子进程退出,但子进程拿到信号量后进入死循环永远不结束,父进程根本没机会尝试获取信号量,自然不会触发死锁
要实现死锁,必须满足四个必要条件:
- 互斥:每个资源同一时间只能被一个进程持有
- 持有并等待:进程持有一个资源的同时,等待获取另一个资源
- 不可剥夺:资源不能被强制从持有进程中夺走
- 循环等待:进程A持有资源1等待资源2,进程B持有资源2等待资源1
下面是符合要求的示例代码,用两个信号量触发典型的死锁场景:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> #include <sys/wait.h> #define KEY_SEM1 19950914 #define KEY_SEM2 19950915 #define FLAG 0666 // 封装信号量P操作(获取资源) void sem_acquire(int semid, int sem_num) { struct sembuf op = {sem_num, -1, SEM_UNDO}; if (semop(semid, &op, 1) == -1) { perror("semop acquire failed"); exit(EXIT_FAILURE); } } // 封装信号量V操作(释放资源) void sem_release(int semid, int sem_num) { struct sembuf op = {sem_num, 1, SEM_UNDO}; if (semop(semid, &op, 1) == -1) { perror("semop release failed"); exit(EXIT_FAILURE); } } int main() { int semid1, semid2; // 创建两个独立信号量,初始值均为1(代表互斥资源) semid1 = semget(KEY_SEM1, 1, FLAG | IPC_CREAT); if (semid1 < 0) { perror("semget for sem1 failed"); exit(EXIT_FAILURE); } semctl(semid1, 0, SETVAL, 1); semid2 = semget(KEY_SEM2, 1, FLAG | IPC_CREAT); if (semid2 < 0) { perror("semget for sem2 failed"); exit(EXIT_FAILURE); } semctl(semid2, 0, SETVAL, 1); pid_t pid = fork(); if (pid == -1) { perror("fork failed"); exit(EXIT_FAILURE); } if (pid == 0) { // 子进程:先拿Sem2,再尝试拿Sem1 printf("Child: Trying to acquire Semaphore 2\n"); sem_acquire(semid2, 0); printf("Child: Acquired Semaphore 2\n"); // 休眠确保父进程有机会拿到第一个资源 sleep(1); printf("Child: Trying to acquire Semaphore 1\n"); sem_acquire(semid1, 0); printf("Child: Acquired Semaphore 1\n"); // 永远不会执行到 sem_release(semid1, 0); sem_release(semid2, 0); exit(EXIT_SUCCESS); } else { // 父进程:先拿Sem1,再尝试拿Sem2 printf("Parent: Trying to acquire Semaphore 1\n"); sem_acquire(semid1, 0); printf("Parent: Acquired Semaphore 1\n"); // 休眠确保子进程有机会拿到第一个资源 sleep(1); printf("Parent: Trying to acquire Semaphore 2\n"); sem_acquire(semid2, 0); printf("Parent: Acquired Semaphore 2\n"); // 永远不会执行到 sem_release(semid1, 0); sem_release(semid2, 0); wait(NULL); } // 死锁后不会执行到这里,需要手动清理信号量 semctl(semid1, 0, IPC_RMID); semctl(semid2, 0, IPC_RMID); exit(EXIT_SUCCESS); }
代码关键说明:
- 两个独立信号量对应两个互斥资源,初始值设为1保证同一时间只有一个进程能持有
- 子进程和父进程的资源获取顺序相反,形成循环等待,触发死锁
sleep(1)的作用是确保两个进程都能成功拿到第一个资源,避免其中一个进程快速抢占所有资源而不触发死锁
验证死锁:
运行程序后,你会看到两个进程都卡在尝试获取第二个资源的步骤,程序不再继续执行。此时打开另一个终端,用ps aux | grep <你的程序名>查看进程状态,会看到两个进程都处于D状态(不可中断睡眠),这就是死锁的典型表现。
手动清理信号量:
因为死锁后程序不会执行到清理代码,你需要手动删除信号量:
ipcrm -s 19950914 # 删除第一个信号量 ipcrm -s 19950915 # 删除第二个信号量
内容的提问来源于stack exchange,提问作者Bakos Dominik
相关产品推荐
相关产品推荐

