Reputation: 383
I want to make two pipes between 3 child processes, but I firstly want to get the first pipe pipe1
working between two fork processes. The problem is that when i dup the stdout from the first process and likewise dup the stdin from the other process, i don't get output.
int main()
{
int pipe1[2];
int pipe2[2];
pipe(pipe1);
pipe(pipe2);
sem_t mutex_pipe1;
sem_t mutex_pipe2;
sem_init(&mutex_pipe1, 0, 0);
sem_init(&mutex_pipe2, 0, 1);
if (fork()==0) { //process 1
close(1); /* close normal stdout */
dup(pipe1[1]); /* make stdout same as pfds[1] */
sem_post(&mutex_pipe1);
execlp("ls", "ls", NULL);
}
if (fork()==0){ //process 2
sem_wait(&mutex_pipe1);
close(0);
dup(pipe1[0]);
dup2(pipe1[1], 1) //want to open stdout again.
sem_post(&mutex_pipe1);
execlp("wc", "wc", "-l", NULL);
}
Upvotes: 1
Views: 698
Reputation: 383
Thanks to John Bollinger given the inspiration. The following will be an answer of using three child processes with two pipes executing ls|sort|less|
:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define DIE do { perror(NULL); exit(1); } while (0)
#define DO_OR_DIE(f) do { if ((f) < 0) DIE; } while (0)
int main(void)
{
int pipe1[2]; //pipe1
int pipe2[2]; //pipe2
DO_OR_DIE(pipe(pipe1)); //initialize pipe1
DO_OR_DIE(pipe(pipe2)); //initialize pipe2
switch (fork()) {
case -1: // failed to fork
DIE;
case 0: // child process 1
/* make stdout a copy of the write end of pipe1 */
DO_OR_DIE(dup2(pipe1[1], STDOUT_FILENO));
/* close the excess file descriptor */
DO_OR_DIE(close(pipe1[1]));
execlp("ls", "ls", NULL);
/* execlp returns only on failure */
DIE;
/* default: parent process */
}
/* close the parent's copy of pipe1 write end. Avoids child2 hanging */
DO_OR_DIE(close(pipe1[1]));
switch (fork()) {
case -1: // failed to fork
DIE;
case 0: // child process 2
/* make stdin a copy of the read end of pipe1 */
DO_OR_DIE(dup2(pipe1[0], STDIN_FILENO));
/* close the excess file descriptor */
DO_OR_DIE(close(pipe1[0]));
/* make stdout a copy of the write end of pipe2 */
DO_OR_DIE(dup2(pipe2[1], STDOUT_FILENO));
/* close the excess file descriptor */
DO_OR_DIE(close(pipe2[1]));
execlp("sort", "sort", NULL);
/* execlp returns only on failure */
DIE;
/* default: parent process */
}
/* close the parent's copy of pipe1 read end, for consistency and tidiness*/
DO_OR_DIE(close(pipe1[0]));
/* close the parent's copy of pipe2 write end. Avoids child3 hanging */
DO_OR_DIE(close(pipe2[1]));
switch (fork()) {
case -1: // failed to fork
DIE;
case 0: // child process 3
/* make stdin a copy of the read end of pipe2 */
DO_OR_DIE(dup2(pipe2[0], STDIN_FILENO));
/* close the excess file descriptor */
DO_OR_DIE(close(pipe2[0]));
execlp("less", "less", NULL);
/* execlp returns only on failure */
DIE;
/* default: parent process */
}
/* close the parent's copy of pipe2 read end, for consistency and tidiness*/
DO_OR_DIE(close(pipe2[0]));
/* wait for child processes to stop */
DO_OR_DIE(wait(NULL));
DO_OR_DIE(wait(NULL));
DO_OR_DIE(wait(NULL));
return 0;
}
Upvotes: 0
Reputation: 180898
Your semaphores are not doing anything useful for you in the code you presented. It's unclear to me what you even suppose they might do, especially once your child processes invoke exec()
-family functions.
Moreover, you cannot "open stdout again" once it is closed. You can associate file descriptor 1 (and thus stdout
) with a different file, but you cannot get back a file once you have closed all the FDs associated with it, except by open()
ing or fopen()
ing it again.
However, you seem not to appreciate that after a fork()
, the child and parent processes do not share memory, at least not in the sense that ordinary memory writes by one are visible to the other. Thus, in your original code, when the first child closes its stdout
, the parent is not affected, and the second child doesn't inherit any change or any need to recover the original stdout
.
On the other hand, be aware that when you fork()
a child process, and it inherits the parent's open file descriptors, that results in two processes having each of those file descriptors open. Also, when you dupe an FD, you have a second FD open on the same file. You need to close all of them before the underlying open file is closed, and failing to do so can have unwanted effects. In your case in particular, your second child process hangs because it's waiting for its stdin
to be closed on the other side, and although the first child process closes its copy, the parent process still holds an open FD on it. The wc
program produces no output until it has consumed its whole input. You would probably see some output if you were instead exec'ing, say, cat
.
Furthermore, the parent should normally wait on its child processes to complete before exiting itself, and it must eventually wait on them to clean up after them if it continues performing other work.
Finally, always, always check return values for error codes.
Here's a pretty minimal version of how to set up a pair of child processes that communicate unidirectionally via a pipe:
#define DIE do { perror(NULL); exit(1); } while (0)
#define DO_OR_DIE(f) do { if ((f) < 0) DIE; } while (0)
int main(void)
{
int pipe1[2];
DO_OR_DIE(pipe(pipe1));
switch (fork()) {
case -1: /* failed to fork */
DIE;
case 0: /* child process 1 */
/* make stdout a copy of the write end of the pipe */
DO_OR_DIE(dup2(pipe1[1], STDOUT_FILENO));
/* close the excess file descriptor */
DO_OR_DIE(close(pipe1[1]));
execlp("ls", "ls", NULL);
/* execlp returns only on failure */
DIE;
/* default: parent process */
}
/* close the parent's copy of this FD, lest child2 hang */
DO_OR_DIE(close(pipe1[1]));
switch (fork()) {
case -1: /* failed to fork */
DIE;
case 0: /* child process 1 */
/* make stdout a copy of the write end of the pipe */
DO_OR_DIE(dup2(pipe1[0], STDIN_FILENO));
/* close the excess file descriptor */
DO_OR_DIE(close(pipe1[0]));
execlp("wc", "wc", "-l", NULL);
/* execlp returns only on failure */
DIE;
/* default: parent process */
}
/* close the parent's copy of this FD, for consistency and tidiness*/
DO_OR_DIE(close(pipe1[0]));
/* wait for two child processes to stop */
DO_OR_DIE(wait(NULL));
DO_OR_DIE(wait(NULL));
return 0;
}
Upvotes: 4