macOS上的gettimeofday()是否使用系统调用?

asn*_*snr 7 c macos time operating-system system-calls

我希望这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;
}
Run Code Online (Sandbox Code Playgroud)

under dtruss -d返回一长串系统调用,最后一个是:

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\0", 0x19)       = 25 0
    3886 write_nocancel(0x1, "After gettimeofday() 1480913810\n\0", 0x20)        = 32 0
    3887 write_nocancel(0x1, "Exiting ! 1480913810\n\0", 0x15)       = 21 0
Run Code Online (Sandbox Code Playgroud)

它看起来好像gettimeofday()没有使用系统调用,但这似乎是错误的 - 当然内核负责系统时钟?是dtruss失去了一些东西?我读错了输出吗?

asn*_*snr 10

正如TheDarkKnight 指出的,有一个gettimeofday系统调用.然而,用户空间的gettimeofday功能往往并没有调用相应的系统调用,而是__commpage_gettimeofday,它试图从调用进程的地址空间中的一个特殊部分读取时间commpage.仅当此调用失败时,gettimeofday系统调用才会被用作回退.这将大多数调用的成本gettimeofday从普通系统调用降低到只读取内存.

" Mac OSX Internals:A Systems Approach "一书描述了这种复杂性.简而言之,它是内核内存的一个特殊区域,映射到每个进程的地址空间的最后八页.除此之外,它包含的时间值"从内核异步更新并从用户空间原子读取,导致偶尔读取失败".

为了查看gettimeofday()用户空间函数调用系统调用的频率,我编写了一个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;
}
Run Code Online (Sandbox Code Playgroud)

dtruss -d在我的机器上运行它表明这触发了10到20次gettimeofday()系统调用之间的呼叫(0.00001%-0.00002%的所有用户空间调用).


对于那些感兴趣的人,用户空间功能的源代码中的相关行gettimeofday()(对于macOS 10.11 - El Capitan)是

if (__commpage_gettimeofday(tp)) {      /* first try commpage */
    if (__gettimeofday(tp, NULL) < 0) { /* if it fails, use syscall */
        return (-1);
    }
}
Run Code Online (Sandbox Code Playgroud)

该功能__commpage_gettimeofday 结合了从commpage读取的时间戳和时间戳计数器寄存器的读数来计算自纪元以来的时间,以秒和微秒为单位.(rdstc指令在里面_mach_absolute_time.)