macOS 上的 gettimeofday() 是否使用系统调用?
Does gettimeofday() on macOS use a system call?
我预计 gettimeofday()
将调用系统调用来完成实际获取时间的工作。但是,运行下面的程序
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
int main(int argc, char const *argv[])
{
struct timeval tv;
printf("Before gettimeofday() %ld!\n", tv.tv_sec);
int rc = gettimeofday(&tv, NULL);
printf("After gettimeofday() %ld\n", tv.tv_sec);
if (rc == -1) {
printf("Error: gettimeofday() failed\n");
exit(1);
}
printf("Exiting ! %ld\n", tv.tv_sec);
return 0;
}
dtruss -d
returns 一长串系统调用,最后是:
RELATIVE SYSCALL(args) = return
... lots of syscalls with earlier timestamps ...
3866 fstat64(0x1, 0x7FFF56ABC8D8, 0x11) = 0 0
3868 ioctl(0x1, 0x4004667A, 0x7FFF56ABC91C) = 0 0
3882 write_nocancel(0x1, "Before gettimeofday() 0!\n[=11=]", 0x19) = 25 0
3886 write_nocancel(0x1, "After gettimeofday() 1480913810\n[=11=]", 0x20) = 32 0
3887 write_nocancel(0x1, "Exiting ! 1480913810\n[=11=]", 0x15) = 21 0
看起来 gettimeofday()
没有使用系统调用,但这似乎是错误的——内核肯定负责系统时钟吗? dtruss
是不是漏了什么?我是否错误地读取了输出?
使用 dtrace 代替 dtruss 将消除您的疑虑。
gettimeofday() 本身就是一个系统调用。如果你 运行 dtrace 脚本,你可以看到这个系统调用被调用。
您可以使用以下 dtrace 脚本
"dtrace1.d"
syscall:::entry
/ execname == "foo" /
{
}
(foo 是您的可执行文件的名称)
到运行以上dtrace使用:dtrace -s dtrace1.d
然后执行您的程序以查看您的程序使用的所有系统调用
作为 TheDarkKnight ,有一个 gettimeofday
系统调用。但是,userspace gettimeofday
函数通常 而不是 调用相应的系统调用,而是 __commpage_gettimeofday
,它试图从一个进程地址 space 的特殊部分称为 commpage。只有当此调用失败时,gettimeofday
系统调用才会用作回退。这将大多数调用 gettimeofday
的成本从普通系统调用降低到仅内存读取。
这本书 Mac OSX Internals: A Systems Approach 介绍了 commpage。简而言之,它是内核内存的一个特殊区域,映射到每个进程地址 space 的最后八页。除其他外,它包含 "updated asynchronously from the kernel and read atomically from user space, leading to occasional failures in reading".
的时间值
为了查看 gettimeofday()
系统调用被用户 space 函数调用的频率,我编写了一个测试程序,在一个紧密循环中调用了 gettimeofday()
1 亿次:
#include <sys/time.h>
int main(int argc, char const *argv[])
{
const int NUM_TRIALS = 100000000;
struct timeval tv;
for (int i = 0; i < NUM_TRIALS; i++) {
gettimeofday(&tv, NULL);
}
return 0;
}
运行 我机器上 dtruss -d
下的这个显示这触发了对 gettimeofday()
系统调用的 10-20 次调用(所有用户的 0.00001%-0.00002%space 调用)。
对于那些感兴趣的人,用户 space gettimeofday()
函数(对于 macOS 10.11 - El Capitan)的 source code 中的相关行是
if (__commpage_gettimeofday(tp)) { /* first try commpage */
if (__gettimeofday(tp, NULL) < 0) { /* if it fails, use syscall */
return (-1);
}
}
函数__commpage_gettimeofday
combines the timestamp read from the commpage and a reading of the time stamp counter register to calculate the time since epoch in seconds and microseconds. (The rdstc
instruction is inside _mach_absolute_time
.)
我预计 gettimeofday()
将调用系统调用来完成实际获取时间的工作。但是,运行下面的程序
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
int main(int argc, char const *argv[])
{
struct timeval tv;
printf("Before gettimeofday() %ld!\n", tv.tv_sec);
int rc = gettimeofday(&tv, NULL);
printf("After gettimeofday() %ld\n", tv.tv_sec);
if (rc == -1) {
printf("Error: gettimeofday() failed\n");
exit(1);
}
printf("Exiting ! %ld\n", tv.tv_sec);
return 0;
}
dtruss -d
returns 一长串系统调用,最后是:
RELATIVE SYSCALL(args) = return
... lots of syscalls with earlier timestamps ...
3866 fstat64(0x1, 0x7FFF56ABC8D8, 0x11) = 0 0
3868 ioctl(0x1, 0x4004667A, 0x7FFF56ABC91C) = 0 0
3882 write_nocancel(0x1, "Before gettimeofday() 0!\n[=11=]", 0x19) = 25 0
3886 write_nocancel(0x1, "After gettimeofday() 1480913810\n[=11=]", 0x20) = 32 0
3887 write_nocancel(0x1, "Exiting ! 1480913810\n[=11=]", 0x15) = 21 0
看起来 gettimeofday()
没有使用系统调用,但这似乎是错误的——内核肯定负责系统时钟吗? dtruss
是不是漏了什么?我是否错误地读取了输出?
使用 dtrace 代替 dtruss 将消除您的疑虑。 gettimeofday() 本身就是一个系统调用。如果你 运行 dtrace 脚本,你可以看到这个系统调用被调用。
您可以使用以下 dtrace 脚本 "dtrace1.d"
syscall:::entry
/ execname == "foo" /
{
}
(foo 是您的可执行文件的名称)
到运行以上dtrace使用:dtrace -s dtrace1.d
然后执行您的程序以查看您的程序使用的所有系统调用
作为 TheDarkKnight gettimeofday
系统调用。但是,userspace gettimeofday
函数通常 而不是 调用相应的系统调用,而是 __commpage_gettimeofday
,它试图从一个进程地址 space 的特殊部分称为 commpage。只有当此调用失败时,gettimeofday
系统调用才会用作回退。这将大多数调用 gettimeofday
的成本从普通系统调用降低到仅内存读取。
这本书 Mac OSX Internals: A Systems Approach 介绍了 commpage。简而言之,它是内核内存的一个特殊区域,映射到每个进程地址 space 的最后八页。除其他外,它包含 "updated asynchronously from the kernel and read atomically from user space, leading to occasional failures in reading".
的时间值为了查看 gettimeofday()
系统调用被用户 space 函数调用的频率,我编写了一个测试程序,在一个紧密循环中调用了 gettimeofday()
1 亿次:
#include <sys/time.h>
int main(int argc, char const *argv[])
{
const int NUM_TRIALS = 100000000;
struct timeval tv;
for (int i = 0; i < NUM_TRIALS; i++) {
gettimeofday(&tv, NULL);
}
return 0;
}
运行 我机器上 dtruss -d
下的这个显示这触发了对 gettimeofday()
系统调用的 10-20 次调用(所有用户的 0.00001%-0.00002%space 调用)。
对于那些感兴趣的人,用户 space gettimeofday()
函数(对于 macOS 10.11 - El Capitan)的 source code 中的相关行是
if (__commpage_gettimeofday(tp)) { /* first try commpage */
if (__gettimeofday(tp, NULL) < 0) { /* if it fails, use syscall */
return (-1);
}
}
函数__commpage_gettimeofday
combines the timestamp read from the commpage and a reading of the time stamp counter register to calculate the time since epoch in seconds and microseconds. (The rdstc
instruction is inside _mach_absolute_time
.)