如果我不使用 wait(),带有 forks 的 C 程序仅打印输出

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

我正在用 C 编写一些程序来了解子进程,但我在使用下面的代码时遇到了一些问题。这个想法是使用 exec、未命名管道和 copy2 来模拟带有管道的 bash 命令,但不知何故,只有当我删除第二个和第三个 wait(NULL) 时,该程序似乎才能工作

int pipe_fd[2][2];
enum {READ, WRITE};
void ls() {
    close(pipe_fd[0][READ]);
    dup2(pipe_fd[0][WRITE], STDOUT_FILENO);
    execlp("ls", "ls", "-al", NULL);
}

void grep() {

    close(pipe_fd[0][WRITE]);
    close(pipe_fd[1][READ]);
    dup2(pipe_fd[0][READ], STDIN_FILENO);
    dup2(pipe_fd[1][WRITE], STDOUT_FILENO);
    execlp("grep", "grep", "e", NULL);
}

void wc() {
    close(pipe_fd[1][WRITE]);
    dup2(pipe_fd[1][READ], STDIN_FILENO);
    execlp("wc", "wc", "-l", NULL);
}

int main(void) {
    pipe(pipe_fd[0]);
    pipe(pipe_fd[1]);
    pid_t pid_ls = fork();
    if (pid_ls < 0) exit(EXIT_FAILURE);
    else if (pid_ls == 0) ls();
    wait(NULL);
    
    pid_t pid_grep = fork();
    if (pid_grep < 0) exit(EXIT_FAILURE);
    else if (pid_grep == 0) grep();
    wait(NULL);
    close(pipe_fd[0][WRITE]);
    close(pipe_fd[0][READ]);
    
    
    pid_t pid_wc = fork();
    if (pid_wc < 0) exit(EXIT_FAILURE);
    else if (pid_wc == 0) wc();
    wait(NULL);
    close(pipe_fd[1][WRITE]);
    close(pipe_fd[1][READ]);
    exit(EXIT_SUCCESS);
}

我不太确定可能是什么问题。我很确定即使在运行 exec() 之后我仍然需要 wait()

c linux pipe fork exec
1个回答
0
投票

提前打开管道意味着,在关闭管道之前,每个子进程都将收到每个打开的文件描述符(管道的开放端)的副本。

每个子进程必须关闭它不使用的每个文件描述符,否则该管道的另一端将保持打开状态,导致使用该特定描述符对的进程被阻塞,等待更多数据。 您还必须关闭给

dup2

(第一个参数)的old文件描述符,否则它和

new
文件描述符(第二个参数)都将打开,指向相同的文件描述。 您应该在额外关闭父进程中的所有管道后

收割

您的子进程。 更完整(尽管为了简洁而跳过了一些错误处理):

#include <stdio.h> #include <stdlib.h> #include <sys/wait.h> #include <unistd.h> void open_all_these_pipes(size_t n, int pipes[n][2]) { for (size_t i = 0; i < n; i++) pipe(pipes[i]); } void close_all_these_pipes(size_t n, int pipes[n][2]) { for (size_t i = 0; i < n; i++) { close(pipes[i][0]); close(pipes[i][1]); } } pid_t spawn_child(size_t n, int pipes[n][2], int read_from, int write_to, char * const * argv) { pid_t pid = fork(); if (0 == pid) { if (-1 != read_from) dup2(pipes[read_from][0], STDIN_FILENO); if (-1 != write_to) dup2(pipes[write_to][1], STDOUT_FILENO); close_all_these_pipes(n, pipes); execvp(argv[0], argv); perror(argv[0]); exit(EXIT_FAILURE); } return pid; } int main(void) { char *ls[] = { "ls", "-al", NULL }; char *grep[] = { "grep", "e", NULL }; char *wc[] = { "wc", "-l", NULL }; int pipes[2][2]; open_all_these_pipes(2, pipes); spawn_child(2, pipes, -1, 0, ls); spawn_child(2, pipes, 0, 1, grep); spawn_child(2, pipes, 1, -1, wc); close_all_these_pipes(2, pipes); pid_t pid; int status; while ((pid = wait(&status)) > 0) printf("Child %ld exited with status %d.\n", (long) pid, WEXITSTATUS(status)); }

$ ls -al | grep e | wc -l
19
$ ./a.out 
19
Child 70609 exited with status 0.
Child 70610 exited with status 0.
Child 70611 exited with status 0.
© www.soinside.com 2019 - 2024. All rights reserved.