954,492 Members — Technology Publication meets Social Media
Username:
Password:
Lost login information?
Have something to say? Contribute New Article Reply to this Article

Pipe problems in C

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!

BobTheLob
Light Poster
34 posts since Dec 2007
Reputation Points: 10
Solved Threads: 0
 

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;
}
L7Sqr
Practically a Master Poster
657 posts since Feb 2011
Reputation Points: 201
Solved Threads: 124
 

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?

BobTheLob
Light Poster
34 posts since Dec 2007
Reputation Points: 10
Solved Threads: 0
 

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.

L7Sqr
Practically a Master Poster
657 posts since Feb 2011
Reputation Points: 201
Solved Threads: 124
 

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?)

BobTheLob
Light Poster
34 posts since Dec 2007
Reputation Points: 10
Solved Threads: 0
 

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.

L7Sqr
Practically a Master Poster
657 posts since Feb 2011
Reputation Points: 201
Solved Threads: 124
 

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.

nezachem
Posting Shark
903 posts since Dec 2009
Reputation Points: 719
Solved Threads: 194
 

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?

BobTheLob
Light Poster
34 posts since Dec 2007
Reputation Points: 10
Solved Threads: 0
 

Yes, read will wait until there is input before returning to you. This is known as ablocking 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).

L7Sqr
Practically a Master Poster
657 posts since Feb 2011
Reputation Points: 201
Solved Threads: 124
 

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.

BobTheLob
Light Poster
34 posts since Dec 2007
Reputation Points: 10
Solved Threads: 0
 

This article has been dead for over three months

Post: Markdown Syntax: Formatting Help
You