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