Linux kernel 为什么使用strace只捕获一次许多系统调用(getpid)?

Linux kernel 为什么使用strace只捕获一次许多系统调用(getpid)?,linux-kernel,system-calls,libc,strace,Linux Kernel,System Calls,Libc,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 arg

我在程序中多次调用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;
}
我不太懂汇编代码。如果有人能给出一些详细的解释,这也会很有帮助。根据我的观察,“call*%gs:0x10”(,跳入vdso)没有执行,除了第一个getpid()调用之外,这可能是后续getpid()调用没有被捕获的原因。但我不知道为什么

linux内核:2.6.24-29 通用条款(gcc)4.2.4 libc 2.7


谢谢

Glibc缓存结果,因为它不能在调用之间更改。例如,请参阅源代码


因此,真正的系统调用只执行一次。其他调用只是从缓存中读取。(代码不是很简单,因为它负责对线程执行正确的操作。)

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;
}
pid\t
__getpid(无效)
{
#如果定义不在libc中
内部系统调用(err);
pid\u t结果=内部系统调用(getpid,err,0);
#否则
pid\u t result=THREAD\u GETMEM(THREAD\u SELF,pid);

如果(uuu builtin_expect(result如今,随着pid_名称空间的引入和在应用程序中检测到的大量错误,在信号接收时或当它们通过调用而不是fork()、vfork()和clone()来创建子进程时,pid不再缓存在GLIBC中。这一点在:

从glibc 2.3.4版到2.24版(包括2.24版) 用于getpid()缓存PID的glibc包装函数,目标是
在进程调用getpid()时避免额外的系统调用
重复。通常这种缓存是不可见的,但它是正确的
操作依赖于fork(2)包装函数中的支持,
vWork(2)和clone(2):如果应用程序绕过了glibc
使用syscall(2)包装这些系统调用,然后使用调用
在子对象中设置getpid()将返回错误的值(即
精确:它将返回父进程的PID)。在
此外,在某些情况下,getpid()可能返回错误的
即使在通过glibc包装函数调用clone(2)时也是如此。
(有关一种情况的讨论,请参阅克隆中的错误(2)。
此外,缓存代码的复杂性是最大的 多年来,glibc内部的一些bug的来源


很好。我还有另一个问题,可能与此无关。我想知道,对于gettimeofday,即使是第一个getpid()调用是否也不会通过利用vdso触发用户内核模式切换?另一个问题是,我如何单步进入“call*%gs:0x10”使用gdb?我不确定是否有一个单一的答案。系统调用的处理方式因平台而异(即,即使是32位x86和64位x86_64也有不同的系统调用机制)。但也许我错了-您可能应该就此提出一个单独的问题,并指定哪种体系结构您感兴趣的是什么,如果有您特别感兴趣的系统调用(我对gdb不太了解)@SIFE:我不知道,我也不认为他们会提供该功能的任何原因-进程的PID永远不会改变。@Mat但这并不能回答我的问题。您不能使用
gettimeofday()
函数测量系统调用开销,因为
gettimeofday
不是Linux上的标准系统调用:它通过vDSO机制进行了优化。在我的笔记本电脑上,标准系统调用持续时间约为225 ns,而
gettimeofday
仅持续20 ns。
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;
}