Zohar81 Zohar81 - 3 months ago 11
C Question

Recursive process tree output is different when forwarded via pipe

I've examined the same piece of code that should create process tree recursively:

void proc_recurse(int layer)
{
if(layer<=0) return;
switch(fork()){
case 0:
proc_recurse(layer-1);
printf("%d: child layer = %d\n", getpid(), layer);
break;
case -1:
printf(stderr,"%d: fork failed\n", getpid());
break;
default:
printf("%d: parent \n", getpid());
break;
}
}

int main (int argc, char *argv[]) {
int i;
int n = atoi(argv[1]);
printf("I'm grandParent %d n = %d \n", getpid(), n);

proc_recurse(n);
}


enter image description here

Obviously, the results are completely different when using pipe. I could observe that the
fork
returns to where the code starts (notice the 'grandParent' message that repeats itself in the output). Can anyone explain to me the nature of this difference.

Answer

The problem is buffering. If you add a call to fflush(stdout) right after the printfs the problem disappears:

void proc_recurse(int layer)
{
    if(layer<=0) return;
    switch(fork()){
    case 0:
        proc_recurse(layer-1);
        printf("%d: child layer = %d\n", getpid(), layer);
        break;
    case -1:
        fprintf(stderr,"%d: fork failed\n", getpid());
        break;
    default:
        printf("%d: parent \n", getpid());
        break;
    }
    fflush(stdout);
}

int  main (int argc, char *argv[]) {
    int i;
    int n = atoi(argv[1]);
    printf("I'm grandParent %d n = %d \n", getpid(), n);
    fflush(stdout);
    proc_recurse(n);
}

Try run:

$ ./a.out 5 | less
I'm grandParent 7124 n = 5 
7124: parent 
7126: parent 
7126: child layer = 5
7127: parent 
7127: child layer = 4
7127: child layer = 5
7128: parent 
7128: child layer = 3
7128: child layer = 4
7128: child layer = 5
7129: parent 
7130: child layer = 1
7130: child layer = 2
7130: child layer = 3
7130: child layer = 4
7130: child layer = 5
7129: child layer = 2
7129: child layer = 3
7129: child layer = 4
7129: child layer = 5

The issue is that stdout is generally unbuffered or line buffered when performing output to the terminal. However once you pipe the output then a buffer is used (something like a 4K buffer, but it depends). This means that the "grandParent" line is not given in output from the main process immediately, the fork then makes it so that the child processes now have a copy of this buffer containing this line and when they write and finally perform the output you end up with repeated contents.

This is a common mistake when writing programs using fork etc. Before performing a fork you should fflush the output to avoid this can of issue.

Comments