glibc时间函数实现
glibc time function implementation
我想了解 time() 在 glibc 中的实现:https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/x86/time.c.html#time
如果展开宏(将鼠标悬停在它们上面),您会得到:
time_t time (time_t *t)
{
unsigned long int resultvar;
long int __arg1 = (long int) (t);
register long int _a1 asm (""rdi"") = __arg1;
asm volatile ( ""syscall\n\t"" : ""=a"" (resultvar) : ""0"" (201) , ""r"" (_a1) : ""memory"", ""cc"", ""r11"", ""cx"");
(long int) resultvar;
}
汇编命令显然必须return当前时间,但我不明白如何。检查英特尔手动系统调用我看到它破坏了 r11 和 cx,其他破坏者是 gcc 的东西,到那里,我很好。
根据我的阅读 (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) “0” 表示某物同时用作输入和输出,尽管我不明白“(201)” 的意思(我期待的是一个变量名,而不是一个号码)。有什么想法吗?
我不确定 _a1 是否需要有信息,我假设它可以为 NULL,所以汇编命令的唯一实际输入是“(201)”,但我不知道系统调用是如何进行的连同 201 让我可以访问内部时钟。
谢谢。
P.S。
奖励问题:根据我在 Intel 手册上读到的内容,我的印象是唯一可用的时钟应该在 MoBo 上,而不是 CPU 硬件的一部分。我是不是误会了?
这个看起来很复杂的内联汇编只会导致编译器发出以下汇编指令:
mov eax, 201
syscall
所以,整个 time
函数就是:
time:
mov eax, 201
syscall
ret
将立即数201(十六进制表示为0xC9)移入EAX
寄存器,然后执行syscall
指令。这条指令的作用正如其名称所暗示的那样:它进行系统调用。这基本上就是您在 Linux 上调用平台 API 函数的方式。另请参阅 System V AMD64 ABI.
的 A.2 节(“AMD64 Linux 内核约定”)
简述:
系统调用ID号放入rax
.
(在这种情况下,数字只有 32 位,因此汇编代码将其放入 eax
。高 32 位为 implicitly zeroed,在 mov
指令。)
系统调用的参数(如果有的话)放在寄存器中:rdi
、rsi
、rdx
、r10
、r8
和 r9
.
(在这种情况下,对于系统调用 #201,没有需要指定的参数,所以这些寄存器中的 none 由 time
函数初始化。)
调用syscall
后,其结果包含在rax
中。传统上,负值(-4095 到-1)表示错误,对应于−errno
.
对于系统调用,rcx
和r11
寄存器被视为volatile,这意味着它们的内容受制于被破坏。如果调用者关心这些值,则需要保留它们。所有其他寄存器的值都在系统调用中保存。
(这就是为什么在扩展的内联 asm 语法中存在 clobbers 的原因。)
有 64 位 Linux 可用系统调用的参考 here (32-bit Linux system calls are here)。可以看到201(0xC9)对应的是sys_time
.
sys_time
将 RDI
寄存器解释为 time_t*
值。此代码:
long int __arg1 = (long int) (t);
register long int _a1 asm ("rdi") = __arg1;
使函数的参数 t
存储在 RDI
寄存器中。但是,这实际上不会导致生成任何机器指令,因为 System V AMD64 调用约定已经在 RDI
中传递了函数的第一个参数,因此 t
已经是 在 RDI
.
sys_time
系统调用只是填充它在 RDI
中找到的指针,这与 time
函数的 t
参数相同。它还 return 其结果(错误代码)在 RAX
中,它始终用于 System V AMD64 调用约定下函数的 return 值,因此没有机器指令那里也需要。
也许更清楚:
# inputs: RDI is a pointer to time_t that will be filled in
# returns: result is left in RAX
time:
mov eax, 201
syscall
ret
关于您的 PS:所有当前的英特尔 CPU 都有自己的时钟,可以使用 rdtsc
等指令读取。然而,它们是否可以用作实时时钟取决于执行环境的配置(例如,需要kernel/hypervisor支持,可能需要禁用某些省电状态等)。
我想了解 time() 在 glibc 中的实现:https://code.woboq.org/userspace/glibc/sysdeps/unix/sysv/linux/x86/time.c.html#time
如果展开宏(将鼠标悬停在它们上面),您会得到:
time_t time (time_t *t)
{
unsigned long int resultvar;
long int __arg1 = (long int) (t);
register long int _a1 asm (""rdi"") = __arg1;
asm volatile ( ""syscall\n\t"" : ""=a"" (resultvar) : ""0"" (201) , ""r"" (_a1) : ""memory"", ""cc"", ""r11"", ""cx"");
(long int) resultvar;
}
汇编命令显然必须return当前时间,但我不明白如何。检查英特尔手动系统调用我看到它破坏了 r11 和 cx,其他破坏者是 gcc 的东西,到那里,我很好。
根据我的阅读 (https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html) “0” 表示某物同时用作输入和输出,尽管我不明白“(201)” 的意思(我期待的是一个变量名,而不是一个号码)。有什么想法吗?
我不确定 _a1 是否需要有信息,我假设它可以为 NULL,所以汇编命令的唯一实际输入是“(201)”,但我不知道系统调用是如何进行的连同 201 让我可以访问内部时钟。
谢谢。
P.S。 奖励问题:根据我在 Intel 手册上读到的内容,我的印象是唯一可用的时钟应该在 MoBo 上,而不是 CPU 硬件的一部分。我是不是误会了?
这个看起来很复杂的内联汇编只会导致编译器发出以下汇编指令:
mov eax, 201
syscall
所以,整个 time
函数就是:
time:
mov eax, 201
syscall
ret
将立即数201(十六进制表示为0xC9)移入EAX
寄存器,然后执行syscall
指令。这条指令的作用正如其名称所暗示的那样:它进行系统调用。这基本上就是您在 Linux 上调用平台 API 函数的方式。另请参阅 System V AMD64 ABI.
简述:
系统调用ID号放入
rax
.(在这种情况下,数字只有 32 位,因此汇编代码将其放入
eax
。高 32 位为 implicitly zeroed,在mov
指令。)系统调用的参数(如果有的话)放在寄存器中:
rdi
、rsi
、rdx
、r10
、r8
和r9
.(在这种情况下,对于系统调用 #201,没有需要指定的参数,所以这些寄存器中的 none 由
time
函数初始化。)调用
syscall
后,其结果包含在rax
中。传统上,负值(-4095 到-1)表示错误,对应于−errno
.对于系统调用,
rcx
和r11
寄存器被视为volatile,这意味着它们的内容受制于被破坏。如果调用者关心这些值,则需要保留它们。所有其他寄存器的值都在系统调用中保存。(这就是为什么在扩展的内联 asm 语法中存在 clobbers 的原因。)
有 64 位 Linux 可用系统调用的参考 here (32-bit Linux system calls are here)。可以看到201(0xC9)对应的是sys_time
.
sys_time
将 RDI
寄存器解释为 time_t*
值。此代码:
long int __arg1 = (long int) (t);
register long int _a1 asm ("rdi") = __arg1;
使函数的参数 t
存储在 RDI
寄存器中。但是,这实际上不会导致生成任何机器指令,因为 System V AMD64 调用约定已经在 RDI
中传递了函数的第一个参数,因此 t
已经是 在 RDI
.
sys_time
系统调用只是填充它在 RDI
中找到的指针,这与 time
函数的 t
参数相同。它还 return 其结果(错误代码)在 RAX
中,它始终用于 System V AMD64 调用约定下函数的 return 值,因此没有机器指令那里也需要。
也许更清楚:
# inputs: RDI is a pointer to time_t that will be filled in
# returns: result is left in RAX
time:
mov eax, 201
syscall
ret
关于您的 PS:所有当前的英特尔 CPU 都有自己的时钟,可以使用 rdtsc
等指令读取。然而,它们是否可以用作实时时钟取决于执行环境的配置(例如,需要kernel/hypervisor支持,可能需要禁用某些省电状态等)。