尝试在 C shell 中实现管道挂起并且未运行命令

doc*_*pus 3 c unix pipe

我正在尝试运行这个命令ps -j | more。我认为我已经正确设置了管道,但由于某种原因它只是挂起:

在此输入图像描述

我正在调用一个运行的叉子ps -j和另一个运行的叉子more,并用管道连接它们。

由于某种原因,这仍然没有按预期工作。

代码如下:

#define BUF_SIZE 100

int main() {

    pid_t pid1, pid2;
    int save_stdin, save_stdout;
    int fd[2];

    char *argv1[] = { "/bin/ps", "-j", NULL };
    char *argv2[] = { "/bin/more", NULL };

    char prompt[] = "Press enter: ";
    char buffer[BUF_SIZE];

    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);

    ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);
    buffer[readIn - 1] = '\0';

    printf("readIn: %d\n", readIn);

    pipe(fd);

    pid1 = fork();
    if (pid1 == 0) { // child
        close(fd[0]);
        save_stdout = dup(1);
        dup2(fd[1], STDOUT_FILENO);
        execvp(argv1[0], argv1);
        close(fd[1]);
    } else { // parent
        pid2 = fork();
        if(pid2 == 0) {
            save_stdin = dup(0);
            dup2(fd[0], STDIN_FILENO);
            execvp(argv2[0], argv2);
            close(fd[0]);
        } else {

        }
    }

    dup2(save_stdin, 0);
    dup2(save_stdout, 1);
    close(save_stdin);
    close(save_stdout);

    int i = 1;
    do {
        wait(NULL);
    } while (i-- > 0);


    exit(0);

}
Run Code Online (Sandbox Code Playgroud)

非常感谢任何帮助!

编辑:

dup2()我尝试在 after但 before关闭管道execvp(),但它仍然挂起:

pipe(fd);

pid1 = fork();
if (pid1 == 0) { // child
    dup2(fd[1], STDOUT_FILENO);
    close(fd[1]);
    close(fd[0]);
    int res1 = execvp(argv1[0], argv1);
    printf("exec1: %d\n", res1);
} else { // parent
    pid2 = fork();
    if(pid2 == 0) {
        dup2(fd[0], STDIN_FILENO);
        close(fd[0]);
        close(fd[1]);
        int res2 = execvp(argv2[0], argv2);
        printf("exec2: %d\n", res2);
    } else {

    }
}

close(fd[1]);
close(fd[0]);
printf("finished\n");

int i = 1;
do {
    wait(NULL);
} while (i-- > 0);
Run Code Online (Sandbox Code Playgroud)

Jon*_*ler 5

您没有关闭子级中管道的足够文件描述符,或者父级中的任何管道的文件描述符。

\n\n

经验法则:如果您\n \n没有连接到标准输入或标准输出的管道末端,请尽快dup2()关闭\n \nas返回的两个原始文件描述符。\n特别是,您应该在使用任何文件之前关闭它们。 pipe()\n exec*()\n函数家族的成员。

\n\n

dup()如果您使用\n \nor\n fcntl()\n复制描述符,则该规则也适用F_DUPFD

\n\n

这是正确关闭管道的工作代码。execvp()我删除了命令的路径部分,因为(a)它们对于我的机器来说是错误的,并且(b)如果指定命令的路径,则使用没有意义。我报告了大多数错误(但我不检查read()和的结果write()调用的结果。我还报告子级的退出状态。如果子级无法执行命令,则报告并退出。您的代码带有 \'save_stdin`等等更加令人困惑,因为变量在父级中未初始化,这是序列所在的位置:

\n\n
dup2(save_stdin, 0);\ndup2(save_stdout, 1);\nclose(save_stdin);\nclose(save_stdout);\n
Run Code Online (Sandbox Code Playgroud)\n\n

被处决。你和我都不知道这两个dup2()调用做了什么 \xe2\x80\x94 但它们很可能失败了,或者它们对您的标准 I/O 通道做了一些奇怪的事情。

\n\n
#include <stdio.h>\n#include <unistd.h>\n#include <stdlib.h>\n\n#define BUF_SIZE 100\n\nint main(void)\n{\n    pid_t pid1, pid2;\n    int fd[2];\n\n    char *argv1[] = { "ps", "-j", NULL };   // Remove PATH because using execvp()\n    char *argv2[] = { "more", NULL };       // Remove PATH because using execvp()\n\n    char prompt[] = "Press enter: ";\n    char buffer[BUF_SIZE];\n\n    write(STDOUT_FILENO, prompt, sizeof(prompt) - 1);\n\n    ssize_t readIn = readIn = read(STDIN_FILENO, buffer, BUF_SIZE);\n    buffer[readIn - 1] = \'\\0\';\n\n    printf("readIn: %zd\\n", readIn);\n\n    if (pipe(fd) < 0)\n    {\n        fprintf(stderr, "failed to create pipe\\n");\n        exit(EXIT_FAILURE);\n    }\n\n    if ((pid1 = fork()) < 0)\n    {\n        fprintf(stderr, "failed to fork - 1\\n");\n        exit(EXIT_FAILURE);\n    }\n\n    if (pid1 == 0)   // child\n    {\n        dup2(fd[1], STDOUT_FILENO);\n        close(fd[0]);\n        close(fd[1]);\n        execvp(argv1[0], argv1);\n        fprintf(stderr, "failed to execute %s\\n", argv1[0]);\n        exit(EXIT_FAILURE);\n    }\n\n    if ((pid2 = fork()) < 0)\n    {\n        fprintf(stderr, "failed to fork - 2\\n");\n        exit(EXIT_FAILURE);\n    }\n\n    if (pid2 == 0)\n    {\n        dup2(fd[0], STDIN_FILENO);\n        close(fd[0]);\n        close(fd[1]);\n        execvp(argv2[0], argv2);\n        fprintf(stderr, "failed to execute %s\\n", argv2[0]);\n        exit(EXIT_FAILURE);\n    }\n\n    close(fd[0]);\n    close(fd[1]);\n\n    int corpse;\n    int status;\n    while ((corpse = wait(&status)) > 0)\n        fprintf(stderr, "%d: child %d exited with status 0x%.4X\\n",\n                (int)getpid(), corpse, (unsigned)status);\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

该程序是pipe41(对输出进行一些小的编辑以删除实际上不需要让您了解我在其他窗口中所做的事情):

\n\n
$ ./pipe41\nPress enter: \nreadIn: 1\nUSER              PID  PPID  PGID   SESS JOBC STAT   TT       TIME COMMAND\njonathanleffler  6618  6617  6618      0    1 S    s000    0:00.11 -bash\njonathanleffler  6629  6628  6629      0    1 S+   s001    0:00.04 -bash\njonathanleffler  6660  6645  6660      0    1 S+   s002    0:00.04 -bash\njonathanleffler  6716  6695  6716      0    1 S+   s003    0:00.04 -bash\njonathanleffler  6776  6746  6776      0    1 S+   s004    0:00.04 -bash\njonathanleffler  6800  6771  6800      0    1 S+   s005    0:00.04 -bash\njonathanleffler  7487  7486  7487      0    1 S    s006    0:00.04 -bash\njonathanleffler  9558  9557  9558      0    1 S    s007    0:00.06 -bash\njonathanleffler 10375  9558 10375      0    1 S+   s007    0:00.01 ./pipe41\njonathanleffler 10377 10375 10375      0    1 S+   s007    0:00.00 more\n(END)10375: child 10376 exited with status 0x0000\n10375: child 10377 exited with status 0x0000\n$\n
Run Code Online (Sandbox Code Playgroud)\n