Linux系统下如何正确获取进程中各独立线程的栈区域信息
现象原因
你之前操作得到的所有线程[stack]条目完全一致,核心原因是Linux内核仅会给进程的主线程栈在/proc/[tid]/maps中标记[stack]标识。其他由pthread_create创建的用户态线程的栈,是pthread库通过mmap申请的匿名私有内存段,不会带[stack]标记,所有线程tid对应的maps里的[stack]都指向主线程栈,所以输出完全相同。
正确获取各线程栈区域的方法
方法1:gdb调试获取(适合允许附加调试的场景)
操作步骤:
- 拿到目标进程PID,假设为
$PID - 执行命令附加进程:
gdb -p $PID - 在gdb交互界面执行以下命令,即可批量输出所有线程的栈顶指针:
thread apply all print $sp
拿到每个线程的栈顶指针$sp后,对应到/proc/$PID/maps中包含$sp的匿名内存段,就是该线程的栈区域,区间差值就是栈大小。
如果需要更精确的栈参数,可执行以下gdb脚本直接输出所有线程栈的基址和大小:
define dump_all_stack set $i = 0 while $i < sizeof (gdb.inferiors()[0].threads()) thread $i+1 set $attr = (pthread_attr_t*)malloc(sizeof(pthread_attr_t)) call pthread_getattr_np(pthread_self(), $attr) set $base = 0 set $size = 0 call pthread_attr_getstack($attr, &$base, &$size) printf "线程TID %d: 栈基址0x%lx, 栈大小%ld Bytes, 栈区间[0x%lx, 0x%lx)\n", $_thread, $base, $size, $base, $base+$size call free($attr) set $i = $i + 1 end end dump_all_stack
方法2:纯proc文件系统获取(适合禁止调试的生产环境)
无需修改程序、无需附加调试,直接通过系统提供的proc节点读取:
- 先获取目标进程PID,假设为
1234 - 遍历进程下所有线程TID:
ls /proc/1234/task/ - 对每个TID(假设为
5678),执行命令获取该线程的栈底地址:cat /proc/1234/task/5678/stat | awk '{print $28}' - 执行
cat /proc/1234/maps查看进程所有内存映射,找到上述栈底地址所属的rw-p权限匿名内存段,该段的起止地址就是当前线程的栈区间,结束地址减起始地址就是栈大小。
注:pthread默认栈大小通常为8MB,如果你在创建线程时通过pthread_attr_setstack自定义了栈参数,上述两种方法依然有效。
内容的提问来源于stack exchange,提问作者Desperado17
相关产品推荐
相关产品推荐

