Syscall implementation of exit()
I wrote a simple C program which just calls the exit() function, however strace says that the binary is actually calling exit_group, is exit() a exit_group() wrapper? Are these two functions equivalent? If so why would the compiler choose exit_group() over exit()?
The Linux and glibc man pages document all of this (See especially the "C library/kernel differences" in the NOTES section).
-
_exit(2)
: In glibc 2.3 and later, this wrapper function actually uses the LinuxSYS_exit_group
system call to exit all threads. Before glibc2.3, it was a wrapper forSYS_exit
to exit just the current thread. -
exit_group(2)
: glibc wrapper forSYS_exit_group
, which exits all threads. -
exit(3)
: The ISO C89 function which flushes buffers and then exits the whole process. (It always usesexit_group()
because there's no benefit to checking if the process was single-threaded and deciding to useSYS_exit
vs.SYS_exit_group
). As @Matteo points out, recent ISO C / POSIX standards are thread-aware and one or both probably require this behaviour.But apparently
exit(3)
itself is not thread-safe (in the C library cleanup parts), so I guess don't call it from multiple threads at once. -
syscall
/int 0x80
withSYS_exit
: terminates just the current thread, leaving others running. AFAIK, modern glibc has no thin wrapper function for this Linux system call, but I thinkpthread_exit()
uses it if this isn't the last thread. (Otherwise exit(3) -> exit_group(2).)
Only exit()
, not _exit()
or exit_group()
, flushes stdout
, leading to "printf
doesn't print anything" problems in newbie asm programs if writing to a pipe (which makes stdout
full-buffered instead of line-buffered), or if you forgot the \n
in the format string. For example, How come _exit(0) (exiting by syscall) prevents me from receiving any stdout content?. If you use any buffered I/O functions, or at_exit
, or anything like that, it's usually a good idea to call the libc exit(3)
function instead of the system call directly. But of course you can call fflush
before SYS_exit_group
.
(Also related: On x64 Linux, what is the difference between syscall, int 0x80 and ret to exit a program? - ret
from main is equivalent to calling exit(3)
)
It's not of course the compiler that chose anything, it's libc. When you include headers and write read(fd, buf, 123)
or exit(1)
, the C compiler just sees an ordinary function call.
Some C libraries (e.g. musl, but not glibc) may use inline asm to inline a syscall
instruction into your binary, but still the headers are part of the C library, not the compiler.