我编写了一个简单的 C 程序,只调用exit()函数,但strace表示二进制文件实际上是调用exit_group,exit()是exit_group()包装器?这两个功能是否相同?如果是这样,为什么编译器会选择exit_group()而不是exit()?
答案 0 :(得分:9)
Linux和glibc手册页记录了所有这些(特别参见" C库/内核差异"在NOTES部分中)。
_exit(2)
:在glibc 2.3及更高版本中,此包装函数实际调用Linux sys_exit_group
系统调用以退出所有线程。在glibc2.3之前,它是sys_exit
的一个包装器,只退出当前线程。sys_exit
:终止当前线程,让其他线程继续运行。 AFAIK,现代glibc没有这个Linux系统调用的包装函数,因为它通常没用。exit_group(2)
:sys_exit_group
的glibc包装器,它退出所有线程。 exit(3)
:ISO C89函数,它刷新缓冲区然后退出整个过程。 (它总是使用exit_group()
,因为检查流程是否是单线程并决定使用sys_exit
与sys_exit_group
)没有任何好处。 As @Matteo points out,最近的ISO C或POSIX标准是线程感知的,其中一个或两个可能都需要这种行为。
但显然exit(3)
本身不是线程安全的(在C库清理部分中),所以我想不要一次从多个线程调用它。
只有exit()
,而非_exit()
或exit_group()
才会刷新stdout
,导致" printf
无法打印任何内容&#34 ;新手asm程序中的问题,如果写入管道(使stdout
全缓冲而不是行缓冲),或者您忘记了格式字符串中的\n
。例如,How come _exit(0) (exiting by syscall) prevents me from receiving any stdout content?。如果您使用任何缓冲的I / O函数,或at_exit
或类似的东西,通常最好直接调用libc exit(3)
函数而不是系统调用。但是,您当然可以在fflush
之前致电sys_exit_group
。
它当然不是选择任何东西的编译器,它是libc。当您包含标头并写入read(fd, buf, 123)
或exit(1)
时,C编译器只会看到普通的函数调用。
某些C库(例如musl,但不是glibc)可以使用内联asm将syscall
指令内联到二进制文件中,但仍然是头库是C库的一部分,而不是编译器。