【问题标题】:Why doesn't time() from time.h have a syscall to sys_time?为什么 time.h 中的 time() 没有对 sys_time 的系统调用?
【发布时间】:2018-06-23 11:08:47
【问题描述】:

我编写了一个非常简单的程序,调用time() 来说明strace 的用法,但是我遇到了问题; time() 调用似乎并没有真正产生系统调用!

我最终踏入了 GDB 中的time() 函数,现在我比以往任何时候都更加困惑。来自time()函数的反汇编:

0x7ffff7ffad90 <time>:  push   rbp
0x7ffff7ffad91 <time+1>:    test   rdi,rdi
0x7ffff7ffad94 <time+4>:    mov    rax,QWORD PTR [rip+0xffffffffffffd30d]        # 0x7ffff7ff80a8
0x7ffff7ffad9b <time+11>:   mov    rbp,rsp
0x7ffff7ffad9e <time+14>:   je     0x7ffff7ffada3 <time+19>
0x7ffff7ffada0 <time+16>:   mov    QWORD PTR [rdi],rax
0x7ffff7ffada3 <time+19>:   pop    rbp
0x7ffff7ffada4 <time+20>:   ret 

如果这个函数不调用内核,它实际上是如何获取当前时间的?它的流程是:

  • 序幕
  • (0x7ffff7ffad94 + 0xffffffffffffd30d) (0x7ffff7ff80a8) 中获取一些值并将其放入 rax(待返回)
  • 检查 rdi(第一个参数)是否为空
  • 如果不将值放入 rax(返回值)中也有
  • 结语

这对于time() 的功能是有意义的;如果参数为空,它只返回值,但如果不是,它也将它放在参数中。我的问题是,它从哪里获得时间价值?地址0x7ffff7ff80a8 有什么神奇之处,它是如何在没有系统调用的情况下做到这一点的?

我正在使用 GCC 6.3.0 和 Ubuntu GLIBC 2.24-9ubuntu2.2。

【问题讨论】:

    标签: linux gcc assembly x86-64 glibc


    【解决方案1】:

    阅读time(7)。您对time(2) 的调用可能使用vdso(7)(可能通过clock_gettime(2)__vdso_time)。如果使用vdso(7)

    当使用strace(1) 跟踪系统调用时,符号(系统调用) 由 vDSO 导出的数据将不会出现在跟踪输出中。

    细节可能是内核和 libc 特定的(当然还有架构特定的)。

    出于类似的vDSO 原因,strace date 不显示任何与时间相关的系统调用。

    vDSO 是一个非常方便的功能(受ASLR 限制)。多亏了它,定时调用(例如clock_gettime(2)...)变得非常快(在我的 i5-4690S 上大约需要 40 纳秒)。 AFAIU,没有context switch(或用户到内核mode 的转换)正在发生。

    所以您的0x7ffff7ff80a8 可能位于 vDSO 中(内核确保它包含当前时间)。您可以使用proc(5)(例如reading 并在您的程序中显示/proc/self/maps)进行检查,或者使用ldd(1)pmap(1)

    【讨论】:

    • 没错,没有内核/用户转换。 VDSO 页面是内核映射到每个进程的地址空间中的固定地址的代码 + 数据。用户空间(特别是 glibc 的一些系统调用的包装器,如 timegetpid 等)只需运行常规的 near-call 指令来调用 VDSO 页面中的函数。内核为它们提供用户空间代码的原因是它们通常根本不需要进入内核,只需从内核导出 RDTSC 比例因子和进程 PID 的只读页面读取。
    • 在 32 位模式下的 x86 上,进行快速系统调用的推荐方法是调用 VDSO:如果sysenter 可用,则它具有用户空间端的代码sysenter 为内核提供 EIP 以返回的舞蹈,或者它使用 AMD 32 位 syscall,或者它回退到 int 0x80。 (内核根据引导时 CPU 检测来选择它导出的 VDSO 页面中的内容。)但是在 64 位 x86 用户空间中,glibc 直接使用syscall,因为syscall 指令是 x86 的基线- 64.
    • 糟糕,实际上VDSO页面可以被ASLRed;这些功能不在固定地址。导出的页面具有 ELF 标头,因此运行时动态链接器会处理这些事情,就像 VDSO 是从磁盘文件映射的共享库一样。 (内核将 VDSO 基地址写入动态链接器可以找到的位置。)参见blog.packagecloud.io/eng/2016/04/05/…
    猜你喜欢
    • 2014-09-28
    • 1970-01-01
    • 1970-01-01
    • 2019-02-22
    • 2020-04-22
    • 2021-02-06
    • 1970-01-01
    • 1970-01-01
    • 2018-11-10
    相关资源
    最近更新 更多