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

wqlqzqxt  于 2023-10-16  发布在  其他
关注(0)|答案(1)|浏览(111)

我正在用C写一些程序来学习子进程,下面的代码有点问题。这个想法是使用exec、未命名管道和copy2来模拟bash命令,但不知何故,只有当我删除第二个和第三个等待(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()之后我仍然需要等待()

pjngdqdw

pjngdqdw1#

提前打开管道意味着,在关闭管道之前,每个子节点都将接收到每个打开的文件描述符的副本(管道的开放端)。
然后,每个子进程必须关闭它没有使用的每个文件描述符,否则管道的另一端将保持打开状态,导致正在使用该特定描述符对的进程阻塞,等待更多数据。
您还必须关闭指定给dup2old 文件描述符(第一个参数),否则它和 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.

相关问题