上面的正规军方法,主要是通过系统函数获取了线程的属性信息,从而获取了栈区的开始地址和栈的总空间大小。
为了获取这两个值,调用了 3 个函数,有点笨重!
不知各位小伙伴是否想起:Linux 操作系统会为一个应用程序,都提供了一些关于 limit 的信息,这其中就包括堆栈的相关信息。
这样的话,我们就能拿到一个线程的栈空间总大小了。
此时,还剩下最后一个变量不知道:栈区的开始地址!
我们来分析一下哈:当一个线程刚刚开始执行的时候,栈区里可以认为是空的,也就是说此时 ESP 寄存器里的值就可以认为是指向栈区的开始地址!
是不是有豁然开朗的感觉?!
但是,这仍然需要调用汇编代码来获取。
再想一步,既然此时栈区里可以认为是空的,那么如果在线程的第一个函数中,定义一个局部变量,然后通过获取这个局部变量的地址,不就相当于是获取到了栈区的开始地址了吗?
如下图所示:
我们可以把这个局部变量的地址,记录在一个全局变量中。然后在应用程序的其他代码处,就可以用它来代表栈的起始地址。
知道了 3 个必需的变量,就可以计算栈空间的使用情况了:
// 用来存储栈区的起始地址 size_t top_stack; void print_stack2() { size_t used, avail; size_t esp_val; asm("movl %%esp, %0" : "=m"(esp_val) :); printf("esp_val = %p \n", esp_val); used = top_stack - esp_val; struct rlimit limit; getrlimit(RLIMIT_STACK, &limit); avail = limit.rlim_cur - used; printf("print_stack2: used = %d, avail = %d, total = %d \n", used, avail, used + avail); } int main(int argc, char *agv[]) { int x = 0; // 记录栈区的起始地址(近似值) top_stack = (size_t)&x; print_stack2(); return 0; }更讨巧的方式
在上面的两种方法中,获取栈的当前指针位置的方式,都是通过汇编代码,来获取寄存器 ESP 中的值。
是否可以继续利用刚才的技巧:通过定义一个局部变量的方式,来间接地获取 ESP 寄存器的值?
void print_stack3() { int x = 0; size_t used, avail; // 局部变量的地址,可以近似认为是 ESP 寄存器的值 size_t tmp = (size_t)&x; used = top_stack - tmp; struct rlimit limit; getrlimit(RLIMIT_STACK, &limit); avail = limit.rlim_cur - used; printf("print_stack3: used = %d, avail = %d, total = %d \n", used, avail, used + avail); } int main(int argc, char *agv[]) { int x = 0; top_stack = (size_t)&x; print_stack3(); return 0; }总结
以上的几种方式,各有优缺点。
我们把以上 3 个打印堆栈使用情况的函数放在一起,然后在 main 函数中,按顺序调用 3 个测试函数,每个函数中都定义一个整型数组(消耗 4K 的栈空间),然后看一下这几种方式的打印输出信息:
// 测试代码(3个打印函数就不贴出来了) void print_stack1() { ... } void print_stack2() { ... } void print_stack3() { ... } void func3() { int num[1024]; print_stack1(); printf("\n\n ********* \n"); print_stack2(); printf("\n\n ********* \n"); print_stack3(); } void func2() { int num[1024]; func3(); } void func1() { int num[1024]; func2(); } int main(int argc, char *agv[]) { int x = 0; top_stack = (size_t)&x; func1(); return 0; }打印输出信息:
espVal = 0xffe8c980 statck top = 0xff693000 stack bottom = 0xffe90000 print_stack1: used = 13952, avail = 8362368, total = 8376320 ********* esp_val = 0xffe8c9a0 print_stack2: used = 12456, avail = 8376152, total = 8388608 ********* print_stack3: used = 12452, avail = 8376156, total = 8388608------ End ------
让知识流动起来,越分享越幸运!
Hi~,我是道哥,嵌入式开发老兵。
星标公众号,能更快找到我!
推荐阅读