wc: 'standard input': 错误的文件描述符

wc: 'standard input': Bad file descriptor

我正在使用 Exec 函数系列在 C 语言中模拟 bash 命令。

这只是更大项目的部分代码。为了简单起见,现在大部分内容都是硬编码的,但将来我需要使用管道或重定向运算符执行任何 bash 命令,这就是代码处于循环中的原因。我知道我可以使用单个文件描述符(没有 2d 数组)来实现当前示例的结果,但我需要概括多个 |, > 命令的代码。我无法删除循环。

即使在正确关闭所有描述符后,我仍收到错误的描述符错误。找不到原因。

int main()
{
    int numOfCmds = 2;
    int numOfPipes = numOfCmds -1;
    int pipes[numOfPipes][2];

    for(int i=0;i<numOfPipes;i++)
        if(pipe(pipes[i])<0) return 1;

    for(int i=0;i<numOfCmds;i++)
    {
        int child = fork();
        if(child == 0)
        {
            if(i==0)
            {
                for(int i=0;i<numOfPipes;i++)
                {
                    close(pipes[i][0]);
                }
                printf("hey\n");
                dup2(pipes[i][1], STDOUT_FILENO);
                close(pipes[i][1]);
                char *cmd1_args[3] = {"ls", "-l", NULL};
                execvp(cmd1_args[0], cmd1_args);
            }
            if(i==1)
            {
                for(int i=0;i<numOfPipes;i++)
                {
                    close(pipes[i][1]);
                }
                printf("bye\n");
                dup2(pipes[i][0], STDIN_FILENO);
                close(pipes[i][0]);
                char *cmd2_args[3] = {"wc", "-l", NULL};
                execvp(cmd2_args[0], cmd2_args);
            }

            return 0;
        }
    }
    for(int i=0;i<numOfCmds;i++)
    {
        for(int j=0;i<2;j++)
            close(pipes[i][j]);
    }

    for(int i=0;i<numOfCmds;i++) wait(NULL);

    return 0;

错误:

hey bye wc: 'standard input': Bad file descriptor 0 wc: -: Bad file descriptor Segmentation fault (core dumped)

您的问题是您正在重用(嵌套)i 的定义。使用 GCC 的 -Wshadow 选项来避免这样做。 i 的值有时会超出文件描述符对数组的末尾,这会导致混乱(未定义的行为)。

你有:

    for(int i=0;i<numOfCmds;i++)
    {
        int child = fork();
        if(child == 0)
        {
            if(i==0)
            {
                for(int i=0;i<numOfPipes;i++)
                {
                    close(pipes[i][0]);
                }
                printf("hey\n");
                dup2(pipes[i][1], STDOUT_FILENO);
                close(pipes[i][1]);
                char *cmd1_args[3] = {"ls", "-l", NULL};
                execvp(cmd1_args[0], cmd1_args);
            }
            if(i==1)
            {
                for(int i=0;i<numOfPipes;i++)
                {
                    close(pipes[i][1]);
                }
                printf("bye\n");
                dup2(pipes[i][0], STDIN_FILENO);
                close(pipes[i][0]);
                char *cmd2_args[3] = {"wc", "-l", NULL};
                execvp(cmd2_args[0], cmd2_args);
            }

            return 0;
        }
    }

你的(外)循环控制变量是i;然后你有一个由关闭一些管道的不同 i 控制的内部循环。我使用的 GCC 报告说您正在 dup2() 调用 wc.

中越界访问 pipes[i][0]

您需要重新使用 i(使用一些不同的名称 — jk(对于 'kids'?)是可用的)。并且 re-review 您正在仔细关闭哪些文件管道描述符。

您的父循环关闭文件描述符是:

    for(int i=0;i<numOfCmds;i++)
    {
        for(int j=0;i<2;j++)
            close(pipes[i][j]);
    }

您需要使用 numOfPipes 而不是 numOfCmds,并且您需要您的内部循环来测试 j,而不是 i

这是一些经过大量检测的有效代码。但修复不是很普遍:

/* SO 7165-1018 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void)
{
    int numOfCmds = 2;
    int numOfPipes = numOfCmds - 1;
    int pipes[numOfPipes][2];

    for(int i=0;i<numOfPipes;i++)
        if(pipe(pipes[i])<0) return 1;

    for (int i = 0; i < numOfPipes; i++)
        printf("%d: pipe[%d][0] = %d; pipe[%d][1] = %d\n",
               getpid(), i, pipes[i][0], i, pipes[i][1]);

    for(int i=0;i<numOfCmds;i++)
    {
        int child = fork();
        printf("%d: child = %d\n", getpid(), child);
        if(child == 0)
        {
            if(i==0)
            {
                for(int i=0;i<numOfPipes;i++)
                {
                    printf("%d: ls: close %d\n", getpid(), pipes[i][0]);
                    close(pipes[i][0]);
                }
                printf("%d: hey\n", getpid());
                dup2(pipes[i][1], STDOUT_FILENO);
                printf("%d: ls: close %d\n", getpid(), pipes[i][1]);
                close(pipes[i][1]);
                char *cmd1_args[3] = {"ls", "-l", NULL};
                execvp(cmd1_args[0], cmd1_args);
                fprintf(stderr, "failed to execute %s\n", cmd1_args[0]);
                exit(EXIT_FAILURE);
            }
            if(i==1)
            {
                for(int i=0;i<numOfPipes;i++)
                {
                    printf("%d: wc: close %d\n", getpid(), pipes[i][1]);
                    close(pipes[i][1]);
                }
                printf("%d: bye\n", getpid());
                dup2(pipes[0][0], STDIN_FILENO);
                printf("%d: wc: close %d\n", getpid(), pipes[0][0]);
                close(pipes[0][0]);
                char *cmd2_args[3] = {"wc", "-l", NULL};
                execvp(cmd2_args[0], cmd2_args);
                fprintf(stderr, "failed to execute %s\n", cmd2_args[0]);
                exit(EXIT_FAILURE);
            }

            return 0;
        }
    }

    for(int i=0;i<numOfPipes;i++)
    {
        for(int j=0;j<2;j++)
        {
            printf("parent (%d): close %d\n", getpid(), pipes[i][j]);
            close(pipes[i][j]);
        }
    }

    for(int i=0;i<numOfCmds;i++)
    {
        int corpse;
        int status;
        if ((corpse = wait(&status)) > 0)
            printf("%d: child %d exited with status 0x%.4X\n", getpid(), corpse, status);
    }

    return 0;
}

一个示例 运行(程序 sh83,编译自 sh83.c)产生:

42654: pipe[0][0] = 3; pipe[0][1] = 4
42654: child = 42655
42655: child = 0
42655: ls: close 3
42654: child = 42656
parent (42654): close 3
parent (42654): close 4
42655: hey
42656: child = 0
42656: wc: close 4
42656: bye
42656: wc: close 3
     128
42654: child 42655 exited with status 0x0000
42654: child 42656 exited with status 0x0000

如果您使用多个进程,我发现在大多数消息前加上 PID 会很有帮助 — 如输出所示。