2011-04-23 67 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