Statement before fork() printing twice [duplicate] - c

This question already has answers here:
printf anomaly after "fork()"
(3 answers)
Closed 8 years ago.
I was experimenting with fork() and re-direction to check whether the re-directions done in the parent apply to the child too. I wrote the following simple program
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main ()
{
freopen( "error.txt", "w+t", stdout ); // From now on, stdout = error.txt
printf (" ERROR! WHY DONT U UNDERSTAND?\n");
if ( fork() == 0 )
{
printf(" I AM CHILD\n");
exit(0);
}
else-
{
printf (" EITHER I AM A PARENT OR SOMETHING GOT SCREWED\n");
}
return 0;
}
The output ( error.txt ) I got is
ERROR! WHY DONT U UNDERSTAND?
EITHER I AM A PARENT OR SOMETHING GOT SCREWED
ERROR! WHY DONT U UNDERSTAND?
I AM CHILD
Surprisingly, ERROR! WHY DONT U UNDERSTAND? is printing twice even though it appears much before the fork() is called and should only be printed once by the parent.
Can anyone shed some light on this?

Since after reopen the stream is non-interactive, it's fully buffered and doesn't flush on '\n'. Before fork is called the buffer still contains the message, and after fork this buffered message was duplicated (because both processes got their own copies of stdout) and then flushed by both the parent and the child. See part 7.19.3 of C standard.
You can avoid such behavior by calling fflush just before fork.

It's because of buffering. Do a fflush right after printf.
Both processes end up with the same copy of stdio's internal stuff and both proceed to flush it at exit. You might also prevent it from happening if you call _exit in the child.

flushing the buffer will solve the problem.
use fflush just after the print statement.

It seems that the ERROR! WHY DONT U UNDERSTAND is still buffered after forking and gets written by both processes.
If you add
fflush(stdout);
right after your first printf() the internal buffer is flushed and it only appears once in your file.

Related

Unexpected output on a fork call in C [duplicate]

This question already has answers here:
Why is main called twice?
(3 answers)
Closed 1 year ago.
I encountered this in my school work and it didn't produce what I thought it should:
int main() {
printf("c");
fork();
printf("d");
}
I know there are several things that aren't good about this code (i.e. no parameters in main, no variable for the return value from fork, no return statement at the end, etc.), but this is how it was presented and it's not relevant to my question anyway.
This code produces the output:
cdcd
It was my understanding that when fork is called, both parent and child would resume/begin on the line after the fork call. Based on that, I would have expected the output to be:
cdd
Assuming, of course, that the fork call is successful. Can anyone explain to me why that "c" is printed a second time even though it's on the line before the fork call?
Thanks!
M_MN
You forked your program before flushing stdout (i.e.: data was still in the output buffer). Just call fflush(stdout) to fix it:
❯ cat test.c
#include <stdio.h>
#include <unistd.h>
int main() {
printf("c");
fflush(stdout);
fork();
printf("d");
}
[22:14:01]~/devel
❯ clang test.c -o test
[22:14:07]~/devel
❯ ./test
cdd[22:14:09]~/devel
The reason you're seeing c twice is that the fork() duplicates the unprinted buffered output. You could flush the output stream before the fork():
fflush(stdout);
Or you could set stdout to be unbuffered, but you should do this first, before calling printf() the first time:
setvbuf(stdout, NULL, _IONBF, 0);
Here's what I'm guessing is happening. printf writes to the stream stdout. Since you didn't flush stdout after printing "c" nor did that string end in a new line, the character sat there in a user-space buffer. When you called fork, the child process got a copy of the parent's virtual address space including the buffered text. When both programs exited, their buffers were flushed and so "c" showed up twice.
Try adding fflush(stdout); just prior to the call to fork.

Forking a process with C

