Richard Martinez
Richard Martinez

Reputation: 712

fork() causes column headers to print for each process

I'm writing a simple C program using fork() to create a binary tree of processes. I am able to get all the output I need (pid's of process, its parent, and its two children). Unfortunately, each forked process wants to print out the column headers. How do I make sure that the printf for the headers is executed only once?

# include <stdio.h>
# include <stdlib.h>
# include <sys/types.h>
# include <unistd.h>
# include <sys/wait.h>

int main(int argc, char *argv[]){

//Declarations
int i;
int child_1_pid, child_2_pid;
int num_levels = atoi(argv[1]);

//Output banners
//execlp("/bin/echo", "echo", "Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID", (char *) NULL);
//if(getpid() > 0)
printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

//Creates binary tree of processes
for(i = 0; i < num_levels; i++){
    if((child_1_pid = fork()) && (child_2_pid = fork())){
        printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);
        sleep(2); //prevents parent from terminating before child can get ppid (parent's pid)
        break; //why?       
    }   
}//end for

printf("\n"); //EXPLAIN ME!!
exit(0);
}//end main

There's some more code (error checking really), but my real problem is that the printf under the output banners section executes multiple times, giving output like this (but correctly aligned):

Level Procs   Parent  Child1  Child2
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
No.   ID  ID  ID  ID
0 30796   24743   30797   30798
1 30797   30796   30799   30800
1 30798   30796   30801   30802

I've tried a few ideas (including those commented out under the banner section), but nothing seems to work and most "fixes" make the problem even worse!

Upvotes: 1

Views: 484

Answers (4)

R.. GitHub STOP HELPING ICE
R.. GitHub STOP HELPING ICE

Reputation: 215173

Read 2.5.1 here:

http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html

Note that after a fork(), two handles exist where one existed before. The application shall ensure that, if both handles can ever be accessed, they are both in a state where the other could become the active handle first. The application shall prepare for a fork() exactly as if it were a change of active handle. (If the only action performed by one of the processes is one of the exec functions or _exit() (not exit()), the handle is never accessed in that process.)

What this means is that, before calling fork you should call fflush on any streams that you intend to use in both processes after the fork.

Upvotes: 0

SiegeX
SiegeX

Reputation: 140227

Replace:

printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

With:

puts("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

Replace:

printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);

With:

printf("%d\t%d\t%d\t%d\t%d\n", i, getpid(), getppid(), child_1_pid, child_2_pid);

Remove:

printf("\n");

Upvotes: 1

DarkDust
DarkDust

Reputation: 92306

First, the if in the for-loop does not behave as you want it to. Remember that after the fork, it returns the child PID in the parent process and 0 in the child. So inside the loop, the first fork assigns a value to child_1_pid in the parent and continues to the second clause. The child does not enter the if but continues to the next for-loop iteration. The very same happens with the second clause. So only the main process should ever be able to enter the body of the if, but no child process. I wonder why the output suggests otherwise.

So to get your "binary tree", you should actually have this:

// COMPLETELY UNTESTED
for(i = 0; i < num_levels; i++){
    if (!(child_1_pid = fork()) || !(child_2_pid = fork())) {
        printf("\n%d\t%d\t%d\t%d\t%d", i, getpid(), getppid(), child_1_pid, child_2_pid);
        // A child process, go on to next iteration.
        continue;
    }

    // A parent process. Wait for children, then stop.
    if (child_1_pid) wait();
    if (child_2_pid) wait();
    break;
}

The strange output of the banners has to do with flushing of streams. Normally, fprintf only flushed on newline (\n), IIRC. So there's still stuff in the buffer after the fork that has not been flushed yet, and each child runs printf("\n"); and thus flushes out the buffer content.

The solution is to either add a "\n" to the end of the very first printf, or call fflush(stdout); before the for loop.

Upvotes: 3

Will
Will

Reputation: 895

Here's something to try, although I'm a little rusty with this stuff. In the line where you print out your banners:

printf("Level\tProcs\tParent\tChild1\tChild2\nNo.\tID\tID\tID\tID");

It may be that everything after the \n is being left in the ouput buffer, so it's still there when each child is forked. Try adding another \n at the end of that printf, and removing the \n from the beginning of the printf inside the loop.

Upvotes: 2

Related Questions