MST

星途 面试题库

面试题:C语言在Linux复杂场景下子进程等待机制优化

假设在一个多子进程的Linux C程序中,每个子进程执行不同时长的任务,并且可能会异常退出。请设计并实现一套高效的子进程等待机制,综合运用信号处理、wait系列函数以及进程间通信技术,确保父进程能够及时准确地获取每个子进程的状态,同时避免资源泄漏和进程僵死。说明设计思路以及关键代码段的作用。
49.0万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 信号处理:使用信号机制来捕获子进程状态变化的信号,例如SIGCHLD信号。当子进程状态发生改变(如正常退出、异常终止或停止)时,内核会向父进程发送SIGCHLD信号。
  2. wait系列函数:在信号处理函数中调用waitpid函数来等待子进程结束,并获取其状态信息。waitpid可以指定等待特定的子进程,避免父进程等待所有子进程结束。
  3. 进程间通信(可选):如果需要在子进程之间或子进程与父进程之间传递数据,可以使用管道、共享内存等IPC机制。这里主要是为了获取子进程状态,所以IPC不是核心部分,但在实际应用中可能会用到。

关键代码段作用

  1. 信号处理函数
void sigchld_handler(int signum) {
    pid_t pid;
    int status;
    // 使用waitpid等待子进程结束,避免僵尸进程
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        if (WIFEXITED(status)) {
            printf("子进程 %d 正常退出,退出状态: %d\n", pid, WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("子进程 %d 异常终止,信号: %d\n", pid, WTERMSIG(status));
        }
    }
}

这个函数在接收到SIGCHLD信号时被调用。waitpid函数的-1参数表示等待任意子进程,WNOHANG标志表示如果没有子进程结束则立即返回,不阻塞。通过WIFEXITEDWIFSIGNALED宏来判断子进程是正常退出还是异常终止,并获取相应的状态信息。

  1. 注册信号处理函数
struct sigaction sa;
sa.sa_handler = sigchld_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
    perror("sigaction");
    exit(EXIT_FAILURE);
}

这里使用sigaction函数注册SIGCHLD信号的处理函数。SA_RESTART标志使得被信号中断的系统调用自动重启。

  1. 创建子进程
pid_t pid = fork();
if (pid == -1) {
    perror("fork");
    exit(EXIT_FAILURE);
} else if (pid == 0) {
    // 子进程代码
    // 执行不同时长的任务
    sleep(2); // 示例,执行2秒任务
    exit(EXIT_SUCCESS);
}

使用fork函数创建子进程,子进程执行具体的任务,这里以sleep模拟任务执行时长,最后通过exit退出。父进程则继续执行其他逻辑,等待子进程状态变化信号。

完整示例代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>

void sigchld_handler(int signum) {
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        if (WIFEXITED(status)) {
            printf("子进程 %d 正常退出,退出状态: %d\n", pid, WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("子进程 %d 异常终止,信号: %d\n", pid, WTERMSIG(status));
        }
    }
}

int main() {
    struct sigaction sa;
    sa.sa_handler = sigchld_handler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        perror("sigaction");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < 5; i++) {
        pid_t pid = fork();
        if (pid == -1) {
            perror("fork");
            exit(EXIT_FAILURE);
        } else if (pid == 0) {
            // 子进程执行不同时长任务
            sleep(i + 1);
            exit(i);
        }
    }

    // 父进程继续执行其他逻辑,等待信号处理子进程状态
    while (1) {
        sleep(1);
    }

    return 0;
}

在这个示例中,创建了5个子进程,每个子进程执行不同时长的任务后退出。父进程通过信号处理机制及时获取子进程的状态,避免了进程僵死和资源泄漏。