Search code examples
clinuxforkstdio

Standard streams and vfork


I am playing a bit with fork/vfork functions, and there is something that is puzzling to me. In Stevens book it is written that:

Note in Figure 8.3 that we call _exit instead of exit.

As we described in Section 7.3, _exit does not perform any flushing of standard I/O buffers. If we call exit instead, the results are indeterminate. Depending on the implementation of the standard I/O library, we might see no difference in the output, or we might find that the output from the parent's printf has disappeared. If the child calls exit, the implementation flushes the standard I/O streams. If this is the only action taken by the library, then we will see no difference with the output generated if the child called _exit. If the implementation also closes the standard I/O streams, however, the memory representing the FILE object for the standard output will be cleared out. Because the child is borrowing the parent's address space, when the parent resumes and calls printf, no output will appear and printf will return -1. Note that the parent's STDOUT_FILENO is still valid, as the child gets a copy of the parent's file descriptor array (refer back to Figure 8.2). Most modern implementations of exit will not bother to close the streams. Because the process is about to exit, the kernel will close all the file descriptors open in the process. Closing them in the library simply adds overhead without any benefit.

so I tried to test if I can get printf error, in my manual of vfork there is:

All open stdio(3) streams are flushed and closed. Files created by tmpfile(3) are removed.

but when I compile and execute this program:

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

  int main()
{
  int s;
  pid_t ret;
  if (vfork() == 0)
  {
      //abort();
      exit(6);
  }
  else
  {
      ret=wait(&s);
      printf("termination status to %d\n",s);
      if (WIFEXITED(s))
          printf("normalnie, status to %d\n",WEXITSTATUS(s));
  }
  return 0;
}

everything is working fine, I don't get any printf errors. Why is that?


Solution

  • The end of the paragraph you quoted says:

    Most modern implementations of exit will not bother to close the streams. Because the process is about to exit, the kernel will close all the file descriptors open in the process. Closing them in the library simply adds overhead without any benefit.

    This is most likely what's happening. Your OS doesn't actually close the stream (but it does probably flush it).

    The important thing isn't what exit does here, its the underlying concept. The child is sharing the parent's memory and stack frame. That means that the child can very easily change something that the parent did not expect, which could easily cause the parent to crash or misbehave when it starts running again. The man page for vfork says the only thing a process can do is call exit() or an exec. In fact, the child should not even allocate memory or modify any variables.

    To see the impact of this, try putting the vfork call inside of a function and let the child return or modify some variables there and see what happens.