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

父进程kill子进程后read()与printf()无响应问题排查

问题排查与修复方案

先给你捋清楚代码里的核心问题,以及对应的修复方法:

现象根源分析

  • 父进程printf内容不显示:你写的printf("NOTHING IS PRINTING HERE ")换行写法错误(应为\n);且stdout默认是行缓冲,即便换行正确,父进程发送信号后直接结束,缓冲内容可能还没来得及刷到终端,导致输出丢失。
  • read无响应/父进程直接退出:发送SIGTERM后,子进程需要时间处理信号、输出内容,但父进程未等待子进程完成操作就直接结束,管道读端被提前关闭,子进程的输出无法写入管道,父进程自然读不到内容。
  • 子进程输出丢失风险:程序a的SIGTERM处理函数如果没显式调用fflush(stdout),输出内容会滞留在用户态缓冲中,子进程被终止时缓冲内容直接丢失,无法写入管道。

修复后的完整代码

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

int main() {
    int pid, childpid;
    int fd1[2], fd2[2];
    int input = 10;

    // 增加管道创建错误检查
    if (pipe(fd1) == -1 || pipe(fd2) == -1) {
        perror("pipe 创建失败");
        exit(EXIT_FAILURE);
    }

    for (int signal = 0; signal < 2; signal++) {
        input += 2;
        char str[20];

        if (signal == 0) {
            pid = fork();
            if (pid == -1) {
                perror("fork 失败");
                exit(EXIT_FAILURE);
            }

            if (pid == 0) {
                // 子进程逻辑
                childpid = getpid();
                // 重定向标准输入输出到管道
                dup2(fd1[0], STDIN_FILENO);
                dup2(fd2[1], STDOUT_FILENO);
                // 关闭所有未使用的管道描述符,避免资源泄漏
                close(fd1[0]);
                close(fd1[1]);
                close(fd2[0]);
                close(fd2[1]);
                // 执行程序a,失败输出错误信息
                if (execlp("./a", "./a", NULL) < 0) {
                    perror("execlp 执行失败");
                    exit(EXIT_FAILURE);
                }
            } else {
                // 父进程逻辑:关闭不用的管道端
                close(fd1[0]);
                close(fd2[1]);
                // 向子进程写入输入
                write(fd1[1], &input, sizeof(input));
                sleep(2);
                // 读取子进程输出
                int nbytes = read(fd2[0], str, sizeof(str));
                printf("第一次读取子进程字节数: %d\n", nbytes);
                fflush(stdout); // 强制刷新缓冲,确保内容实时显示
            }
        }

        if (signal == 1) {
            // 向子进程写入输入
            write(fd1[1], &input, sizeof(input));
            // 发送终止信号
            kill(childpid, SIGTERM);
            // 修正换行符,加fflush确保输出立即显示
            printf("NOTHING IS PRINTING HERE\n");
            fflush(stdout);

            // 读取子进程终止前的输出内容
            int nbytes = read(fd2[0], str, sizeof(str));
            if (nbytes > 0) {
                printf("子进程终止时输出: %.*s\n", nbytes, str);
            }

            // 等待子进程完全退出,确保信号处理和输出操作完成
            waitpid(childpid, NULL, 0);
        }
    }

    // 父进程关闭剩余管道描述符
    close(fd1[1]);
    close(fd2[0]);
    return 0;
}

程序a的必要修改

程序a的SIGTERM信号处理函数必须显式刷新stdout,否则输出内容会丢失,示例代码:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>

void handle_sigterm(int sig) {
    printf("收到SIGTERM,准备终止\n");
    fflush(stdout); // 强制将缓冲内容写入管道
    exit(EXIT_SUCCESS);
}

int main() {
    // 注册SIGTERM信号处理函数
    signal(SIGTERM, handle_sigterm);

    // 读取父进程发来的输入
    int input;
    read(STDIN_FILENO, &input, sizeof(input));
    printf("收到输入: %d\n", input);
    fflush(stdout);

    // 保持运行,等待信号触发
    while(1);
    return 0;
}

关键修复点总结

  • 修正printf换行符为标准\n,并添加fflush(stdout)强制刷新缓冲,解决父进程输出不显示的问题;
  • 使用waitpid等待子进程退出,给子进程足够时间完成信号处理和输出操作;
  • 在signal==1分支中添加子进程输出读取逻辑,获取终止前的内容;
  • 程序a的信号处理函数必须调用fflush(stdout),避免缓冲内容丢失;
  • 完善管道、fork的错误检查,便于问题排查;
  • 关闭所有未使用的管道描述符,避免资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 22:17:51