Saturday, 26 May 2012

printf() anomaly after fork()...

For more info visitActually the title of this post is wrong, printf() doesn't show any deviation from its normal behaviour. Everything is fine with printf(), but then, after seeing a couple of examples given below, you will definitely say that something is wrong with printf(), until I explain you the reason behind this behaviour. Now let's see a couple of examples. 1)#include<stdio.h> int main() { int pid; printf("Hello World"); if((pid=fork())==0) { printf("\nHello, I am child\n"); } else if(pid>0) { sleep(1); printf("\nHello, I am Parent\n"); } exit(0); } I wrote sleep(1) in the program because I just wanted the output of child process to come before parent process, otherwise, there is no special significance of sleep(1) in this program. Compile and run this program, and you will find an interesting output. The output of this program will be OUTPUT Hello World Hello, I am child Hello World Hello, I am Parent Did you notice the anomaly in the output? "Hello World" is printed twice on terminal despite of the fact that we have only one print statement printing "Hello World" in our program, and that also before fork(). So, where did this second "Hello World" came from? Now, let's see, what actually happened? We know that printf() is line buffered when it is connected to the terminal ,and fully buffered when connected to any other device. The first printf() statement in the program puts its output in the output buffer connected to STDOUT stream, instead of directly printing it on STDOUT. This is because it has not encountered '\n' yet. So, a hidden output buffer is in existence in the address space of process. Now comes the interesting part, when forking takes place, a new child process is born. We all know that during the forking, complete address space of parent process is copied to child's address space. This hidden buffer connected to STDOUT stream also gets copied to child's address space. After understanding this, let's go with the flow of the program. i) "Hello World" is in the output buffer in the parent's address space after the first printf is executed. It hasn't got flushed to STDOUT stream because, '\n' is not encountered yet. ii) Fork() copies this output buffer with data "Hello World" to the child's address space.Now child also contains an output buffer with data "Hello World" connected to the same stream, i.e STDOUT. iii) The parent process sleeps for 1 sec. Child process executes the printf() statement inside the if { } condition. First '\n' is encountered, and "Hello World" from the output buffer of child is flushed out to STDOUT. After that, "Hello ,I am child" is also flushed to STDOUT. iv) Parent process wakes up after 1 sec and after finding first '\n' , it flushes "Hello world" to STDOUT. After this, it again flushes "Hello, I am Parent" to STDOUT. In the next example, we are redirecting the STDOUT stream of  both child and parent processes to a file. Let's do some slight changes in our program. 2)#include<stdio.h> int main() { int pid; printf("Hello World\n"); if((pid=fork())==0) { printf("Hello, I am child\n"); } else if(pid>0) { sleep(1); printf("Hello, I am Parent\n"); } exit(0); } Note the changes which we have made in our program. All the changes are made inside printf() statements. Run this program. The output will be OUTPUT Hello World Hello, I am child Hello, I am Parent which is absolutely normal, since I have already flushed my data before forking(). Now, run this program as follows ./a.out>temp We are redirecting the STDOUT stream of both the processes to a file, "temp". Open the file "temp "with cat temp. The content of the temp will be Hello World Hello, I am child Hello World Hello, I am Parent Again, there is a reason for this behaviour. When printf() is connected to a stream other than STDOUT then it is fully buffered. This means, even '\n' cannot flush the data. The data is flushed at the end, when both the processes terminate.

No comments:

Post a Comment