2013-03-20 48 views
1

如何在Linux下确定线程堆栈的大小及其大小?有没有可用的C/C++ API或在gdb中查找的方法? 感谢确定线程的堆栈大小和位置

+0

什么语言? C/C++?还有别的吗? – Mysticial 2013-03-20 05:01:28

+0

使用第0帧来获得最内层的框架,然后上来和上? – StarPinkER 2013-03-20 05:12:02

+2

为什么你想通过API找到?做什么的?你可以在TLS或全局数据中存储每个线程中一些起始局部变量的地址... – 2013-03-20 06:21:59

回答

1

这里是这样,涉及到阅读/proc/self/maps的不同方法。与其他一些方法不同,它不需要在程序开始时使用特殊的仪器,并且可以为堆栈的末尾提供精确的位置。

如果您尝试cat /proc/self/maps,你得到的东西是这样的:

00400000-0040c000 r-xp 00000000 08:01 6039736    /usr/bin/cat 
0060b000-0060c000 r--p 0000b000 08:01 6039736    /usr/bin/cat 
0060c000-0060d000 rw-p 0000c000 08:01 6039736    /usr/bin/cat 
00908000-00929000 rw-p 00000000 00:00 0     [heap] 
7fcdb1c68000-7fcdb1e01000 r-xp 00000000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb1e01000-7fcdb2000000 ---p 00199000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb2000000-7fcdb2004000 r--p 00198000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb2004000-7fcdb2006000 rw-p 0019c000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb2006000-7fcdb200a000 rw-p 00000000 00:00 0 
7fcdb200a000-7fcdb202c000 r-xp 00000000 08:01 6032717  /usr/lib/ld-2.21.so 
7fcdb21f5000-7fcdb21f8000 rw-p 00000000 00:00 0 
7fcdb2209000-7fcdb222b000 rw-p 00000000 00:00 0 
7fcdb222b000-7fcdb222c000 r--p 00021000 08:01 6032717  /usr/lib/ld-2.21.so 
7fcdb222c000-7fcdb222d000 rw-p 00022000 08:01 6032717  /usr/lib/ld-2.21.so 
7fcdb222d000-7fcdb222e000 rw-p 00000000 00:00 0 
7ffe78c41000-7ffe78c62000 rw-p 00000000 00:00 0   [stack] 
7ffe78dba000-7ffe78dbc000 r--p 00000000 00:00 0   [vvar] 
7ffe78dbc000-7ffe78dbe000 r-xp 00000000 00:00 0   [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 

正如你可以看到,有一个[stack]条目。这可能是你正在寻找的。

一个例子程序来解析该行指出:

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

int main(int argc, char *argv[]) { 
    FILE *file = fopen("/proc/self/maps", "r"); 
    char line[1024]; 
    void *result = NULL; 
    while (!feof(file)) { 
     if (fgets(line, sizeof(line)/sizeof(char), file) == NULL) { 
      break; 
     } 
     unsigned long start, end, offset; 
     unsigned int devma, devmi, ino; 
     char perms[6]; 
     char path[128]; 
     if (sscanf(line, "%lx-%lx %5s %lx %d:%d %d %127s", &start, &end, &perms, &offset, &devma, &devmi, &ino, &path) != 8) { 
      continue; // could not parse. fail gracefully and try again on the next line. 
     } 
     if (strcmp(path, "[stack]") == 0) { // use [stack:TID] for a thread besides the main thread 
      printf("Stack found from %lx to %lx\n", start, end); 
      break; 
     } 
    } 
    fclose(file); 
    return 0; 
} 

这将打印出类似这样:

Stack found from 7fff91834000 to 7fff91855000 

这可能是相当接近你在找什么。

+0

这就是我最终做到的。如果您有多个线程并且需要为特定线程查找堆栈,则可以使用任何堆栈变量的地址并将其与堆栈范围进行匹配。 – hlitz 2015-06-14 17:40:56

+0

是的 - 你也可以使用'gettid()'和'[stack:TID]'。 – 2015-06-14 18:26:20

1

如果您能够接受没有得到确切的堆栈顶部(这可能足够了我的应用程序),遍历帧指针可以工作:

// main.c 
#include <stdint.h> 

// -fno-omit-frame-pointer might be required - otherwise you might get a crash 
// might not be exactly at the top; but probably very close 

void *stack_top() { 
    void **top; 
    asm("movq %%rbp, %0" : "=r" (top)); // set top to %rbp - replace with %ebp for 32-bit x86 
    // if top is higher in memory than the variable, then still part of the stack. 
    while ((uintptr_t) *top > (uintptr_t) &top) { 
     top = *top; 
    } 
    return top; 
} 

此操作,因为在32位的%rbp寄存器(或%ebp寄存器)被用于将指针存储到父堆栈帧的基(其中保存%rbp%ebp值是) - 所以我们可以迭代遍历这个链表直到我们到达一个无效的地址。

注意stack_top可能在涉及&top比较某些情况下失败 - 我的系统端接一个指针,指向程序加载代码的位置的链表,所以这是我发现,以检测它的最好方法 - 但你会想要彻底地测试它。 (如果任何人有一个更好的办法来检测链的末端,请添加评论。)

实例测试程序:

#include <stdio.h> 
#include <pthread.h> 

void *test_another_layer(int x) { 
    return stack_top(); 
} 

void *subthread(void *ptr) { 
    void *st1 = stack_top(); 
    void *st2 = test_another_layer(0); 
    void *st3 = &ptr; 
    printf("stack tops 2: %x %x %x\n", st1, st2, st3); 
    return NULL; 
} 

int main(int argc, char *argv[]) { 
    void *st1 = stack_top(); 
    void *st2 = test_another_layer(0); 
    void *st3 = &argc; 
    printf("stack tops: %x %x %x\n", st1, st2, st3); 
    pthread_t ot; 
    if (pthread_create(&ot, NULL, subthread, NULL) != 0) { 
     perror("cannot create"); 
     return 1; 
    } 
    if (pthread_join(ot, NULL) != 0) { 
     perror("cannot join"); 
     return 2; 
    } 
    return 0; 
}