2011-04-23 2 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() 호출을 제외하고 "getgid : 0x10"(vdso로 점프)은 실행되지 않습니다. 이는 다른 getpid 호출이 캡처되지 않은 이유 일 수 있습니다. 그러나 나는 왜 그런지 모른다.

리눅스 커널 : 2.6.24-29 GCC (GCC) 2.7의 libc 4.2.4 ,

감사합니다!

답변

4

Glibc는 호출간에 변경할 수 없으므로 결과를 캐시합니다. 예를 들어 소스 코드 here을 참조하십시오.

그래서 실제 syscall은 한 번만 실행됩니다. 다른 통화는 캐시에서 읽습니다. (코드는 쓰레드로 올바른 일을하기 때문에 간단하지 않습니다.)

+0

좋습니다. 그리고 또 다른 질문이 있습니다. 그것은 적절하지 않을 수 있습니다. 나는 첫 getpid() 호출이 gettimeofday처럼 vdso를 활용하여 사용자 커널 모드 전환을 트리거하지 않는지 궁금합니다. – Infinite

+0

또 다른 질문은 gdb를 사용하여 "call * % gs : 0x10"을 어떻게 수행 할 수 있습니까? – Infinite

+0

거기에 단 하나의 대답이 확실하지 않습니다. syscall은 플랫폼에 따라 다르게 처리됩니다 (즉, 32 비트 x86 및 64 비트 x86_64조차 다른 syscall 메커니즘을 가짐). 어쩌면 내가 틀렸을 것입니다. 아마도 그 질문에 대해 별도의 질문을 게시하고, 관심있는 아키텍처가 무엇인지, 특히 관심이있는 아키텍처가 있다면 syscalls를 지정하십시오. (필자는 – Mat

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'는 리눅스에서 표준 시스템 콜이 아니기 때문에'gettimeofday()'함수를 사용하여 시스템 콜 오버 헤드를 측정 할 수 없습니다 : vDSO 메커니즘을 통해 최적화됩니다. 내 랩탑에서 표준 시스템 호출은 약 225 ns 동안 지속되는 반면'gettimeofday '는 20 ns 동안 만 지속됩니다. – pdagog

관련 문제