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

fork创建的C进程间stdin/stdout通信异常:右子进程返回垃圾值

问题分析与解决

问题描述

需要创建两个子进程left和right,分别执行./left和./right程序:

  • left从标准输入读取两个整数求和,将结果通过标准输出传回父进程
  • right接收left的结果再加10后,同样通过标准输出传回父进程

当前代码中left子进程结果正常,但right子进程返回垃圾值,预期输出Right child result: 25,实际得到随机值。

问题代码

main.c

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


int main() {

    //###### 0 is read end, 1 is write end

    int p2c_l[2]; // Parent to child left pipe, p2c[0] is read end, p2c[1] is write end from parent perspective
    int c2p_l[2]; // Child to parent left pipe

    // Create pipes
    pipe(p2c_l);
    pipe(c2p_l);
    
    int pid = fork();
    if (pid == 0) { //Left child
        fprintf(stderr, "Left child process\n");
        // close right child pipes

        close(c2p_l[0]);
        close(p2c_l[1]);
        
        dup2(p2c_l[0], 0);
        dup2(c2p_l[1], 1);

        char* args[] = {"./left", NULL};
        execvp(args[0], args);
        fprintf(stderr, "Left child process failed\n");
        exit(EXIT_FAILURE);
    } else { // Parent process
        close(c2p_l[1]);
        close(p2c_l[0]);

        // dup2(p2c_l[1], 1);
        // dup2(c2p_l[0], 0);

        fprintf(stderr, "Parent process after first fork\n");

        int n1, n2;
        n1 = 5;
        n2 = 10;
        write(p2c_l[1], &n1, sizeof(n1));
        write(p2c_l[1], &n2, sizeof(n2));

        int resultFromLeftChild;
        read(c2p_l[0], &resultFromLeftChild, sizeof(resultFromLeftChild));

        fprintf(stderr, "Left child result: %d\n", resultFromLeftChild);

        int p2c_r[2]; // Parent to child right pipe
        int c2p_r[2]; // Child to parent right pipe

        int pid2 = fork();
        if (pid2 == 0) { //Right child
            fprintf(stderr, "Right child process\n");
            // close left child pipes

            close(p2c_r[0]);
            close(c2p_r[1]);
            
            //make pc2_r[0] and c2p_r[1] as stdin and stdout
            dup2(p2c_r[0], 0);
            dup2(c2p_r[1], 1);

            char* args[] = {"./right", NULL};
            execvp(args[0], args);

            fprintf(stderr, "Right child process failed\n");
            exit(EXIT_FAILURE);
        } else { // Parent process
            close(p2c_r[1]);
            close(c2p_r[0]);

            // dup2(p2c_r[1], 1);
            // dup2(c2p_r[0], 0);

            fprintf(stderr, "Parent process after second fork\n");

            int n1, n2;
            n1 = resultFromLeftChild;
            n2 = 10;
            // printf("%d\n", n1);
            // printf("%d\n", n2);
            write(p2c_r[1], &n1, sizeof(n1));
            write(p2c_r[1], &n2, sizeof(n2));

            int resultFromRightChild;
            read(c2p_r[0], &resultFromRightChild, sizeof(resultFromRightChild));

            fprintf(stderr, "Right child result: %d\n", resultFromRightChild);

            int result = resultFromLeftChild + resultFromRightChild;
            fprintf(stderr, "Result: %d\n", result);

        }
    }

    return 0;
}

left.c 和 right.c

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

int main(int argc, char *argv[])
{
    int num1, num2;
     if (argc != 1) {
        printf("Usage: %s \n", argv[0]);
        return 1; // Error code for incorrect usage
    }
    scanf("%d", &num1);
    scanf("%d", &num2);
    // Calculate the addition
    int result = num1 + num2;
    //printf("Inputs: %d %d \n", num1, num2);
    // Print the result
    printf("%d\n", result);

    return 0; // Successful execution
}

问题根源

1. Right子进程管道操作完全反向

管道两端的功能被搞反:

  • 对于p2c_r(父→子管道):子进程需要从p2c_r[0](读端)读取数据,应关闭p2c_r[1](写端),但原代码关闭了p2c_r[0],导致子进程无法读取父进程发送的数据。
  • 对于c2p_r(子→父管道):子进程需要往c2p_r[1](写端)写入结果,应关闭c2p_r[0](读端),但原代码关闭了c2p_r[1],导致子进程无法向父进程发送数据。

