MST

星途 面试题库

面试题:C语言在复杂Linux多进程环境下的优雅退出设计

假设有一个基于C语言的Linux项目,包含多个父子进程以及进程间通信(如管道、共享内存),设计一套机制确保在任何情况下(包括子进程异常终止)整个进程组都能实现优雅退出,并且阐述在这种复杂环境下保证数据一致性和资源正确释放的关键要点与设计思路。
33.8万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 实现进程组优雅退出机制

  1. 信号处理
    • 在父进程中,为常见的终止信号(如 SIGTERMSIGINT)注册信号处理函数。在信号处理函数中,使用 killpg 函数向整个进程组发送相同的信号,这样所有子进程都会收到终止信号。
    • 例如:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void sig_handler(int signum) {
    pid_t pgid = getpgid(0);
    killpg(pgid, signum);
}

int main() {
    signal(SIGTERM, sig_handler);
    signal(SIGINT, sig_handler);
    // 创建子进程等操作
    return 0;
}
  1. 子进程异常终止处理
    • 在父进程中,使用 waitpidwait 系列函数来等待子进程的状态变化。当子进程异常终止时(通过 WIFEXITEDWIFSIGNALED 等宏判断),父进程可以采取相应的措施,比如向其他子进程发送终止信号。
    • 例如:
pid_t pid = fork();
if (pid == 0) {
    // 子进程代码
    exit(0);
} else if (pid > 0) {
    int status;
    pid_t wpid = waitpid(pid, &status, 0);
    if (wpid == pid) {
        if (WIFSIGNALED(status)) {
            // 子进程异常终止,向其他子进程发信号
            pid_t pgid = getpgid(0);
            killpg(pgid, SIGTERM);
        }
    }
}

2. 保证数据一致性和资源正确释放的关键要点与设计思路

  1. 数据一致性
    • 共享内存
      • 使用信号量(semaphore)来控制对共享内存的访问。在进程访问共享内存前获取信号量,访问结束后释放信号量。
      • 例如,使用 semgetsemop 等函数。创建信号量集:
int semid = semget(IPC_PRIVATE, 1, 0666);
union semun {
    int val;
    struct semid_ds *buf;
    unsigned short *array;
} arg;
arg.val = 1;
semctl(semid, 0, SETVAL, arg);

访问共享内存前获取信号量:

struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
semop(semid, &sb, 1);
// 访问共享内存
sb.sem_op = 1;
semop(semid, &sb, 1);
  - 引入事务机制,将对共享内存的一系列操作视为一个原子操作。在操作开始前记录共享内存的状态,操作过程中如果出现异常,可以回滚到初始状态。
- **管道**:
  - 确保读写操作的同步。如果一个进程从管道读取数据,另一个进程向管道写入数据,要保证写入操作完成后,读取操作才开始,反之亦然。可以使用信号或同步原语(如互斥锁)来实现。
  - 例如,使用 `pthread_mutex_t` 来保护管道操作:
pthread_mutex_t pipe_mutex = PTHREAD_MUTEX_INITIALIZER;
// 写管道前加锁
pthread_mutex_lock(&pipe_mutex);
write(pipe_fd[1], data, sizeof(data));
pthread_mutex_unlock(&pipe_mutex);
// 读管道前加锁
pthread_mutex_lock(&pipe_mutex);
read(pipe_fd[0], buffer, sizeof(buffer));
pthread_mutex_unlock(&pipe_mutex);
  1. 资源正确释放
    • 文件描述符
      • 在进程终止前,显式关闭所有打开的文件描述符,包括管道、共享内存相关的文件描述符。可以使用 close 函数。
      • 例如,对于管道 pipe_fd
close(pipe_fd[0]);
close(pipe_fd[1]);
  - 对于共享内存,使用 `shmdt` 解除映射,`shmctl` 删除共享内存段。
// 解除共享内存映射
shmdt(shmaddr);
// 删除共享内存段
shmctl(shmid, IPC_RMID, NULL);
- **内存分配**:
  - 使用 `malloc` 等函数分配的内存,在进程终止前要使用 `free` 函数释放。如果是使用 `mmap` 进行内存映射,要使用 `munmap` 解除映射。
  - 例如:
char *ptr = (char *)malloc(100);
// 使用ptr
free(ptr);
- **进程相关资源**:
  - 父进程在等待子进程结束后,正确处理子进程的退出状态,避免产生僵尸进程。通过 `wait` 系列函数等待子进程,并根据返回状态进行相应处理。

通过上述机制,可以在基于C语言的Linux项目中,实现进程组的优雅退出,并保证数据一致性和资源的正确释放。