2011-04-23 66 views
3

我在程序中調用了「getpid()」多次(以測試系統調用的效率),但是當我使用strace獲取跟蹤時,只捕獲到一個getpid調用。爲什麼使用strace只能捕獲一次系統調用(getpid)?

代碼很簡單:

#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 

void print_usage(){ 
    printf("Usage: program count\n"); 
    exit(-1); 
} 

int main(int argc, char** argv){ 
    if(argc != 2) 
     print_usage(); 
    int cnt = atoi(argv[1]); 
    int i = 0; 
    while(i++<cnt) 
     getpid(); 
    return 0; 
} 

我用gdb,並得到這個:

(gdb) disasse 
Dump of assembler code for function getpid: 
0xb76faac0 <getpid+0>: mov %gs:0x4c,%edx 
0xb76faac7 <getpid+7>: cmp $0x0,%edx 
0xb76faaca <getpid+10>: mov %edx,%eax 
0xb76faacc <getpid+12>: jle 0xb76faad0 <getpid+16> 
0xb76faace <getpid+14>: repz ret 
0xb76faad0 <getpid+16>: jne 0xb76faadc <getpid+28> 
0xb76faad2 <getpid+18>: mov %gs:0x48,%eax 
0xb76faad8 <getpid+24>: test %eax,%eax 
0xb76faada <getpid+26>: jne 0xb76faace <getpid+14> 
0xb76faadc <getpid+28>: mov $0x14,%eax 
0xb76faae1 <getpid+33>: call *%gs:0x10 
0xb76faae8 <getpid+40>: test %edx,%edx 
0xb76faaea <getpid+42>: mov %eax,%ecx 
0xb76faaec <getpid+44>: jne 0xb76faace <getpid+14> 
0xb76faaee <getpid+46>: mov %ecx,%gs:0x48 
0xb76faaf5 <getpid+53>: ret 

我不太懂彙編代碼。如果有人能夠對此進行一些詳細的解釋,那也是有幫助的。根據我的觀察,除了第一個getpid()調用之外,「調用*%gs:0x10」(跳轉到vdso)不會執行,這可能是其他getpid調用未捕獲的原因。但我不知道爲什麼。

Linux內核:2.6.24-29 海合會(GCC)4.2.4 的libc 2.7,

謝謝!

回答

4

Glibc緩存結果,因爲它不能在兩次調用之間切換。例如,請參閱源代碼here

所以真正的系統調用只能執行一次。其他調用只從緩存中讀取。 (代碼不是非常簡單,因爲它需要線程處理正確的事情。)

+0

太好了。我還有一個問題;它可能不相關。我想知道gettimeofday是否通過利用vdso來實現第一個getpid()調用不會觸發用戶內核模式切換? – Infinite 2011-04-23 18:59:08

+0

另一個問題是如何使用gdb單步進入「call *%gs:0x10」? – Infinite 2011-04-23 19:00:42

+0

我不確定這有一個單一的答案。根據平臺不同,系統調用的處理方式也不同(即使32位x86和64位x86_64也有不同的系統調用機制)。但也許我錯了 - 你應該爲此發佈一個單獨的問題,並確定你感興趣的體系結構,以及如果你有一些特別感興趣的系統調用。根本不瞭解gdb) – Mat 2011-04-23 19:02:14

3

glibc緩存pid值。第一次調用getpid時,它會向內核請求pid,下一次它會返回從第一個getpid系統調用中獲得的值。

的glibc代碼:

pid_t 
__getpid (void) 
{ 
#ifdef NOT_IN_libc 
    INTERNAL_SYSCALL_DECL (err); 
    pid_t result = INTERNAL_SYSCALL (getpid, err, 0); 
#else 
    pid_t result = THREAD_GETMEM (THREAD_SELF, pid); 
    if (__builtin_expect (result <= 0, 0)) 
    result = really_getpid (result); 
#endif 
    return result; 
} 

如果你想測試系統調用的開銷,gettimeofday()經常用來做到這一點 - 做內核的工作是非常小的,而且無論是編譯器和C庫可以優化掉對它的呼叫。

+0

由於'gettimeofday'不是Linux上的標準系統調用,您不能使用'gettimeofday()'函數來確定系統調用開銷,因爲它是通過vDSO機制進行優化的。在我的筆記本電腦上,一個標準的系統調用持續約225ns,而'gettimeofday'只持續20ns。 – pdagog 2018-03-09 14:35:16