Reputation: 73
Let's say within a program I want to execute two processes, one to execute a ls -al
command, then piping the result into the wc
command, and displaying the output on the terminal. How can I do this using pipe file descriptors? So far the code I have written:
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]);
}
An example would be greatly helpful.
Upvotes: 4
Views: 16206
Reputation: 3076
I've commented out the code as thoroughly as possible. I've also added some minor error handling for handling process forking and command execution. As @msw pointed out, you only need a single pipe to communicate unidirectionally between two processes.
// 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;
}
Compiling and running it:
$ ls
a.out error.log ls-wc-pipes.c out.log so-pipes.c
$ gcc ls-wc-pipe.c && ./aout
8 65 449
Your result will vary depending on what you've in the directory that ls-wc-pipe.c
runs.
Upvotes: 0
Reputation: 43487
Your example code was syntactically and semantically broken (e.g. pipefd2 not decared, confusion between pipefd and pipefd2, etc.) Since this smells like homework, please make sure you understand my annotations below and ask more if you need to. I've omitted error checks on pipe, fork and dup, but they should be there, ideally.
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;
}
Upvotes: 12