管道在其中一个子进程内未正确关闭

Pipes are not closing properly inside one of the child processes

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

int funcone(){
    int i;
    char *arr[5] = {"One", "Two", "Three", "Four", "Five"};
    for(i = 0; i <= 5; i++){
        printf("%s\t%d\n", arr[i], i);
    }
    printf("\n~");
    return 1;
}

#define NCHAR 1024
#define DELIMIT "\t\n"
int functwo(){
    //prints only numbers from input received from funcone
    int c;
    int position = 0, nchar = NCHAR, i;
    char *arr = malloc(sizeof *arr * nchar);

    if (!arr)
    {
        fprintf(stderr, "Memory Exhausted.");
    }

    //reads input from stdin stream, supposedly piped from func1
    while ((c = fgetc(stdin)) != 126)
    {
        arr[position++] = c;

        if (position >= nchar) // reallocate space if needed
        {
            nchar += NCHAR;
            arr = realloc(arr, nchar + NCHAR);

            if (!arr)
            {
                fprintf(stderr, "Memory Exhausted.\n");
                break;
            }
        }
    }
    arr[position] = 0;

    char *token;
    //splits string by \t\n, and converts string into numbers
    token = strtok(arr, DELIMIT);
    while (token != NULL)
    {
        char *endptr;
        int x = strtol(token, &endptr, 10);
        if (x > 0)
        {
            printf("%d\n", x);
        }
        token = strtok(NULL, DELIMIT);
    }
    printf("\n~");

    free(arr);  

    return 1;
}

int main(){
    printf("piping begins\n");
    int fd[2];
    int childstatus = 0;
    
    if (pipe(fd) == -1)
    {
        perror("Pipe1 error");
        return 1;
    }

    int pid1 = fork();
    if (pid1 < 0)
    {
        perror("Pid1 error");
        return 1;
    }

    if (pid1 == 0)
    {
        //child 1 process
        dup2(fd[1], STDOUT_FILENO);
        close(fd[1]);
        close(fd[0]);
        int f1 = funcone();
        // int f1 = (*builtin_functions[aone])(argone);
        /*In the main program, this line runs a function 
         *with a corresponding index number, and takes in the
         *command keywords as arguments. Returns 1 to continue Shell loop, 0 to exit.*/
    }

    int pid2 = fork();
    if (pid2 < 0)
    {
        perror("Pid2 error");
        return 1;
    }

    if (pid2 == 0)
    {
        // child 2 process
        dup2(fd[0], STDIN_FILENO);
        dup2(fd[1], STDOUT_FILENO);
        close(fd[0]);
        close(fd[1]);
        int f2 = functwo();
        // int f1 = (*builtin_functions[aone])(argone);
    }

    // parent process

    waitpid(pid1, &childstatus, WNOHANG);
    waitpid(pid2, &childstatus, WNOHANG);

    dup2(fd[0], STDIN_FILENO);
    close(fd[0]);
    close(fd[1]);

    int f3 = functwo();
    //supposed to be functhree, but something similar to functwo so i reused it.
    // int f3 = (*builtin_functions[aone])(argone);
    printf("Piping process complete.\n");
}

我正在用 C 开发一个迷你 shell,一段时间以来我一直在努力弄清楚为什么有些管道卡在子进程中并且不执行。让两个子进程都停止的唯一方法是使用 waitpid( , ,WNOHANG),这表明有东西卡在那里。这会导致重复的 STDOUT 和 STDIN 流无法正确关闭,导致程序不断将我的 shell 提示循环到输入流中,最终导致我的程序因分段错误而崩溃。

有什么方法可以解决这个问题或确定管道卡在子进程中的确切位置吗?

编辑 1

所以我尝试在主函数中创建两个不同的管道,即 int fd1[2], fd2[2];,确保关闭所有末端,如​​下所示。我还使所有 funcone()functwo() 无效,而不是 return 任何东西。但是,我的程序现在在终端上打印 ���� 时无限运行,直到我使用 CTRL + C。关于为什么会发生这种情况,还有其他可能的原因吗?或者是否有任何替代方法来模拟进程间通信 (IPC)?

int main(){
printf("piping begins\n");
int fd1[2], fd2[2];
int childstatus = 0;

if (pipe(fd1) == -1)
{
    perror("Pipe1 error");
    return 1;
}

if (pipe(fd2) == -1)
{
    perror("Pipe2 error");
    return 1;
}

int pid1 = fork();
if (pid1 < 0)
{
    perror("Pid1 error");
    return 1;
}

if (pid1 == 0)
{
    //child 1 process
    dup2(fd1[1], STDOUT_FILENO);
    close(fd1[1]);
    close(fd1[0]);
    close(fd2[0]);
    close(fd2[1]);
    funcone();
    // int f1 = (*builtin_functions[aone])(argone);
}

int pid2 = fork();
if (pid2 < 0)
{
    perror("Pid2 error");
    return 1;
}

if (pid2 == 0)
{
    // child 2 process
    dup2(fd1[0], STDIN_FILENO);
    dup2(fd2[1], STDOUT_FILENO);
    close(fd1[0]);
    close(fd1[1]);
    close(fd2[0]);
    close(fd2[1]);
    functwo();
}

// parent process

waitpid(pid1, &childstatus, WNOHANG);
waitpid(pid2, &childstatus, WNOHANG);

dup2(fd2[0], STDIN_FILENO);
close(fd1[0]);
close(fd1[1]);
close(fd2[0]);
close(fd2[1]);

functwo();
printf("Piping process complete.\n");
return 0;

}

您的第一个 child 进程正在写入管道。您的 parent 进程和第二个 child 进程都从同一个管道读取。写入管道的每个字节都将被后两个进程中的 one 使用,但您无法直接控制哪个进程。这本质上并不是错误的,但它很少是您想要的,并且在示例程序中它不会为您可靠地工作。

可能发生的情况是 parent 进程消耗了第一个进程写入的数据,或者至少是 end-of-message 字符,而您打算将其用于第二个 child .第二个 child 然后永远等待读取永远不会到达的数据。当第一个 child 终止时,它甚至没有检测到管道上的 end-of-file,因为第二个 child 本身保持管道的写端打开。 parent 依次永远等待第二个 child 终止。

假设第二个child消耗第一个child写入的所有数据,而parent依次消耗第一个child写入的所有数据第二个 child,您应该为 child2 创建一个单独的管道 --> parent link。这通常是您想要的模式:每个不同的有序端点对都有一个单独的管道。如果您希望在相同的两个进程之间进行双向通信,则每个方向都包括一个单独的管道。

更新:

此外,funcone() 超出了其本地 arr 数组的范围。有效索引为 0 到 4,但该函数中的 for 循环从 0 到 5。