Skip to content

Instantly share code, notes, and snippets.

@uzluisf
Created November 23, 2023 23:59
Show Gist options
  • Save uzluisf/72d9ee885d0dcb7253615e69cc32ed1c to your computer and use it in GitHub Desktop.
Save uzluisf/72d9ee885d0dcb7253615e69cc32ed1c to your computer and use it in GitHub Desktop.
C program that implements a pipeline between two child processes using fork(), dup2(), and execlp().
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
/*
* C program that implements the pipeline
*
* ls -l /tmp/ | wc -l
*
* to count the number of files under the /tmp/ directory.
*
* The parent process declares a pipe, and then forks twice to create two
* childs processes for the ls and wc commands. The process doesn't send or
* receive any output from its children, it simply forks them and then waits
* for them to finish execution.
*
* The first child process represents the ls command, and reassigns its STDOUT
* to the pipe's write end so the output from the process doesn't go to the
* console and instead goes to the pipe's write end.
*
* The second child process represents the wc command, and reassign its STDIN
* to the pipe's read end so the input to the process comes from the pipe, and
* not from the keyboard.
*
* NOTE: It might benefit from more error handling.
*/
int main(int argc, char *argv[]) {
pid_t ls_pid, wc_pid;
int pipefd[2];
pipe(pipefd);
if ((ls_pid = fork()) == 0) {
// we assign this process's output to the pipe's write end, i.e.,
// instead of sending output to the screen, it sends it to the pipe's
// write end.
dup2(pipefd[1], STDOUT_FILENO);
// now this process's stdout refers to the pipe's write end too so we
// can close this descriptor.
close(pipefd[1]);
// this process doesn't use the pipe's read end, and thus we close this
// file descriptor.
close(pipefd[0]);
// replace process's current image with this new process image, i.e., the ls command.
if (execlp("ls", "ls", "-l", "/tmp/", (char *) NULL) < 0) {
fprintf(stderr, "failed trying to execute the ls command");
exit(0);
};
}
else if (ls_pid < 0) {
fprintf(stderr, "failed forking ls process");
}
if ((wc_pid = fork()) == 0) {
// we assign this process's input to the pipe's read end, i.e., instead
// of taking input from the keyboard, it takes it from the pipe's read end.
dup2(pipefd[0], STDIN_FILENO);
// now this process's stdin refers to the pipe's read end too so we
// can close this descriptor.
close(pipefd[0]);
// this process doesn't use the pipe's write end, and thus we close this
// file descriptor.
close(pipefd[1]);
// replace process's current image with this new process image, i.e., the wc command.
if (execlp("wc", "wc", "-l", (char *) NULL) < 0) {
fprintf(stderr, "failed trying to execute the wc command");
exit(0);
};
}
else if (wc_pid < 0) {
fprintf(stderr, "failed forking wc process");
exit(0);
}
// the parent process doesn't use the pipe so we close both ends. Also
// needed to send EOF so the children can continue (children blocks until
// all input has been processed).
close(pipefd[0]);
close(pipefd[1]);
// the parent process waits for both child process to finish their execution.
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 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment