Linux應用程序設計:如何獲取線程棧的使用信息?
把以上代碼放在一起:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <sys/resource.h>
void print_stack1()
{
size_t used, avail;
pthread_attr_t attr;
void *stack_addr;
int stack_size;
// 獲取棧寄存器 ESP 的當前值
size_t esp_val;
asm("movl %%esp, %0" : "=m"(esp_val) :);
// 通過線程屬性,獲取棧區的起始地址和空間總大小
memset(&attr, 0, sizeof(pthread_attr_t));
pthread_getattr_np(pthread_self(), &attr);
pthread_attr_getstack(&attr, &stack_addr, &stack_size);
pthread_attr_destroy(&attr);
printf("espVal = %p ", esp_val);
printf("statck top = %p ", stack_addr);
printf("stack bottom = %p ", stack_addr + stack_size);
avail = esp_val - (size_t)stack_addr;
used = stack_size - avail;
printf("print_stack1: used = %d, avail = %d, total = %d ",
used, avail, stack_size);
}
int main(int argc, char *agv[])
{
print_stack1();
return 0;
}
雜牌軍方式
上面的正規軍方法,主要是通過系統函數獲取了線程的屬性信息,從而獲取了棧區的開始地址和棧的總空間大小。
為了獲取這兩個值,調用了 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 ", 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 ",
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 ",
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(" ********* ");
print_stack2();
printf(" ********* ");
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
請輸入評論內容...
請輸入評論/評論長度6~500個字
最新活動更多
- 1 AI狂歡遇上油價破百,全球股市還能漲多久? | 產聯看全球
- 2 OpenAI深夜王炸!ChatGPT Images 2.0實測:中文穩、細節炸,設計師慌了
- 3 6000億美元估值錨定:字節跳動的“去單一化”突圍與估值重構
- 4 Tesla AI5芯片最新進展總結
- 5 連夜測了一波DeepSeek-V4,我發現它可能只剩“審美”這個短板了
- 6 熱點丨AI“瑜亮之爭”:既生OpenClaw,何生Hermes?
- 7 AI界的殺豬盤:9秒刪庫跑路,全員被封號,還繼續扣錢!
- 8 2026,人形機器人只贏了面子
- 9 DeepSeek降價90%:價格屠夫不是身份,是戰略
- 10 AI Infra產業鏈卡在哪里了?


分享