I'm following this guide about fork() but something isn't clear for me.
Both processes will start their execution at the next statement following the fork() call. In this case, both processes will start their execution at the assignment statement as shown below:
According to this sentence, this script
printf("before ");
fork();
printf("after ");
should print this: (Because child process will start from printf("after"))
before after after
but it is printing this instead:
before after before after
So did the child process start from the 1st line of the file? Can you tell me what's wrong with my code? Did I misunderstood that sentence?
EDIT
Script compiled and executed on OS X
When you create a new process, it 'inherits' all the variables of the original process - thus all the buffers as well. Since "before" wasn't flushed yet and is still in the buffer, the child process will as well contain this string in the buffer and print it. Therefore you have to call fflush(stdout); before forking the process.
You understood the sentence correctly, but...
When you call fork it takes a snapshot of the process, and creates an exact duplicate. So if there is data buffered in stdout waiting to be written to the console, then that data will be in the child's output buffer after the fork, as well as the parent's buffer.
There are two ways to clear the output buffer before the fork. You can either add a newline \n at the end of the printf
printf( "before\n" );
fork();
printf( "after\n" );
or you can use the fflush function
printf( "before " );
fflush( stdout );
fork();
printf( "after " );
It is because the "before" is buffered and only outputted when that buffer is flushed. This occurs in when the two processes terminates.
If you call fflush on stdout prior to fork, you will see the output you expect. In general, C buffered IO won't play nicely when you do operations at the OS level.
The memory buffer associated with the standard output is cloned on fork including any previously buffered output. That's why you see the "before" twice.

Questions on fork() [duplicate]

This question already has answers here:
printf anomaly after "fork()"
(3 answers)
Closed 8 years ago.
I am trying to understand fork(), and so I put together the following example:
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
void main()
{
if(fork()==0)
{
printf("2");
if(fork()==0)
{
printf("4");
}
else
{
printf("3");
}
}
else
{
printf("1");
}
}
When I was tracing this on paper, I drew the following sketch:
So I believe the output should be 1234. However, when I run this code, the output is 12324. Why is that? Where is my mistake?
Update:
After reading the comments, it was suggested to do any of the following
Add \n to each printf statement
OR: Add fflush(stdout); after each printf statement
OR: Add setbuf(stdout, NULL); <---- this is what I ended-up doing :)
After updating my code, the output was indeed 1234.
printf() output is usually line-buffered. So when you fork(), the new processes gets the copy of the buffer as well. When the process exits, the whole buffer is flushed (or anytime it's flushed in the code or whenever the buffer becomes full). Hence, you see a copy of printf's output one more time.
1) You can flush it after each printf() call by using fflush(stdout);
2) or using \n for example:
printf("2\n");
Another way is to disable the buffering with:
setbuf(stdout, NULL);
Your analysis is almost correct. However, printf does not necessarily write immediately do file descriptor - output is buffered inside the process. The second process does fork after putting 2 in the buffer. Both second and third processes will have it in the buffer and print 2.
If you do printf("2\n") instead new line character will force printf to flush the buffer and you will see only one 2.

C close STDOUT running forever

