在分叉的 C 进程之间使用 stdin 和 stdout 进行相互通信

问题描述 投票:0回答:1

我想要实现的是,创建 2 个子进程说“左”和“右”,左子进程执行一个名为 ./left 的进程,该进程从 stdin 获取 2 个输入并将其求和,然后通过 stdout 将其转发到其父进程。然后右子进程再次执行一个进程,再次对 2 个 stdin 输入求和并将其转发给其父进程。在下面的代码中,左侧结果成功处理并通过 stderr 打印,但右侧结果是垃圾值,我不明白为什么。

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;
}

左.c 和右.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
}

我期望得到

Right child result: 25
作为输出,但我得到
Right child result: {garbage value}
作为输出。

c operating-system fork exec dup
1个回答
0
投票

问题的一部分是您没有关闭足够的管道描述符。

经验法则:如果您将管道的一端

dup2()
连接到标准输入或标准输出,请尽快关闭
pipe()
中的两个原始文件描述符。特别是,这意味着在使用任何
exec*()
系列函数之前。该规则也适用于
dup()
fcntl()
F_DUPFD

当前代码修订版(修订版 4)中的另一个问题是子级希望使用

scanf()
读取 ASCII 数字,但父级发送二进制数。

最大的问题是您没有为子进程创建管道。 第二个问题是您关闭了父进程中右子管道的错误端。

解决这些问题会产生如下代码:

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

int main(void)
{
    // ###### 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
    if (pipe(p2c_l) < 0 || pipe(c2p_l) < 0)
    {
        fprintf(stderr, "Failed to create pipes for left child: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    int resultFromLeftChild = -1;
    int pid1 = fork();
    if (pid1 < 0)
    {
        fprintf(stderr, "Fork 1 failed: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    if (pid1 == 0)   // Left child
    {
        fprintf(stderr, "Left child process\n");
        dup2(p2c_l[0], 0);  // STDIN_FILENO?
        dup2(c2p_l[1], 1);  // STDOUT_FILENO?

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

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

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

        int n1, n2;
        n1 = 5;
        n2 = 10;
        char buffer[32];
        snprintf(buffer, sizeof(buffer), "%d\n", n1);
        write(p2c_l[1], buffer, strlen(buffer));
        snprintf(buffer, sizeof(buffer), "%d\n", n2);
        write(p2c_l[1], buffer, strlen(buffer));

        int nbytes = read(c2p_l[0], buffer, sizeof(buffer));
        if (nbytes <= 0)
            fprintf(stderr, "Failed to read from child 1\n");
        else
        {
            fprintf(stderr, "Left child result: %s\n", buffer);
            resultFromLeftChild = atoi(buffer);
        }
        close(p2c_l[1]);
        close(c2p_l[0]);

        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("PID %d exited with status 0x%.4X\n", corpse, status);
    }

    /* Now repeat the code for the right child - this is where we use functions! */
    int p2c_r[2]; // Parent to child right pipe
    int c2p_r[2]; // Child to parent right pipe

    // Create pipes
    if (pipe(p2c_r) < 0 || pipe(c2p_r) < 0)
    {
        fprintf(stderr, "Failed to create pipes for right child: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }

    int pid2 = fork();
    if (pid2 < 0)
    {
        fprintf(stderr, "Fork 1 failed: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    if (pid2 == 0)   // Right child
    {
        fprintf(stderr, "Right child process\n");
        dup2(p2c_r[0], 0);
        dup2(c2p_r[1], 1);
        close(p2c_r[0]);
        close(p2c_r[1]);
        close(c2p_r[0]);
        close(c2p_r[1]);
        char *args[] = {"./right", NULL};
        execvp(args[0], args);
        fprintf(stderr, "Right child process failed: %d %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
    }
    else     // Parent process
    {
        close(p2c_r[0]);
        close(c2p_r[1]);
        fprintf(stderr, "Parent process after second fork\n");

        int n1, n2;
        n1 = resultFromLeftChild;
        n2 = 10;
        char buffer[32];
        snprintf(buffer, sizeof(buffer), "%d\n", n1);
        write(p2c_r[1], buffer, strlen(buffer));
        snprintf(buffer, sizeof(buffer), "%d\n", n2);
        write(p2c_r[1], buffer, strlen(buffer));

        int nbytes = read(c2p_r[0], buffer, sizeof(buffer));
        if (nbytes <= 0)
            fprintf(stderr, "Failed to read from child 2\n");
        else
            fprintf(stderr, "Right child result: %s\n", buffer);
        close(p2c_r[1]);
        close(c2p_r[0]);

        int corpse;
        int status;
        while ((corpse = wait(&status)) > 0)
            printf("PID %d exited with status 0x%.4X\n", corpse, status);
    }

    return 0;
}

输出示例:

Parent process after first fork
Left child process
Left child result: 15

PID 57771 exited with status 0x0000
Parent process after second fork
Right child process
Right child result: 25

PID 57772 exited with status 0x0000
© www.soinside.com 2019 - 2024. All rights reserved.