Member Avatar for BobTheLob

Hey guys. I'm working on a project where a parent process forks two children (so Process A is parent to both Process B and Process C). I need the children to write to the pipe so that the parent can see it. When I make a simple one child pipe to the parent, i got it working. But i'm hitting some snags here with two children. Currently, the parent reads nothing, as if the children never got to write to the pipe. I'm not amazing at pipes, so there very well could be a stupid mistake. Here's my code:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>


#define MSGSIZE 37

char *msg1 = "Child process 1 is sending a message!";
char *msg2 = "Child process 2 is sending a message!";


main()
{
    
	char inbuf[MSGSIZE];
	int p[2];
	
	pid_t pid[2];
    

    
    pid[0]=fork();//Parent creates first child
    
    if (pid[0]!=0) { //If pid[0]=0, then process is child and should not fork
        pid[1]=fork(); //current process is parent, so fork a second child
    }    
        
    if (pipe(p)==-1) { //error
        perror("pipe call");
        exit(1);
    }
    
    switch(pid[0]){
        case -1:
            printf("Fork failed");
            exit(2);
            break;
        case 0: //first child is active. Write to pipe
            close(p[0]);
            write(p[1], msg1, MSGSIZE);
            close(p[1]);
            exit(EXIT_SUCCESS);
        default://parent is active. Read pipe messages (2)
            close(p[1]);
            read(p[0], inbuf, MSGSIZE);
            close(p[0]);
            printf("%s\n", inbuf);
            wait(NULL);
    }
    
    switch(pid[1]){
        case -1:
            printf("Fork failed");
            exit(2);
            break;
        case 0: //first child is active. Write to pipe
            close(p[0]);
            write(p[1], msg2, MSGSIZE);
            close(p[1]);
            exit(EXIT_SUCCESS);
        default://parent is active. Read pipe messages (2)
            close(p[1]);
            read(p[0], inbuf, MSGSIZE);
            close(p[0]);
            wait(NULL);
            printf("%s\n", inbuf);
            
    }
    exit(0);
}

How can I get these child processes to write properly so that the parent can read them? Thanks for the help!

I think your shared use of the pipe in each child is causing you trouble. Also, the fork logic there is a little convoluted and harder to follow. Here is something that allows two children to spawn, write to their own pipes, and have the parent receive the messages. It is certainly not generalized for N children, for that you would need an altogether different setup.

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char * m1 = "Child #1";
const char * m2 = "Child #2";

int main () {

    char mbuff[1024] = {0};
    pid_t pid1 = 0, pid2 = 0;
    int pipes1[2] = {0}, pipes2[2] = {0};

    pipe(pipes1); /* for child 1 */

    pid1 = fork ();
    if (pid1 == 0) {
        /* I am the [first] child */
        write (pipes1[1], m1, strlen(m1));
        close (pipes1[0]);
        close (pipes1[1]);
        fprintf (stderr, "First child wrote\n");
        exit (0);
    }

    /* Parent continues here */
    pipe(pipes2);

    pid2 = fork ();
    if (pid2 == 0) {
        /* I am the [second] child */
        write (pipes2[1], m2, strlen(m2));
        close (pipes2[0]);
        close (pipes2[1]);
        fprintf (stderr, "Second child wrote\n");
        exit (0);
    }

    /* Parent continues and reads all data */
    read(pipes1[0], mbuff, 1024);
    printf ("Parent reads: %s\n", mbuff);
    close (pipes1[0]);
    close (pipes1[1]);

    read(pipes2[0], mbuff, 1024);
    printf ("Parent reads: %s\n", mbuff);
    close (pipes2[0]);
    close (pipes2[1]);

    return 0;
}
Member Avatar for BobTheLob

Thanks! I'll try that. But question for you. In line 30 when you fork for a second time, would not the child process also fork, causing grandchildren?

No. If you notice the execution path of the first child is confined to the if { ... } block. At the end of that block there is an exit(0) .
The second fork happens in the parents execution path.

Member Avatar for BobTheLob

That's excellent man! My mind is still trying to fully understand fork and pipe commands, but that code makes a lot of sense (my mind just exploded a bit due to understanding, i've been struggling with this :P).
Although, I did notice that the parent wouldn't always read one of the children. So I put a wait(NULL) command before reading the buffer which allowed the children to finish their writing. Is that a proper fix (it works, but is it good code?)

That should never be the case. Even if the children run last, the read is a blocking call (waits for there to be data before returning) so the parent will just stall until the child eventually writes. Be sure that there is not a copy/paste error in your code. For instance, with the following updates to the above example

/* ... */
    pid1 = fork ();
    if (pid1 == 0) {
        /* I am the [first] child */
        sleep(4);
        write (pipes1[1], m1, strlen(m1));

/* ... */
    pid2 = fork ();
    if (pid2 == 0) {
        /* I am the [second] child */
        sleep(1);
        write (pipes2[1], m2, strlen(m2));
/* ... */

I get the following output:

Second child wrote
First child wrote
Parent reads: Child #1
Parent reads: Child #2

Where that second read blocks until the first one completes.

Hey guys. I'm working on a project where a parent process forks two children (so Process A is parent to both Process B and Process C). I need the children to write to the pipe so that the parent can see it. When I make a simple one child pipe to the parent, i got it working. But i'm hitting some snags here with two children. Currently, the parent reads nothing, as if the children never got to write to the pipe. I'm not amazing at pipes, so there very well could be a stupid mistake. Here's my code:

main()
{
    ...    
    pid[0]=fork();//Parent creates first child
    
    if (pid[0]!=0) { //If pid[0]=0, then process is child and should not fork
        pid[1]=fork(); //current process is parent, so fork a second child
    }    
        
    if (pipe(p)==-1) { //error
        perror("pipe call");
        exit(1);
    }
...
    exit(0);
}

How can I get these child processes to write properly so that the parent can read them? Thanks for the help!

The most serious error here is an order of events. This code first forks, then creates pipes. Each of the 3 processes create a unique, distinctly separate pipe, and they are not related to each other.

On the other hand, what happens in the correct sequence (first create a pipe, then fork, as in L5Sqr example):
fork clones the userspace, yet the pipe, which is a kernel object, is not cloned. That is, both processes refer to the same pipe, and may communicate through it.

Member Avatar for BobTheLob

That should never be the case. Even if the children run last, the read is a blocking call (waits for there to be data before returning) so the parent will just stall until the child eventually writes. Be sure that there is not a copy/paste error in your code. For instance, with the following updates to the above example

/* ... */
    pid1 = fork ();
    if (pid1 == 0) {
        /* I am the [first] child */
        sleep(4);
        write (pipes1[1], m1, strlen(m1));

/* ... */
    pid2 = fork ();
    if (pid2 == 0) {
        /* I am the [second] child */
        sleep(1);
        write (pipes2[1], m2, strlen(m2));
/* ... */

I get the following output:

Second child wrote
First child wrote
Parent reads: Child #1
Parent reads: Child #2

Where that second read blocks until the first one completes.

I'm unsure about the sleep commands (sorry, still a newbie). However, I even tried just copy-pasting your original code, and on occasion (not every time), the first write would not show up.
But what you're saying is that the read command is supposed to wait until something has been written to the pipe, and won't continue till something is in there?

Yes, read will wait until there is input before returning to you. This is known as a blocking call.

I can not explain why the first write would not 'show up.' I don't exactly know what you mean by that - is it that you dont see output from the first child or you can not read from the pipe in the parent? (The two situations are wildly different).

Member Avatar for BobTheLob

It seems like I just don't see the output. There's a blank line where I assume the output of the first child should be.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.