![如何使用 /proc/ 找到子任务(线程)的堆栈/地图?](https://linux22.com/image/225522/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8%20%2Fproc%2F%20%E6%89%BE%E5%88%B0%E5%AD%90%E4%BB%BB%E5%8A%A1%EF%BC%88%E7%BA%BF%E7%A8%8B%EF%BC%89%E7%9A%84%E5%A0%86%E6%A0%88%2F%E5%9C%B0%E5%9B%BE%EF%BC%9F.png)
目的:
我正在理论如何在 Linux 环境中创建指针扫描器。免责声明:
我的发现已经在 Debian Bookworm(当前稳定版)和具有自定义内核的 Gentoo 系统上进行了测试。没有观察到差异。问题:
在不将调试器附加到目标进程的情况下,我希望能够识别每个线程/子任务堆栈的 VMA。这应该可以使用以下方法实现进程伪文件系统
讨论:
在 Linux 4.5 之前,将在路径名字段中/proc/[parent_tid]/maps
标记父任务的堆栈区域,并使用 标记每个子任务的堆栈区域。[STACK]
[STACK:child_tid]
在 Linux 4.5 之后,只有父任务的堆栈区域保留其[STACK]
标签。子任务堆栈区域现在没有标签。在此更改的提交消息中(请参阅链接 1),Johannes Weiner 指出,仍然可以通过观察子任务堆栈 VMA 的流程图来查看它们/proc/[parent_tid]/task/[child_tid]/maps
。
事实证明这对我来说是无效的。父任务和子任务之间的内存区域映射是相同的。/proc/[parent_tid]/maps
== /proc/[parent_tid]/task/[child_tid*]/maps
。这最终意味着[STACK]
标签贴在同一区域。
/proc/[tid]/stat
可用于查找给定任务的堆栈底部的 VMA。这是第 28 个值(参见 man 5 proc)。再次,/proc/[parent_tid]/stat
。/proc/[parent_tid]/task/[child_tid*]/stat
显然,子任务不与进程中的所有其他任务共享堆栈的开头。
clone(2)
,用于创建新子任务的系统调用,采用堆栈指针作为参数。获取堆栈内存的最明显方法是通过匿名mmap(2)
。匿名内存映射在/proc/[tid]/maps
.通过观察一些单线程和多线程进程,匿名内存映射的数量和程序的线程数之间存在直接的相关性。虽然此类映射不仅仅用于线程堆栈,但我非常希望线程堆栈能够以这种方式分配。每个内存映射在 中都有自己的条目/proc/[tid]/maps
。当然有一种方法可以确定其中哪些充当子任务堆栈?
我在这里犯了什么错?
相关链接:
[STACK:tid]
承诺从以下位置删除标签/proc/[tid]/maps
:
https://lists.ubuntu.com/archives/kernel-team/2016-March/074681.html
答案1
第 28 个字段适用于我,在 Linux 6.1.0 上。请注意,任务不会有自己的堆栈,除非它们需要它。
这是一个要运行的简单脚本,它应该返回不同的底部堆栈值一些的任务。您可能需要下载/安装 Firefox。
#!/bin/bash
# create a temporary profile
test -d /tmp/temporary_profile || mkdir /tmp/temporary_profile
# run Firefox on that profile
firefox --profile /tmp/temporary_profile &
# Give it some time to create the tasks
sleep 10
# Get the PID
FFPID=$!
# Print field 28 of the stat files
awk '{print FILENAME" "$28}' /proc/$FFPID/stat /proc/$FFPID/task/*/stat
如果您运行的是 Firefox,则可以运行以下行并键入其当前进程 ID:
read a;awk '{print FILENAME" "$28}' /proc/$a/stat /proc/$a/task/*/stat
它可以与其他多线程程序一起使用,但我知道 Firefox 几乎会立即创建一些具有自己的堆栈的任务,这可能会帮助您检查代码是否正常工作。