Linux和glibc的手册记录了所有这些内容(特别是NOTES部分中的“C库/内核差异”)。
_exit(2)
: 在glibc 2.3及更高版本中,此包装函数实际上使用Linux的SYS_exit_group
系统调用退出所有线程。在glibc2.3之前,它是SYS_exit
的包装器,仅退出当前线程。
exit_group(2)
: SYS_exit_group
的glibc包装器,用于退出所有线程。
exit(3)
: ISO C89函数,刷新缓冲区然后退出整个进程。(它总是使用exit_group()
,因为检查进程是否单线程并决定使用SYS_exit
vs. SYS_exit_group
没有任何好处)。正如@Matteo指出的那样, 最近的ISO C / POSIX标准都支持线程,并且其中一个或两个可能需要此行为。
但显然,exit(3)
本身不是线程安全的(在C库清理部分),因此我猜不要同时从多个线程调用它。
syscall
/ int 0x80
与SYS_exit
:仅终止当前线程,使其他线程保持运行状态。据我所知,现代glibc没有此Linux系统调用的薄包装函数,但我认为如果这不是最后一个线程,则pthread_exit()
会使用它。(否则exit(3) -> exit_group(2)。)
exit()
,而不是_exit()
或exit_group()
,会刷新stdout
,导致新手汇编程序在写入管道(使stdout
变为全缓冲而不是行缓冲),或者忘记格式字符串中的\n
时出现“printf
不打印任何内容”的问题。例如,为什么使用_exit(0)(通过系统调用退出)会阻止我接收任何stdout内容?如果您使用任何缓冲I/O函数、at_exit
或类似的东西,通常最好直接调用libc的exit(3)
函数,而不是直接调用系统调用。但当然,在调用SYS_exit_group
之前可以调用fflush
。
(另外相关的:在x64 Linux上,syscall、int 0x80和ret退出程序有什么区别?-从main返回ret
等效于调用exit(3)
)
当你包含头文件并写入read(fd, buf, 123)
或exit(1)
时,编译器只看到一个普通的函数调用,而选择的不是编译器本身,而是libc。
一些C库(例如musl,但不包括glibc)可能使用内联汇编将syscall
指令内联到您的二进制文件中,但头文件仍然是C库的一部分,而不是编译器。
assembly
标签让我有些困惑。(因为汇编语言确实有exit
和exit_group
系统调用。) - David C. Rankin