I am writing some C code that involves the use of pipes. To make a child process use my pipe instead of STDOUT for output, I used the following lines:
close(STDOUT);
dup2(leftup[1], STDOUT);
However, it seems to go into some sort of infinite loop or hang on those lines. When I get rid of close, it hangs on dup2.
Curiously, the same idea works in the immediately preceding line for STDIN:
close(STDIN);
dup2(leftdown[0], STDIN);
What could be causing this behavior?
Edit: Just to be clear...
#define STDIN 0
#define STDOUT 1
Edit 2: Here is a stripped-down example:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#define STDIN 0
#define STDOUT 1
main(){
pid_t child1 = 0;
int leftdown[2];
if (pipe(leftdown) != 0)
printf("ERROR");
int leftup[2];
if (pipe(leftup) != 0)
printf("ERROR");
printf("MADE PIPES");
child1 = fork();
if (child1 == 0){
close(STDOUT);
printf("TEST 1");
dup2(leftup[1], STDOUT);
printf("TEST 2");
exit(0);
}
return(0);
}
The "TEST 1" line is never reached. The only output is "MADE PIPES".
At a minimum, you should ensure that the dup2 function returns the new file descriptor rather than -1.
There's always a possibility that it will give you an error (for example, if the pipe() call failed previously). In addition, be absolutely certain that you're using the right indexes (0 and 1) - I've been bitten by that before and it depends on whether you're in the parent or child process.
Based on your edit, I'm not the least bit surprised that MADE PIPES is the last thing printed.
When you try to print TEST 1, you have already closed the STDOUT descriptor so that will go nowhere.
When you try to print TEST 2, you have duped the STDOUT descriptor so that will go to the parent but your parent doesn't read it.
If you change your forking code to:
child1 = fork();
if (child1 == 0){
int count;
close(STDOUT);
count = printf("TEST 1\n");
dup2(leftup[1], STDOUT);
printf("TEST 2 (%d)\n", count);
exit(0);
} else {
char buff[80];
read (leftup[0], buff, 80);
printf ("%s\n", buff);
sleep (2);
}
you'll see that the TEST 2 (-1) line is output by the parent because it read it via the pipe. The -1 in there is the return code from the printf you attempted in the child after you closed the STDOUT descriptor (but before you duped it), meaning that it failed.
From ISO C11 7.20.6.3 The printf function:
The printf function returns the number of characters transmitted, or a negative value if an output or encoding error occurred.
Multiple thing to mention,
When you use fork, it causes almost a complete copy of parent process. That also includes the buffer that is set up for stdout standard output stream as well. The stdout stream will hold the data till buffer is full or explicitly requested to flush the data from buffer/stream. Now because of this , now you have "MADE PIPES" sitting in buffer. When you close the STDOUT fd and use printf for writing data out to terminal, it does nothing but transfers your "TEST 1" and "TEST 2" into the stdout buffer and doesn't cause any error or crash (due to enough buffer). Thus even after duplicating pipe fd on STDOUT, due to buffered output printf hasn't even touched pipe write end. Most important, please use only one set of APIs i.e. either *NIX or standard C lib functions. Make sure you understand the libraries well, as they often play tricks for some sort of optimization.
Now, another thing to mention, make sure that you close the appropriate ends of pipe in appropriate process. Meaning that if say, pipe-1 is used to communicate from parent to child then make sure that you close the read end in parent and write end in child. Otherwise, your program may hung, due to reference counts associated with file descriptors you may think that closing read end in child means pipe-read end is closed. But as when you don't close the read end in parent, then you have extra reference count for read end of pipe and ultimately the pipe will never close.
There are many other things about your coding style, better you should get hold on it :)
Sooner you learn it better it will save your time. :)
Error checking is absolutely important, use at least assert to ensure that your assumptions are correct.
While using printf statements to log the error or as method of debugging and you are changing terminal FD's (STDOUT / STDIN / STDERR) its better you open a log file with *NIX open and write errors/ log entries to it.
At last, using strace utility will be a great help for you. This utility will allow you to track the system calls executed while executing your code. It is very straight forward and simple. You can even attach this to executing process, provided you have right permissions.

how to check if a stdout is closed

I got some code that prints to stdout, in pseudo code it looks like
int main(){
//allocate data
while(conditional){
char *string = makedata();
fprintf(stdout,"%s",string);
}
//cleanup
}
This works fine, if the conditional is toggled to zero, but if I pipe the output like
./a.out |head -n10 >dumped
Then the code never reaches the cleanup part, I don't understand how to check if the stdout gets closed.
Thanks
Your stdout hasn't been closed, so checking for that will be useless. Your program has received a SIGPIPE and exited. A SIGPIPE is delivered whenever your program writes to a pipe on which there are no readers. In your example, that happens when head exits, closing its stdin.
You should ignore SIGPIPE if you want your program to continue. This code will ignore SIGPIPE:
(void)signal(SIGPIPE, SIG_IGN);
If you don't want to modify your program, you can arrange that something continues to read from the pipe, even after head closes its input. 1
./a.out | ( head -n10 >dumped ; cat > /dev/null )
1: The shell example is valid for bash, maybe not for csh.
It is closed by killing the process with SIGPIPE.
If you want to go on when your output is being ignored, then set up a SIG_IGN handler for SIGPIPE, and handle the error from the fprintf (which will be delayed by buffering, so you cannot assume data that has been written has actually reached the user).
As Rob points out in comments to his answer, you are not worried that stdout has been closed; rather, you are worried that the other end of the pipe is closed. This may be pedantry, but it leads to the solution of your problem. Namely, you do not care if stdout is closed, but only if your printf succeeds. You should check the return value of printf: if it is -1, then the write has failed.
As Simon Richter points out, you will never get the return value of printf if you do not ignore SIGPIPE, because a result of writing to stdout when the other side of the pipe has been closed is that SIG_PIPE will be sent to the process. So you need to do something like:
signal( SIGPIPE, SIG_IGN ); /* Careful: you now must check the return of *all* writes */
if( fprintf( stdout, ... ) == -1 ) {
/* handle the error */
}
I've not tried this, but provided you were using the standard file descriptor for stdout, you may be able to try performing an fstat(1, &stat_structure) and check the return and error codes.

Resources