Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does gettimeofday() on macOS use a system call?

I expect that gettimeofday() will call a system call to do the work of actually getting the time. However, running the following program

#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;
}

under dtruss -d returns a long list of system calls, the last of which are:

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

It looks like gettimeofday() isn't using a syscall, but this seems wrong-surely the kernel takes responsibility of the system clocks? Is dtruss missing something? Am I reading the output incorrectly?

like image 691
asnr Avatar asked Dec 05 '16 05:12

asnr


1 Answers

As TheDarkKnight pointed out, there is a gettimeofday system call. However, the userspace gettimeofday function often does not call the corresponding system call, but rather __commpage_gettimeofday, which tries to read the time from a special part of the process' address space called the commpage. Only if this call fails does the gettimeofday system call get used as a fallback. This reduces the cost of most calls to gettimeofday from that of an ordinary system call to just a memory read.

The book Mac OSX Internals: A Systems Approach describes the commpage. Briefly, it is a special area of kernel memory that is mapped into the last eight pages of the address space of every process. Among other things, it contains time values that are "updated asynchronously from the kernel and read atomically from user space, leading to occasional failures in reading".

To see how often the gettimeofday() system call is called by the userspace function, I wrote a test program that called gettimeofday() 100 million times in a tight loop:

#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;
}

Running this under dtruss -d on my machine showed that this triggered between 10-20 calls to the gettimeofday() system calls (0.00001%-0.00002% of all the userspace calls).


For those interested, the relevant lines in the source code for the userspace gettimeofday() function (for macOS 10.11 - El Capitan) are

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

The function __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.)

like image 129
asnr Avatar answered Oct 01 '22 11:10

asnr