2. 父进程处理Right管道时关闭了错误的端

父进程需要向p2c_r[1](写端)写入数据,却错误关闭了该端;虽然读取c2p_r[0](读端)的逻辑正确,但由于子进程管道已失效,最终读取到的是垃圾值。

3. 管道数据格式不匹配(隐藏问题)

父进程用write直接发送二进制格式的int,但子进程用scanf读取文本格式的整数,两者不兼容。left子进程看似正常只是巧合,实际scanf会因无法识别二进制数据读取失败,num1和num2会是未初始化的随机值。

修复后的代码

修复方案说明

  • 修正Right子进程和父进程的管道关闭逻辑
  • 统一管道数据格式:改用文本格式传输,父进程用fprintf写入数字,子进程保持scanf读取逻辑不变

修复后的main.c

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

int main() {
    int p2c_l[2];
    int c2p_l[2];
    pipe(p2c_l);
    pipe(c2p_l);
    
    int pid = fork();
    if (pid == 0) { // Left child
        fprintf(stderr, "Left child process\n");
        close(c2p_l[0]);
        close(p2c_l[1]);
        
        dup2(p2c_l[0], 0);
        dup2(c2p_l[1], 1);

        char* args[] = {"./left", NULL};
        execvp(args[0], args);
        fprintf(stderr, "Left child process failed\n");
        exit(EXIT_FAILURE);
    } else { // Parent process
        close(c2p_l[1]);
        close(p2c_l[0]);

        fprintf(stderr, "Parent process after first fork\n");

        // 改用文本格式写入管道
        FILE* p2c_l_file = fdopen(p2c_l[1], "w");
        fprintf(p2c_l_file, "%d\n%d\n", 5, 10);
        fflush(p2c_l_file);
        fclose(p2c_l_file); // 关闭后子进程的scanf会读到EOF,结束读取

        // 读取left子进程的文本结果并转为int
        char left_result_buf[32];
        read(c2p_l[0], left_result_buf, sizeof(left_result_buf));
        int resultFromLeftChild = atoi(left_result_buf);
        fprintf(stderr, "Left child result: %d\n", resultFromLeftChild);
        close(c2p_l[0]);

        int p2c_r[2];
        int c2p_r[2];
        pipe(p2c_r);
        pipe(c2p_r);

        int pid2 = fork();
        if (pid2 == 0) { // Right child
            fprintf(stderr, "Right child process\n");
            // 修正管道关闭逻辑:关闭不需要的端
            close(p2c_r[1]);
            close(c2p_r[0]);
            
            dup2(p2c_r[0], 0);
            dup2(c2p_r[1], 1);

            char* args[] = {"./right", NULL};
            execvp(args[0], args);
            fprintf(stderr, "Right child process failed\n");
            exit(EXIT_FAILURE);
        } else { // Parent process
            // 修正管道关闭逻辑:关闭不需要的端
            close(p2c_r[0]);
            close(c2p_r[1]);

            fprintf(stderr, "Parent process after second fork\n");

            // 文本格式写入数据
            FILE* p2c_r_file = fdopen(p2c_r[1], "w");
            fprintf(p2c_r_file, "%d\n%d\n", resultFromLeftChild, 10);
            fflush(p2c_r_file);
            fclose(p2c_r_file);

            // 读取right子进程的文本结果
            char right_result_buf[32];
            read(c2p_r[0], right_result_buf, sizeof(right_result_buf));
            int resultFromRightChild = atoi(right_result_buf);
            fprintf(stderr, "Right child result: %d\n", resultFromRightChild);

            int result = resultFromLeftChild + resultFromRightChild;
            fprintf(stderr, "Result: %d\n", result);

            close(p2c_r[1]);
            close(c2p_r[0]);
            wait(NULL); // 等待right子进程结束
        }
        wait(NULL); // 等待left子进程结束
    }

    return 0;
}

修复后的输出

Parent process after first fork
Left child process
Left child result: 15
Parent process after second fork
Right child process
Right child result: 25
Result: 40

内容的提问来源于stack exchange,提问作者Emre Kırmızı

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 04:34:50