【问题标题】:Syscall implementation of exit()exit() 的系统调用实现
【发布时间】:2018-04-04 19:37:06
【问题描述】:

我写了一个简单的C 程序,它只调用exit() 函数,但是strace 说二进制实际上是在调用exit_group,exit() 是exit_group() 包装器吗?这两个功能是等价的吗?如果是这样,为什么编译器会选择 exit_group() 而不是 exit()?

【问题讨论】:

  • 所以你在eax中加载1并调用内核中断,strace显示exit_group调用(2520xfc)?哪个编译器?你的编译和链接字符串是什么? (也可能是strace 问题,尽管您会认为他们有正确的查找表)
  • @DavidC.Rankin 据我正确理解这个问题,用户正在调用libc 库中的exit() 函数。
  • IIRC,来自标准 C(和/或 POSIX?)成为线程感知,并要求 exit() 应该终止所有正在运行的线程;在 Linux 上,普通的 exitsyscall 只会终止当前线程,因此在某些时候,exit() libc 函数被重新映射到 exit_group syscall,这符合标准的要求。
  • @MartinRosenau,你可能是对的,assembly 标签让我失望了。 (因为程序集确实有 exitexit_group 系统调用。
  • 我只是做了一个简短的C文件并使用gcc转储到汇编程序(例如gcc -S -masm=intel -o exitcall.asm exitcall.c和汇编程序指令是call exit@PLT,运行trace通过,你是对的,我发现exit_group(0)。然后我在程序集中编写文件nasm 并在可执行文件上运行strace,它调用exit(0) = ?; +++ exited with 0 +++ 奇怪,我没有答案,但我现在完全理解了这个问题。你在使用@ 987654347@作为编译器?

标签: c gcc assembly x86 system-calls


【解决方案1】:

Linux 和 glibc 手册页记录了所有这些内容(尤其请参阅注释部分中的“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_exitSYS_exit_group 没有任何好处)。 As @Matteo points out,最近的 ISO C / POSIX 标准是线程感知的,一个或两个可能需要这种行为。

    但显然exit(3) 本身不是线程安全的(在 C 库清理部分中),所以我想不要一次从多个线程调用它。

  • syscall / int 0x80SYS_exit:只终止当前线程,让其他线程继续运行。 AFAIK,现代 glibc 没有针对这个 Linux 系统调用的瘦包装函数,但我认为 pthread_exit() 如果这不是最后一个线程,就会使用它。 (否则退出(3) -> exit_group(2)。)

只有exit(),而不是_exit()exit_group(),刷新stdout,如果写入管道(这使得stdout全缓冲而不是行缓冲),或者如果您忘记了格式字符串中的\n。例如,How come _exit(0) (exiting by syscall) prevents me from receiving any stdout content?。如果您使用任何缓冲 I/O 函数,或 at_exit 或类似的东西,通常最好调用 libc exit(3) 函数而不是直接调用系统调用。当然你也可以在SYS_exit_group之前调用fflush

(也相关:On x64 Linux, what is the difference between syscall, int 0x80 and ret to exit a program? - ret from main 相当于调用exit(3)


选择任何东西的当然不是编译器,而是libc。当您包含头文件并编写read(fd, buf, 123)exit(1) 时,C 编译器只会看到一个普通的函数调用。

一些 C 库(例如 musl,但不是 glibc)可能使用内联 asm 将 syscall 指令内联到您的二进制文件中,但头文件仍然是 C 库的一部分,而不是编译器。

【讨论】:

  • 老实说,你在手册页上打败了我……我再次了解到我还有多少东西要学。谢谢彼得。
  • @DavidC.Rankin:我刚才不需要查这个;几年前我很好奇。 :P 我仅仅通过阅读 Linux 手册页并了解使用 strace 的各种程序做了什么,就学到了 很多 系统编程知识。
  • @PeterCordes:就是这样。 Linux 系统上的线程随着时间的推移而演变,以及系统调用机制即使在找到更好的机制后仍保留在 API 中这一事实反映了这种复杂性。我说 system 是为了强调内核、libc 和编译器之间的相互作用。
猜你喜欢
  • 2012-09-28
  • 1970-01-01
  • 2016-05-25
  • 2016-01-18
  • 2012-08-22
  • 1970-01-01
  • 2013-09-30
  • 2017-07-27
  • 1970-01-01
相关资源
最近更新 更多