读:witr——从源码看 Linux 如何获取进程信息
内容提要
witr 工具通过解析 Linux /proc 文件系统,回答“进程为何运行”的问题。它读取 /proc/[pid]/stat、cmdline、exe、cwd、environ、net 等文件,获取进程身份、启动时间、命令行、可执行路径、用户、祖先链、工作目录、网络端口、容器归属、systemd 服务、环境变量、内存、I/O、文件描述符、线程数等信息,并检测安全风险,最终构建出从 PID 1 到目标进程的完整因果链。
延伸解读
解析 /proc 的常见陷阱
文章展示了多个解析 /proc 的易错点:进程名可能含括号,需用 LastIndex 定位右括号;cmdline 和 environ 以空字节分隔;IPv4 地址在 /proc/net/tcp 中为小端序;IPv6 地址需按 32 位组倒序。这些细节对编写相关工具或脚本很有参考价值。
健康检查的局限性
witr 的健康检查基于简单阈值:CPU 时间超 2 小时或 RSS 超 1GB 即标记,且状态判断串行覆盖,僵尸状态可能被高 CPU 覆盖。这种启发式方法可能误报或漏报,实际使用时需结合具体场景判断。
容器与沙箱检测的依赖
witr 通过 cgroup 路径和环境变量识别容器,但依赖 docker、podman 等 CLI 获取容器名,若未安装或权限不足则无法显示。此外,cgroup v2 下路径格式不同,但关键字匹配仍有效。检测结果仅供参考,并非绝对可靠。
Q&A
witr 工具是做什么的?
witr(Why Is This Running)是一个用于回答“这个进程为什么在运行”的工具。它通过解析 Linux 的 /proc 文件系统,获取进程的详细信息,并构建从 PID 1 到目标进程的完整因果链,包括谁启动的、怎么启动的、有没有危险等。
如何从 /proc/[pid]/stat 中提取进程名?
进程名在 /proc/[pid]/stat 中位于括号内,但进程名本身可能包含括号,因此需要找到第一个 '(' 和最后一个 ')',中间的内容即为进程名。例如,使用 Go 代码中的 strings.Index 和 strings.LastIndex 来定位括号。
如何计算进程的启动时间?
进程启动时间通过读取 /proc/[pid]/stat 中的 starttime 字段(单位是 clock ticks),结合系统启动时间(/proc/stat 中的 btime)和每秒 tick 数(通常为 100)来计算。公式为:启动时间 = btime + starttime / ticks_per_second。
如何获取进程的完整命令行?
读取 /proc/[pid]/cmdline 文件,该文件中的参数以空字符(\0)分隔,将空字符替换为空格即可得到完整命令行。注意进程名在 stat 中会被截断到 15 个字符,而 cmdline 提供完整信息。
如何判断一个进程是否运行在容器中?
通过读取 /proc/[pid]/cgroup 文件,检查其中是否包含 docker、podman、kubepods、containerd、colima 等关键字。如果匹配,则进程运行在相应的容器中。此外,还可以通过环境变量检测 Snap 和 Flatpak 沙箱。
如何将端口号映射到对应的进程 PID?
首先解析 /proc/net/tcp 和 /proc/net/tcp6 等文件,获取监听端口和对应的 socket inode。然后遍历 /proc/[pid]/fd/ 目录,读取每个文件描述符的符号链接,如果链接以 'socket:[' 开头,则提取 inode 号,与 socket 表中的 inode 匹配,从而确定端口对应的 PID。
如何检测进程是否使用了危险的安全能力(Capabilities)?
读取 /proc/[pid]/status 文件中的 CapEff 字段,该字段是一个十六进制掩码,每一位对应一个 capability。如果掩码中包含危险的能力(如 CAP_SYS_ADMIN),则可能存在安全风险。
如何获取进程的 I/O 统计信息?
读取 /proc/[pid]/io 文件,其中包含 rchar、wchar、syscr、syscw、read_bytes、write_bytes 等字段,分别表示进程读写的总字节数、系统调用次数以及实际磁盘读写字节数。