MST
星途 面试题库

面试题:C语言Linux信号量同步复杂场景处理

假设有一个复杂的C项目,涉及多个进程间通过Linux信号量进行同步,其中某些进程可能异常终止。描述如何设计健壮的信号量同步机制,保证在进程异常终止的情况下,整个系统依然能够正常运行,不出现死锁等问题,给出详细的设计思路和关键代码框架。
29.7万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 信号量初始化
    • 使用semget函数创建信号量集。在创建时,指定合适的权限和初始值。例如,如果是二元信号量,初始值可以设为1;如果是计数信号量,根据实际需求设置初始值。
    • 考虑使用ftok函数生成唯一的key值,以便不同进程能正确获取同一信号量集。
  2. 信号量操作
    • 获取信号量:使用semop函数,并将sem_op字段设为 -1,表示等待信号量可用(P操作)。为防止进程异常终止导致信号量永远被占用,可设置操作的SEM_UNDO标志,这样当进程终止时,内核会自动调整信号量的值。
    • 释放信号量:同样使用semop函数,将sem_op字段设为1,表示释放信号量(V操作)。
  3. 进程异常处理
    • 对于可能异常终止的进程,在进程启动时,注册信号处理函数,捕获如SIGABRTSIGSEGV等导致进程异常终止的信号。
    • 在信号处理函数中,执行清理操作,例如释放已获取的信号量(通过semop执行V操作)。
  4. 检测死锁
    • 可以使用超时机制。在获取信号量时,记录开始时间,若等待时间超过一定阈值,认为可能出现死锁,进行相应处理,如释放已获取的资源并尝试重新获取。
    • 还可以通过资源分配图算法(如银行家算法的变体)来检测系统是否处于安全状态,预防死锁的发生。

关键代码框架

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <signal.h>

// 信号量操作结构体
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short int *array;
};

// 信号处理函数,用于清理信号量
void signal_handler(int signum) {
    // 假设已经获取了信号量集的id为semid
    int semid = semget(ftok(".", 'a'), 1, 0666);
    struct sembuf sem_op;
    sem_op.sem_num = 0;
    sem_op.sem_op = 1;
    sem_op.sem_flg = 0;
    semop(semid, &sem_op, 1);
    exit(EXIT_FAILURE);
}

int main() {
    // 生成唯一的key值
    key_t key = ftok(".", 'a');
    if (key == -1) {
        perror("ftok");
        return 1;
    }

    // 创建信号量集
    int semid = semget(key, 1, IPC_CREAT | 0666);
    if (semid == -1) {
        perror("semget");
        return 1;
    }

    // 初始化信号量
    union semun sem_union;
    sem_union.val = 1;
    if (semctl(semid, 0, SETVAL, sem_union) == -1) {
        perror("semctl SETVAL");
        return 1;
    }

    // 注册信号处理函数
    signal(SIGABRT, signal_handler);
    signal(SIGSEGV, signal_handler);

    // 获取信号量
    struct sembuf sem_op;
    sem_op.sem_num = 0;
    sem_op.sem_op = -1;
    sem_op.sem_flg = SEM_UNDO;
    if (semop(semid, &sem_op, 1) == -1) {
        perror("semop");
        return 1;
    }

    // 进程正常运行的代码

    // 释放信号量
    sem_op.sem_op = 1;
    sem_op.sem_flg = 0;
    if (semop(semid, &sem_op, 1) == -1) {
        perror("semop");
        return 1;
    }

    // 删除信号量集
    if (semctl(semid, 0, IPC_RMID) == -1) {
        perror("semctl IPC_RMID");
        return 1;
    }

    return 0;
}