1

如何在 Linux 下确定线程堆栈的基数及其大小?是否有可用的 C/C++ API 或在 gdb 中查找的方法?谢谢

4

2 回答 2

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;
}

这是因为%rbp寄存器(或%ebp32 位上的寄存器)用于存储指向父堆栈帧基址的指针(保存的%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;
}
于 2015-05-08T05:15:49.027 回答
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

这可能与您正在寻找的内容非常接近。

于 2015-06-13T22:36:41.397 回答