What is the difference between using _exit() & exit() in a conventional Linux fork-exec?

44,279

Solution 1

You should use _exit (or its synonym _Exit) to abort the child program when the exec fails, because in this situation, the child process may interfere with the parent process' external data (files) by calling its atexit handlers, calling its signal handlers, and/or flushing buffers.

For the same reason, you should also use _exit in any child process that does not do an exec, but those are rare.

In all other cases, just use exit. As you partially noted yourself, every process in Unix/Linux (except one, init) is the child of another process, so using _exit in every child process would mean that exit is useless outside of init.

switch (fork()) {
  case 0:
    // we're the child
    execlp("some", "program", NULL);
    _exit(1);  // <-- HERE
  case -1:
    // error, no fork done ...
  default:
    // we're the parent ...
}

Solution 2

exit() flushes io buffers and does some other things like run functions registered by atexit(). exit() invokes _end( )

_exit() just ends the process without doing that. You call _exit() from the parent process when creating a daemon for example.

Ever notice that main() is a function? Ever wonder what called it in the first place? When a c program runs the shell you are running in provides the executable path to 'exec' system call and the control is passed to kernel which in turn calls the startup function of every executable _start(), calls your main(), when main() returns it then calls _end() Some implementations of C use slightly different names for _end() & _start() ...

exit() and _exit() invoke _end()

Normally - for every main() there should be one & only one exit() call. (or return at the end of main() )

Solution 3

exit() is on the top of _exit(), using conventional C library.

There are the differences:

  1. _exit() won't flushes the stdio buffer while exit() flushes the stdio buffer prior to exit.

  2. _exit() can not perform clean-up process while exit() can be registered with some function ( i.e on_exit or at_exit) to perform some clean-up process if anything is required before existing the program.

exit(status) simply passes the exit status to _exit(status). It is recommended that whenever to perform fork(), one of them between child and parent, one use _exit() and another use exit().

Share:
44,279
ned1986zha
Author by

ned1986zha

Updated on July 08, 2022

Comments

  • ned1986zha
    ned1986zha almost 2 years

    I've been trying to figure out how the fork-exec mechanism is used inside Linux. Everything was going on according to the plan until some web pages started to confuse me.

    It is said that a child process should strictly use _exit() instead of a simple exit() or a normal return from main().

    As I know, Linux shell fork-execs every one of the external commands; assuming what I said above is true, the conclusion is that none of these external commands nor any other execution happening inside the Linux shell can do normal return!

    Wikipedia & some other webpages claim we've got to use _exit() just to prevent a child process causing deletion of parent's temporary files while a probable double flushing of stdio buffers may happen. though I understand the former, I have no clues how a double flushing of buffers could be harmful to a Linux system.

    I've spent my whole day on this... Thanks for any clarification.

  • ned1986zha
    ned1986zha about 13 years
    TONS of Thanks. I feel much better now; though still twice flushing of a buffer being problematic, has no meaning for me.
  • caf
    caf about 13 years
    @ned1986zha: At the time of the fork(), there may be data in the stdio buffers. If both the parent and the child flush those buffers, that data will appear twice in the output. The problem doesn't exist if exec() succeeds, because in that case the newly exec'd process starts with fresh stdio buffers.
  • ned1986zha
    ned1986zha about 13 years
    @caf: OK, now it makes sense to me. here, conclusion is that buffering is an internal C language mechanism rather than being some OS provided facility. so far, I mistakingly believed that a single open file may have a shared buffer for all the processes! Thank you so much;
  • caf
    caf about 13 years
    @ned1986zha: Yes - the stdio buffer is a C library mechanism. There may be OS-provided buffering as well - but that doesn't matter, because the fork() won't duplicate that.
  • Bin
    Bin almost 8 years
    @caf: why no duplicated output when use _exit ? Although _exit won't flush I/O buffer, but when the child process terminated, kernel should flush any data in the buffer and close the standard I/O streams for the terminated process...
  • caf
    caf almost 8 years
    @Bin: As the comments above say, _exit() ensures that the C-library-level (userspace stdio) buffers aren't flushed - because those buffers get duplicated by fork() (since they're userspace, the kernel isn't aware of them). Any kernel-level buffers would still get flushed, but that's OK, because kernel-level buffers aren't duplicated by fork().
  • Bin
    Bin almost 8 years
    @caf: Got your point, I just wrote a small code snippet to demonstrate what you said: printf("abc") without new line character within (to make sure the characters will be buffered), then following _exit(0), result showed that nothing get flushed out.
  • Bin
    Bin almost 8 years
    @caf: There's still one small detail not so clear to me, when we call _exit() to terminate the process, let's say there're some buffer exist in stdout, it will not be flushed by _exit()... after process terminated, will stdout be closed ? if yes, won't the i/o stream close operation flush the buffer ? ( as far as I know, fclose() will do flushing ), thanks!
  • caf
    caf almost 8 years
    @Bin: When the process is terminated by the kernel, the file descriptors get closed but the stdio buffers aren't flushed - because the kernel doesn't know anything about those buffers, they're entirely a creation of the userspace C library.
  • yanpas
    yanpas over 7 years
    IMPORTANT: you MUST NOT use exit in signal handler, you can use abort, _exit or _Exit. stackoverflow.com/questions/8833394/…
  • 12431234123412341234123
    12431234123412341234123 over 6 years
    fork() without exec*() are very useful for multithreading, if you want avoid race conditions, at least there are not rare in me programs.
  • Soner from The Ottoman Empire
    Soner from The Ottoman Empire about 5 years
    Not good, being a kinda stealer is not good. Respect for someone else's effort -> #3 answer is there -> unix.com/programming/116721-difference-between-exit-_exit.ht‌​ml