ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux进程溯源实战:从PID到启动来源的完整排查方法

Linux进程溯源实战:从PID到启动来源的完整排查方法 之前排查服务器高负载问题时经常遇到“进程明明看到了却说不清它是怎么起来的”。ps能列出进程kill能结束进程可一旦要回答“这个进程从哪来”“谁启动了它”“它为什么一直在运行”很多同学就会卡壳。本文整理了一套完整的进程溯源思路并动手实现一个轻量小工具witrWhat Is This Running。通过它我们可以像福尔摩斯一样从 PID 出发一层层查父进程、可执行文件、工作目录、网络连接、systemd 关系最后定位进程来源和启动原因。内容同样适合 Linux 系统下排查“可疑进程”“僵尸进程”“CPU 异常”等场景。1. 背景与核心概念1.1 什么是进程溯源进程溯源就是通过一系列系统命令和文件信息还原一个进程的“身份档案”和“出生过程”。一个进程在 Linux 中并不是孤立存在的它一定有一个父进程除非是内核启动的第一个进程一定对应某个可执行文件一定继承了某些环境变量并且大概率会打开文件描述符、网络连接或 socket。当我们需要回答下面这些问题时本质上就是在做进程溯源PID 是 31415 的这个进程它的父进程是谁它是从哪个路径下的二进制文件启动的它的启动命令是什么有没有带参数它是 systemd 托管的服务还是被某个脚本临时拉起它是否监听了端口是否在对外发起网络连接把这些问题串起来就能形成一个相对完整的“进程来源报告”。1.2 为什么会想知道“进程为什么运行”在实际生产环境中我们经常遇到以下几类问题服务器 CPU 突然飙高top里出现一个陌生的进程名但没人记得部署过它。一个服务被重复启动导致端口冲突需要找出是谁第二次拉起了它。某个脚本会定时执行但找不到对应的 crontab 或 systemd timer。服务器疑似被入侵出现异常进程需要快速判断进程的启动入口。排查性能问题时希望了解某个进程的工作目录、运行身份和启动参数。这些问题有一个共同点光看进程列表不够必须把进程和它的“根源”关联起来。1.3 witr 是什么witr是 What Is This Running 的缩写在本文中它既是一种排查思路也是一个用 Bash 实现的小工具脚本。简单来说witr接收一个 PID 或进程名然后自动帮我们收集以下信息进程基本信息PID、PPID、用户、组、状态、运行时长、启动时间。可执行文件路径和当前工作目录。启动命令行和关键状态字段。网络连接和监听端口。systemd cgroup 归属。父进程链。我们不用去记一堆命令一条witr命令就能输出结构化报告非常适合在排查现场快速使用。2. 环境准备与工具概览2.1 系统与权限准备本文以 Linux 环境为例不限制具体发行版。常见的 CentOS 7/8、Ubuntu 20.04/22.04、Rocky Linux 等都能使用。建议满足以下条件操作系统Linux内核版本建议 3.10 以上。ShellBash 4.0 以上。基础命令ps、pgrep、readlink、ss、grep、awk、tr。权限普通用户可查看大部分/proc信息查看网络连接的进程 PID 以及审计日志建议使用 root 或sudo。# 简单检查环境 bash --version | head -1 ps --version 2/dev/null || procps-ng version版本需要根据你的实际系统调整本文重点演示排查思路。2.2 常用命令速查在进入实战前先熟悉几个进程排查常用命令命令作用ps -ef查看当前所有进程含 PID 和 PPIDpstree -ps pid以树形展示进程父子关系pgrep -f 关键字根据进程名或完整命令行查找 PIDls -l /proc/pid/exe查看进程对应的可执行文件链接cat /proc/pid/cmdline查看进程启动命令行ss -tulpen查看网络连接、监听端口及所属进程cat /proc/pid/cgroup查看进程对应的 cgroup推断 systemd unitausearch -p pid查询该 PID 的审计日志需配置 auditd这些命令是witr的底层基础。理解它们比单纯背命令更重要。3. 进程溯源核心原理3.1 父进程 PPID查“生父”每个普通进程都有自己的父进程。当我们在终端执行一条命令时Shell如bash就是该命令的父进程当 systemd 启动某个服务时systemd 就是服务的父进程。查看父进程最简单的方式ps -o pid,ppid,cmd -p 12345输出中PPID就是父进程 PID。如果 PPID 是 1说明这个进程的父进程已经退出它被 systemd 或 init 进程收养。这类进程需要额外注意要么是正常守护进程要么是某个脚本启动后“脱管”的异常进程。用pstree可以更直观地看出进程树pstree -ps 12345例如输出systemd(1)───bash(1024)───python(12345)说明 PID 12345 的 python 进程是由 bash 启动的而 bash 又是在 systemd 的环境下运行的。3.2 /proc 文件系统进程的“户籍档案”Linux 系统会把每一个进程的信息以文件形式暴露在/proc/pid/目录下。这个目录就是进程的“档案袋”。常用关键文件包括文件作用/proc/pid/exe可执行文件的符号链接指向真实二进制文件/proc/pid/cwd当前工作目录的符号链接/proc/pid/cmdline启动命令行多个参数以\0分隔/proc/pid/status进程状态、PPID、UID、GID、线程数等/proc/pid/cgroup所在 cgroup可用于识别 systemd unit/proc/pid/fd打开的文件描述符列表/proc/pid/environ环境变量可能包含敏感信息/proc/pid/io进程 I/O 统计比如查看 PID 31415 的可执行文件路径readlink -f /proc/31415/exe如果输出是/tmp/.x/sysudp这种非标准路径就需要高度警惕。查看启动命令行tr \0 /proc/31415/cmdline很多恶意进程会刻意把进程名伪装成正常名称但/proc/pid/exe和cmdline往往能暴露出真实位置。3.3 网络连接、端口与进程绑定如果可疑进程存在网络行为查看它的网络连接会非常有价值。最常见的命令是ss -tulpen | grep 31415输出示例tcp ESTAB 0 0 192.168.1.100:52340 45.77.1.2:8443 users:((sysudp,pid31415,fd5))从中可以看出进程 PID、外部连接地址和端口。如果发现一个系统常见服务的进程名却连接了陌生 IP那么大概率存在问题。同样的方法也可以排查端口占用ss -lntup | grep :80803.4 启动方式和持久化入口一个进程如果“每次开机都会出现”说明它一定有一个持久化入口。常见的持久化位置包括systemd service 或 timer。crontab 定时任务。/etc/rc.local或/etc/rc.d/rc.local。Shell 配置文件~/.bashrc、~/.profile、/etc/profile。守护进程管理工具supervisord、pm2 等。使用 systemd 的系统可以通过 cgroup 快速判断进程归属cat /proc/31415/cgroup输出0::/system.slice/myapp.service如果出现了system.slice/xxx.service就能知道它由哪个 unit 管理。再看这个 unit 的具体内容systemctl cat myapp.service这样就找到了“为什么运行”的直接答案。3.5 审计日志当常规命令无法解释进程来源时Linux 审计系统 auditd 可以提供更底层的记录。前提是系统已经安装了 auditd 并配置了 execve 审计规则auditctl -a always,exit -F archb64 -S execve -k exec_log之后用ausearch按 PID 查询ausearch -p 31415 --start today审计日志会记录进程是谁在什么时间、由哪个父进程执行启动的对入侵溯源非常有帮助。注意审计规则会记录大量信息建议只在排查或合规要求时开启并定期清理日志。4. 动手实现一个轻量溯源工具witr4.1 设计目标witr的设计目标很简单支持输入 PID 或进程名。一键输出进程基本信息、可执行文件、命令行、网络、systemd 关系、父进程链。尽量只依赖系统常见命令不引入重型依赖。代码短小方便复制到服务器上直接使用。4.2 脚本完整实现将下面的内容保存为witr.sh#!/usr/bin/env bash # witr - What Is This Running # 进程溯源小工具 usage() { cat EOF 用法: ./witr.sh PID ./witr.sh 进程名 ./witr.sh -n 进程名 示例: ./witr.sh 31415 ./witr.sh nginx EOF exit 0 } inspect_pid() { local pid$1 echo 进程基本信息 ps -p $pid -o pid,ppid,user,group,stat,etime,lstart,cmd --no-headers 2/dev/null \ || { echo PID $pid 不存在或无权访问; return 1; } echo echo 可执行文件路径 if [ -e /proc/$pid/exe ]; then readlink -f /proc/$pid/exe else echo 无法读取可能是内核线程或权限不足 fi echo echo 当前工作目录 if [ -e /proc/$pid/cwd ]; then readlink -f /proc/$pid/cwd else echo 无法读取工作目录 fi echo echo 启动命令行 if [ -r /proc/$pid/cmdline ]; then tr \0 /proc/$pid/cmdline echo else echo 无法读取 cmdline fi echo echo 关键进程状态 grep -E ^(Name|Umask|State|Tgid|Pid|PPid|Uid|Gid|Threads|voluntary_ctxt_switches|nonvoluntary_ctxt_switches) \ /proc/$pid/status 2/dev/null || echo 无法读取 status echo echo 打开的网络连接 if command -v ss /dev/null 21; then ss -tulpen 2/dev/null | grep pid$pid || echo 没有发现网络连接或无权限查看 else echo 未安装 ss 命令 fi echo echo systemd 关系 cgroup$(grep -E system.slice|user.slice /proc/$pid/cgroup 2/dev/null | head -1) if [ -n $cgroup ]; then echo cgroup: $cgroup unit$(echo $cgroup | sed s/.*slice\///;s/\.service.*//) if [ -n $unit ]; then echo 对应 unit: $unit.service systemctl status $unit.service --no-pager 2/dev/null | head -15 || true fi else echo 未检测到 systemd 管理的 cgroup fi } trace_parents() { local pid$1 local cur$pid local depth0 echo echo 父进程链 echo 从当前 PID 向上追溯 while [ $cur -gt 1 ] [ $depth -lt 10 ]; do ppid$(awk /PPid:/{print $2} /proc/$cur/status 2/dev/null || echo ) cmd$(ps -p $cur -o pid,ppid,user,etime,cmd --no-headers 2/dev/null || true) if [ -z $cmd ]; then break fi echo $cmd | cut -c1-160 if [ -z $ppid ] || [ $ppid -eq $cur ]; then break fi cur$ppid depth$((depth1)) done } find_pids_by_name() { local name$1 pgrep -x $name 2/dev/null || pgrep -f $name 2/dev/null || true } main() { if [ $# -lt 1 ]; then usage fi local target if [ $1 -n ]; then target$2 else target$1 fi if [[ $target ~ ^[0-9]$ ]]; then inspect_pid $target trace_parents $target else pids$(find_pids_by_name $target) if [ -z $pids ]; then echo 没有找到进程名包含 $target 的进程 exit 1 fi echo 找到 PID: $pids for pid in $pids; do inspect_pid $pid trace_parents $pid done fi } main $4.3 核心函数作用inspect_pid核心侦查函数收集单个 PID 的完整信息。trace_parents向上递归父进程链最多追溯 10 层。find_pids_by_name支持按进程名查找优先精确匹配再走模糊匹配。main解析参数决定按 PID 还是按进程名执行。脚本刻意没有读取/proc/pid/environ因为环境变量中可能包含数据库密码、Token 等敏感内容。生产环境排查时如果确需查看请谨慎使用并做好脱敏。4.4 如何运行给脚本添加执行权限chmod x witr.sh按 PID 查看sudo ./witr.sh 31415按进程名查看./witr.sh nginx使用-n参数指定进程名sudo ./witr.sh -n sshd执行时会看到分模块的输出比一条条手动敲命令高效很多。5. 实战揪出一个可疑进程5.1 现象CPU 突然飙高某天线上服务器负载告警使用top后发现一个进程占用 CPU 很高进程名看起来像系统进程但此前没有见过。top -b -n 1 | head -20假设输出中存在PID USER PR NI VIRT RES SHR S %CPU %MEM TIME COMMAND 31415 root 20 0 250000 20000 5000 S 99.7 0.1 12:34.56 sysudp进程名sysudp看起来像一个系统工具但 CPU 占用异常需要进一步确认。5.2 使用 witr 定位来源执行sudo ./witr.sh 31415输出关键部分如下 进程基本信息 31415 1 root root S 6-02:15:22 2025-06-28 03:11:22 /tmp/.x/sysudp 可执行文件路径 /tmp/.x/sysudp 当前工作目录 /tmp 启动命令行 /tmp/.x/sysudp -c config.json 打开的网络连接 tcp ESTAB 0 0 10.0.0.5:52340 45.77.1.2:8443 users:((sysudp,pid31415,fd5)) systemd 关系 未检测到 systemd 管理的 cgroup 父进程链 31415 root 2025-06-28 03:11:22 /tmp/.x/sysudp -c config.json 1 root 2025-06-28 00:00:01 /usr/lib/systemd/systemd --switched-root --system ...看到这些信息后可以快速判断可执行文件在/tmp/.x/下不是标准系统路径。启动时带了一个自定义config.json说明不是系统原始服务。进程连接了一个外部 IP 的 8443 端口。父进程链最终到 systemdPID 1但 cgroup 中没有 systemd 管理的 unit说明它很可能是被某个程序启动后父进程退出被 systemd 收养。综合来看这是一个高度可疑的进程。5.3 结合审计日志确认入口为了确认它是怎么被拉起来的可以使用 auditd。首先确认系统安装了 auditdsystemctl status auditd如果未开启可以临时添加一条规则再触发一次进程启动来记录auditctl -a always,exit -F archb64 -S execve -k exec_log然后查询ausearch -p 31415 --start today如果审计日志显示ppid2000, 再分析 PID 2000 是什么进程往往就能找到真正的启动入口。5.4 处理建议对于可疑进程不建议在信息不足时直接kill -9因为恶意进程通常会设置守护、定时拉起的逻辑。更稳妥的流程是保留现场截图保存进程信息、网络连接、文件路径。隔离网络通过防火墙限制可疑进程访问外网。找到持久化入口检查 crontab、systemd timer、/etc/rc.local、shell 配置文件。清除恶意文件删除/tmp/.x/下的可疑文件及相关配置。修复漏洞确认入侵入口例如弱密码、未授权服务等。观察清理后继续监控是否再次出现。6. 常见问题与排查思路问题现象常见原因解决思路witr提示 PID 不存在进程已退出或输入 PID 有误先确认ps -ef或使用进程名模糊匹配看不到网络连接普通用户无法读取其他进程连接进程本身没有网络连接使用 root 运行witr或用ss -tulpen验证可执行文件路径显示“权限不足”进程属于其他用户或开启了hidepid挂载使用 root 执行确认/proc挂载参数父进程链到 PID 1 就结束了父进程已经退出进程被 systemd 收养结合审计日志、shell history、定时任务排查原启动者witr查不到 systemd unit进程并非由 systemd 直接启动或 cgroup 中无相关信息检查 crontab、rc.local、supervisor、启动脚本ausearch无输出未开启 auditd或没有添加 execve 审计规则安装并启动 auditd添加审计规则后再测试进程名正常但路径异常存在进程名伪装重点看/proc/pid/exe、cmdline和cwd另外建议排查时按下面的顺序自检用witr输出基础报告。检查/proc/pid/exe是否为已知二进制。检查cwd是否为异常目录。查看ss -tulpen是否存在外部连接。检查crontab -l、/etc/cron.d、systemd timer。查看审计日志定位原始父进程。7. 最佳实践与工程建议7.1 用 systemd 管理你的进程对于自研服务强烈建议使用 systemd unit 来管理。这样做的好处是进程来源清晰cgroup 直接显示服务单元。支持自动重启、开机自启。日志统一由journalctl管理。一个最小示例/etc/systemd/system/myapp.service[Unit] DescriptionMy App Service Afternetwork.target [Service] Userapp WorkingDirectory/opt/myapp ExecStart/usr/bin/python3 /opt/myapp/app.py Restartalways RestartSec3 [Install] WantedBymulti-user.target配置完成后systemctl daemon-reload systemctl enable --now myapp这样以后任何进程溯源都能直接通过 unit 名称找到启动方式和配置。7.2 配置进程监控和告警不要每次等 CPU 飙高后才去查进程。建议提前配置监控系统层面使用top、htop、pidstat观察资源占用。监控工具Zabbix、Prometheus Node Exporter 都能采集进程级指标。进程存活使用 systemd 的Restart策略或者外部探活脚本。例如用pidstat观察 CPU 占用pidstat -p 31415 1 57.3 收紧权限进程管理一定要遵循最小权限原则普通服务使用专用用户运行不要直接使用 root。避免将可执行文件放在/tmp目录。/tmp、/var/tmp尽量不要放业务脚本。设置合理的 umask避免创建全局可写文件。检查系统中的全局可写目录find /tmp -maxdepth 2 -type f -name *.sh -ls7.4 保留审计记录对于核心生产环境建议开启 auditd 的 execve 审计并制定日志轮转策略auditctl -a always,exit -F archb64 -S execve -k exec_log注意生产环境中审计规则需要经过测试避免产生过多日志。建议配合 logrotate 定期清理。7.5 建立进程白名单在运维规范层面可以整理一份“进程白名单”包含服务名。对应二进制路径。启动用户。启动参数。是否监听端口。负责人和部署文档。这样再遇到陌生进程对照白名单就能快速判断是否为异常。8. 总结与下一步通过本文我们从进程溯源的核心概念出发梳理了 PPID、/proc文件系统、网络连接、systemd cgroup、审计日志等关键信息并动手实现了一个名为witr的 Bash 溯源工具。以后再看到陌生进程不用再“凭感觉”处理而是可以拿着报告一步步排查。如果你想继续深入可以尝试给witr增加 JSON 输出方便接监控平台。将witr与 auditd 联动自动回溯原始父进程。在容器环境下结合/proc/pid/cgroup分析容器归属。学习bpftrace等动态追踪工具观察进程运行时行为。排查进程问题最忌讳“上来就 kill”。先搞清进程从哪里来、为什么运行再决定下一步才是稳妥的做法。希望这份实战笔记能给你带来帮助建议收藏备用。
返回列表