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

父进程Socket accept与waitpid管理问题及SIGCHLD日志异常排查

关于Socket Accept与子进程管理的两个问题

问题1:Poll超时期间的僵尸进程处理

我有一个父进程,先通过poll等待连接,再在while循环中调用waitpid。相关代码如下:

poll_list[0].fd = server_fd;
poll_list[0].events = POLLIN;
retval = poll(poll_list, 1, 3500);
if (retval == -1)
{
    my_error(stderr,
             "%s: Error setting pool %d", __func__, __LINE__);
    exit(EXIT_FAILURE);
}
if (retval > 0)
{ // There was an accept
    if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t *)&addrlen)) < 0)
    {
        my_error(stderr,
                 "%s: Accepted error of near line %d", __func__, __LINE__);
        exit(EXIT_FAILURE);
    }
}
...
// Forker pour créer un processus enfant
while (waitpid(-1, NULL, WNOHANG) > 0);

但在3.5秒的poll超时期间,可能会产生僵尸进程。我可以缩短poll超时时间,但有没有更好的方案来管理Socket accept与子进程?

问题2:SIGCHLD信号处理中异常退出日志无法输出

此外,我使用SIGCHLD信号处理的代码如下:

void childsignal()
{
    pid_t status;
    int pid;
    while ((pid=waitpid(-1, &status, WNOHANG))>0)
    {
        my_error(stderr, "Parent: The child process %d caught signal %d and exited", pid, status);
        if (!WIFEXITED(status)) // process exit abnormally, need to log.
            my_error(stderr, "Parent: The child process %d caught signal %d and exited", pid, status); //Never executed
    };
}

例如,若进程a正常退出,第一条日志会输出,但进程b被信号杀死时,异常日志却无法输出,请问该如何解决?


解决方案

一、解决Poll超时期间的僵尸进程问题

不要依赖缩短poll超时来回收子进程,正确的做法是用SIGCHLD信号异步处理子进程退出,配合poll的信号中断处理:

  1. 注册SIGCHLD信号处理函数:在进程启动时就注册你写的childsignal函数,确保子进程退出时父进程能立即收到信号并回收,不会等到poll超时。
  2. 处理poll被信号中断的情况:当poll被SIGCHLD信号打断时,会返回-1且errno设为EINTR,此时直接重新调用poll即可,无需退出。

修改后的poll调用逻辑示例:

while (1) {
    poll_list[0].fd = server_fd;
    poll_list[0].events = POLLIN;
    retval = poll(poll_list, 1, 3500);
    if (retval == -1) {
        if (errno == EINTR) {
            // 被SIGCHLD中断,重新进入poll等待
            continue;
        }
        my_error(stderr, "%s: Error setting poll %d", __func__, __LINE__);
        exit(EXIT_FAILURE);
    }
    if (retval > 0) {
        // 处理accept及后续fork逻辑...
    }
    // 无需在此调用waitpid,信号处理函数已异步完成回收
}

这样子进程一退出就会触发SIGCHLD信号,信号处理函数里的waitpid会立即清理僵尸进程,不会等到poll超时,同时poll被中断后会重新等待连接,不影响正常业务逻辑。

二、修复SIGCHLD处理函数的异常日志问题

你的代码核心问题是错误解析waitpid返回的status状态码:

  • 直接将status作为信号值输出是错误的,status是复合状态码,必须用系统宏解析。
  • 异常判断逻辑正确,但获取信号值的方式错误,需要用WTERMSIG宏提取导致进程终止的信号编号。

修改后的信号处理函数:

void childsignal()
{
    int status;
    pid_t pid;
    // 注意:确保my_error是异步信号安全的函数,避免在信号处理中调用非安全函数
    while ((pid = waitpid(-1, &status, WNOHANG)) > 0)
    {
        if (WIFEXITED(status)) {
            // 正常退出,获取退出码
            int exit_code = WEXITSTATUS(status);
            my_error(stderr, "Parent: Child process %d exited normally with code %d", pid, exit_code);
        } else if (WIFSIGNALED(status)) {
            // 被信号终止,获取信号编号
            int sig = WTERMSIG(status);
            my_error(stderr, "Parent: Child process %d was killed by signal %d", pid, sig);
        }
    }
}

关键修正点:

  1. 用WIFEXITED判断正常退出,WEXITSTATUS提取退出码。
  2. 用WIFSIGNALED判断信号终止,WTERMSIG提取信号编号。
  3. 分情况输出日志,避免重复且逻辑清晰。

另外注意:信号处理函数中必须使用异步信号安全的函数(如write),如果my_error内部调用了printf这类非安全函数,可能会引发未知问题,建议替换为异步安全的日志实现。


内容的提问来源于stack exchange,提问作者JoelCrypto

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 11:12:54