Created
November 23, 2023 23:59
-
-
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().
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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