2016-12-05 112 views
7

我預計gettimeofday()會調用系統調用來完成實際獲得時間的工作。但是,運行下面的程序macOS上的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回報系統調用的一個長長的清單,其中最後一個是:

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 

它看起來像gettimeofday()沒有使用的系統調用,但這似乎錯了 - 內核肯定會承擔系統時鐘的責任? dtruss錯過了什麼?我讀錯了嗎?

+2

在Solaris上,'gettimeofday()'只是讀取內存位置。很可能是因爲macOS Sierra(或之前的Mac OS X版本)有類似的情況發生。您可能會也可能不會注意到,macOS Sierra(但不是早期版本)最後支持'clock_gettime()'。 –

+2

您可以在[源代碼]中看到'gettimeofday()'系統調用(https://opensource.apple.com/source/xnu/xnu-3789.1.32/bsd/kern/syscalls.master.auto.html ) - #116 – TheDarkKnight

+2

在Linux上,像gettimeofday()這樣的系統調用可以實現爲*「普通函數調用和少量內存訪問」*:[vDSO](http://man7.org/linux/) man-pages/man7/vdso.7.html) – jfs

回答

6

As TheDarkKnight pointed out,有一個系統調用。但是,用戶空間gettimeofday功能往往是而不是調用相應的系統調用,而是__commpage_gettimeofday,它試圖從過程的地址空間的一個特殊部分讀取時間,稱爲commpage。只有在此呼叫失敗的情況下,gettimeofday系統呼叫才能用作後備。這樣可以將大多數來自gettimeofday的調用的成本從普通系統調用的成本降低到只讀內存。

Mac OSX Internals: A Systems Approach描述了commpage。簡而言之,它是內核內存的一個特殊區域,映射到每個進程的地址空間的最後八頁。除此之外,它包含「從內核異步更新並從用戶空間原子讀取的時間值,導致偶然的讀取失敗」。

要看到多久gettimeofday()系統調用是由用戶空間的函數調用,我寫道,在一個緊湊的循環稱爲gettimeofday()了100萬次的測試程序:

#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下,在我的機器上顯示這觸發了10-20次調用gettimeofday()系統調用(所有用戶空間調用的0.00001%-0.00002%)。


對於那些有興趣,相關線路中的source code爲用戶空間gettimeofday()功能(MACOS 10。11 - 埃爾卡皮坦)是

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

功能__commpage_gettimeofdaycombines時間戳從commpage和時間戳計數器寄存器的讀取來讀取來計算由於秒和毫秒曆元的時間。 (rdstc指令位於_mach_absolute_time之內。)

+0

非常有趣。謝謝。 –

+0

很好的答案。您提到:_這樣可以將大部分gettimeofday調用的開銷從普通系統調用的開銷降低到只讀內存。這看起來並不嚴格:___ commpage_gettimeofday和_mach_absolute_time函數(前者調用後者)每個都有幾十條指令,它們之間可能共有10條內存讀取,並且'rdtsc'調用本身是二十多個週期或更多(另外由於某種原因,它們有兩條'lfence'指令)。所以可能你至少在看50個週期。還是比內核調用好得多! – BeeOnRope

1

使用dtrace而不是dtruss會清除您的疑惑。 gettimeofday()本身就是一個系統調用。如果您運行dtrace腳本,您可以看到這個系統調用被調用。

您可以使用下面DTrace腳本 「dtrace1.d」

syscall:::entry 
/execname == "foo"/
{ 
} 

(foo是你的可執行文件的名稱)

運行上面的DTrace使用:DTrace的-s dtrace1.d

然後執行你的程序來查看你的程序使用的所有系統調用

+0

我遵循你的指示,'dtrace'沒有標識'gettimeofday'系統調用。它的輸出與'dtruss'幾乎相同,除了在程序的最後列出對'exit()'的調用外,最後五個系統調用(請參見問題)是相同的。我正在運行El Capitan(10.11.6)。你在電腦上看到不同的東西嗎? – asnr