You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求助:使用Semaphore实现Linux C死锁的正确示例代码

用Semaphore实现死锁的Linux C示例

嘿,我来帮你搞定这个死锁实现的问题!先看看你现有代码的核心问题:

  • 你只创建了一个信号量,死锁需要至少两个独立资源才能形成循环等待的必要条件
  • 父进程调用了wait(NULL),会一直阻塞等待子进程退出,但子进程拿到信号量后进入死循环永远不结束,父进程根本没机会尝试获取信号量,自然不会触发死锁

要实现死锁,必须满足四个必要条件:

  1. 互斥:每个资源同一时间只能被一个进程持有
  2. 持有并等待:进程持有一个资源的同时,等待获取另一个资源
  3. 不可剥夺:资源不能被强制从持有进程中夺走
  4. 循环等待:进程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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.07 12:57:32