MST
星途 面试题库

面试题:C语言Linux环境下子进程等待超时的优化及异常处理

在高并发的Linux C应用场景下,需要频繁创建和等待子进程,且对每个子进程的等待超时设置需根据系统负载动态调整(例如,系统负载高时,超时时间适当延长)。同时,要处理可能出现的信号干扰(如SIGCHLD信号),避免出现僵尸进程以及由于信号处理不当导致的程序异常。请设计一个完整的解决方案,包括数据结构设计、动态超时调整算法、信号处理机制等,并给出关键的C语言代码实现。
25.2万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. 数据结构设计

我们可以设计一个结构体来存储每个子进程的相关信息,包括进程ID、创建时间、超时时间等。

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

// 定义子进程信息结构体
typedef struct {
    pid_t pid;
    time_t create_time;
    int timeout;
} ChildProcess;

2. 动态超时调整算法

动态超时调整可以根据系统负载来进行。我们可以通过读取/proc/loadavg文件获取系统负载信息,然后根据负载值调整超时时间。

// 获取系统负载并调整超时时间
int adjust_timeout() {
    FILE *loadavg_file = fopen("/proc/loadavg", "r");
    if (loadavg_file == NULL) {
        perror("fopen");
        return -1;
    }
    double loadavg;
    fscanf(loadavg_file, "%lf", &loadavg);
    fclose(loadavg_file);

    // 假设负载在0 - 10之间,负载越高,超时时间越长
    int base_timeout = 5; // 基础超时时间
    int new_timeout = base_timeout + (int)(loadavg * 2); // 简单的调整算法
    return new_timeout;
}

3. 信号处理机制

为了避免僵尸进程,我们需要处理SIGCHLD信号。在信号处理函数中,我们可以使用waitpid来回收子进程资源。

// SIGCHLD信号处理函数
void sigchld_handler(int signum) {
    pid_t pid;
    int status;
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
        // 处理子进程退出
        printf("Child process %d terminated\n", pid);
    }
}

4. 关键C语言代码实现

以下是完整的代码示例,展示如何创建子进程、设置动态超时并处理信号。

int main() {
    // 注册SIGCHLD信号处理函数
    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");
        return 1;
    }

    ChildProcess *child_processes[100];
    int child_count = 0;

    for (int i = 0; i < 10; i++) {
        // 创建子进程
        pid_t pid = fork();
        if (pid == 0) {
            // 子进程
            printf("Child process %d started\n", getpid());
            sleep(2); // 模拟子进程工作
            exit(0);
        } else if (pid > 0) {
            // 父进程
            ChildProcess *new_process = (ChildProcess *)malloc(sizeof(ChildProcess));
            new_process->pid = pid;
            new_process->create_time = time(NULL);
            new_process->timeout = adjust_timeout();
            child_processes[child_count++] = new_process;
        } else {
            perror("fork");
            return 1;
        }
    }

    // 等待子进程或超时
    for (int i = 0; i < child_count; i++) {
        int status;
        time_t current_time = time(NULL);
        while (difftime(current_time, child_processes[i]->create_time) < child_processes[i]->timeout) {
            pid_t result = waitpid(child_processes[i]->pid, &status, WNOHANG);
            if (result == child_processes[i]->pid) {
                printf("Child process %d exited normally\n", child_processes[i]->pid);
                break;
            }
            sleep(1);
            current_time = time(NULL);
        }
        if (difftime(current_time, child_processes[i]->create_time) >= child_processes[i]->timeout) {
            printf("Child process %d timed out\n", child_processes[i]->pid);
            kill(child_processes[i]->pid, SIGKILL); // 超时则杀死子进程
        }
        free(child_processes[i]);
    }

    return 0;
}

上述代码实现了以下功能:

  1. 数据结构设计:定义了ChildProcess结构体来存储子进程相关信息。
  2. 动态超时调整算法:通过读取/proc/loadavg文件获取系统负载,并据此调整每个子进程的等待超时时间。
  3. 信号处理机制:注册了SIGCHLD信号处理函数,在子进程结束时及时回收资源,避免产生僵尸进程。
  4. 关键代码实现:展示了如何在高并发场景下创建子进程、设置动态超时并处理子进程的等待和超时情况。