Почему многие системные вызовы (getpid) перехватываются только один раз с использованием strace? - PullRequest
3 голосов
/ 23 апреля 2011

Я вызывал «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  

Я не совсем понимаю код ассемблера.Также было бы полезно, если бы кто-то мог дать некоторые подробные объяснения по этому поводу.Согласно моим наблюдениям "call *% gs: 0x10" (который переходит в vdso) не выполняется, за исключением первого вызова getpid (), который может быть причиной того, что другие вызовы getpid не перехватываются.Но я не знаю почему.

Ядро Linux: 2.6.24-29 gcc (GCC) 4.2.4 libc 2.7,

Спасибо!

Ответы [ 2 ]

4 голосов
/ 23 апреля 2011

Glibc кэширует результат, поскольку он не может меняться между вызовами.См., Например, исходный код здесь .

Таким образом, настоящий системный вызов выполняется только один раз.Остальные звонки просто читаются из кеша.(Код не очень прост, потому что он заботится о том, чтобы делать правильные вещи с потоками.)

3 голосов
/ 23 апреля 2011

glibc кэширует значение pid.Когда вы в первый раз вызываете getpid, он запрашивает ядро ​​для pid, в следующий раз он просто возвращает значение, полученное при первом вызове syscall getpid.

glibc code:

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 не могут оптимизировать вызовы к нему.

...