假设在一个程序中我想执行两个进程,一个进程执行
ls -al
命令,然后将结果通过管道传输到 wc
命令,并在终端上显示输出。如何使用管道文件描述符来做到这一点?到目前为止我写的代码:
int main(int argc, char* argv[]) {
int pipefd[2];
int pipefd2[2];
pipe(pipefd2);
if ((fork()) == 0) {
dup2(pipefd2[1], STDOUT_FILENO);
close(pipefd2[0]);
close(pipefd2[1]);
execl("ls", "ls", "-al", NULL);
exit(EXIT_FAILURE);
}
if ((fork()) == 0){
dup2(pipefd2[0], STDIN_FILENO);
close(pipefd2[0]);
close(pipefd2[1]);
execl("/usr/bin/wc", "wc", NULL);
exit(EXIT_FAILURE);
}
close(pipefd[0]);
close(pipefd[1]);
close(pipefd2[0]);
close(pipefd2[1]);
}
一个例子会很有帮助。
您的示例代码在语法和语义上都被破坏了(例如,pipefd2 没有被声明,pipefd 和 pipelinefd2 之间的混淆等)因为这听起来像家庭作业,请确保您理解我下面的注释,并在需要时询问更多信息。我省略了对 pipeline、fork 和 dup 的错误检查,但理想情况下它们应该存在。
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
pipe(pipefd);
// this child is generating output to the pipe
//
if ((ls_pid = fork()) == 0) {
// attach stdout to the left side of pipe
// and inherit stdin and stdout from parent
dup2(pipefd[1],STDOUT_FILENO);
close(pipefd[0]); // not using the right side
execl("/bin/ls", "ls","-al", NULL);
perror("exec ls failed");
exit(EXIT_FAILURE);
}
// this child is consuming input from the pipe
//
if ((wc_pid = fork()) == 0) {
// attach stdin to the right side of pipe
// and inherit stdout and stderr from parent
dup2(pipefd[0], STDIN_FILENO);
close(pipefd[1]); // not using the left side
execl("/usr/bin/wc", "wc", NULL);
perror("exec wc failed");
exit(EXIT_FAILURE);
}
// explicitly not waiting for ls_pid here
// wc_pid isn't even my child, it belongs to ls_pid
return EXIT_SUCCESS;
}
我已经尽可能彻底地注释掉了代码。我还添加了一些小错误处理来处理进程分叉和命令执行。正如 @msw 所指出的,您只需要一个管道即可在两个进程之间进行单向通信。
// ls-wc-pipe.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
int pipefd[2];
pid_t ls_pid, wc_pid;
pipe(pipefd);
// CHILD PROCESS: ls
if ((ls_pid = fork()) == 0) {
// We close-and-then-connect STDOUT to the pipe's write end so the process
// write to the pipe instead of the screen/terminal.
dup2(pipefd[1], STDOUT_FILENO);
// Since STDOUT_FILENO also refers to the pipe's write end, we can
// close this file descriptor; it's no longer needed.
close(pipefd[1]);
// This process doesn't read from the pipe's read end so this is unused
// and we should close it.
close(pipefd[0]);
// Execute the `ls` command, and exit if errors out.
if ((execl("/bin/ls", "ls", "-al", (char *) NULL)) < 0) exit(0);
}
else if (ls_pid < 0) {
fprintf(stderr, "failed to fork ls process");
exit(0);
}
// CHILD PROCESS: wc
if ((wc_pid = fork()) == 0) {
// We close-and-then-connect STDIN to the pipe's read end so the process
// read from the pipe instead of from the keyboard.
dup2(pipefd[0], STDIN_FILENO);
// Since STDIN_FILENO also refers to the pipe's read, we can
// close this file descriptor; it's no longer needed.
close(pipefd[0]);
// This process doesn't write to the pipe's write end so this is unused
// and we should close it.
close(pipefd[1]);
// Execute the `wc` command, and exit if errors out.
if ((execl("/usr/bin/wc", "wc", (char *) NULL)) < 0) exit(0);
}
else if (wc_pid < 0) {
fprintf(stderr, "failed to fork wc process");
exit(0);
}
// PARENT PROCESS
// The parent process isn't using the pipe, however these descriptors are
// another references to the pipe's read and write ends and we must close
// them. Otherwise, it doesn't send the EOF so the children can continue
// (children block until all input has been processed).
close(pipefd[0]);
close(pipefd[1]);
// The parent process waits for both children to finish before exiting.
int ls_status, wc_status;
pid_t ls_wpid = waitpid(ls_pid, &ls_status, 0);
pid_t wc_wpid = waitpid(wc_pid, &wc_status, 0);
// Return main's status based on whether the parent process waited both
// child processes successfully. Status based only on `ls_status`.
return
ls_pid == ls_wpid && WIFEXITED(ls_status) &&
wc_pid == wc_wpid && WIFEXITED(wc_status)
? WEXITSTATUS(ls_status)
: -1;
}
编译并运行:
$ ls
a.out error.log ls-wc-pipes.c out.log so-pipes.c
$ gcc ls-wc-pipe.c && ./aout
8 65 449
您的结果将根据
ls-wc-pipe.c
运行的目录中的内容而有所不同。