“Linux怎么查看进程?”这个问题看着简单,但还真不是一句“敲个 ps”就能讲完的。我自己刚接触 Linux 的时候,也以为这就是个任务管理器,打开看一眼谁在跑、谁占内存多就完事了。后来被各种诡异场景折腾过几轮——服务明明起了却找不到进程、CPU 飙到 100% 不知道谁干的、进程列表里突然冒出个 ——才发现看进程这件事,既考验命令熟练度,也考验对输出的理解深度。这篇文章我会把日常运维和开发中最常用的整套方法串一遍,包含 ps、top、pgrep、/proc 这些核心工具的用法,以及我踩过的一些坑,适合刚入门的新手抄作业,也适合老手对照查漏补缺。
1. 查看进程之前,先搞清楚你在看什么
1.1 程序、进程、线程这三者别再搞混了
很多教程上来就直接甩命令,但我建议先花两分钟把概念捋清楚,否则后面读输出会非常痛苦。
程序(Program)是静态的东西,就是磁盘上那个二进制文件或者脚本,比如/usr/bin/nginx。进程(Process)是程序被操作系统加载到内存后,正在运行的一个实例,它有自己的地址空间、文件描述符、寄存器状态等。同一个程序可以被同时运行成多个进程,比如 Nginx 启动后经常有 master 和多个 worker,每个 worker 都是一个独立进程。线程(Thread)则是进程内部更小的执行单元,同一进程的多个线程共享这块进程的内存空间,但各自有独立的栈和寄存器上下文。
在 Linux 上,很多查进程的命令默认只展示“进程”这个层级,但个别场景下线程同样需要关注。比如某个 Java 应用 CPU 飙高,你往往要找到是哪个线程在烧 CPU,这时候就得用到ps -L或者top -H,后面我会讲到。记住这个层级关系,读输出时就不会懵了。
1.2 查进程到底是为解决什么问题
我在实际工作中,查进程的需求基本可以归类成三种:
- 确认服务状态:业务部署完了,或者刚重启完机器,想知道 Nginx、MySQL、某个 Java 服务到底起来没有。这时候最常用的是
ps -ef | grep或者pgrep,看到 PID 就算基本起来了。 - 定位性能问题:机器卡、CPU 报警、内存告警,需要找出是哪几个进程在"吃"资源。这时候要用
ps aux --sort=-%cpu、top这类能给出资源占用的命令。 - 清理异常进程:遇到僵尸进程、多次启动残留的多余实例、杀不掉的进程,需要查看状态字段以及父子关系,再做针对性处理。
带着这三个目的去查进程,你会发现工具选型和输出理解都不再是死记硬背,而是有明确指向的。
2. 最常用的 ps 命令,从入门到熟练
2.1 三套参数风格,别再混着记了
ps可以说是 Linux 进程查看的第一命令,但它也是最容易把人绕晕的命令,因为它兼容了三种参数风格:
- BSD 风格:不加横杠,比如
ps aux。 - System V 风格:加横杠,比如
ps -ef。 - GNU 风格:使用双横杠长选项,比如
ps --sort=-%cpu。
我见过好多人在这里栽跟头,比如用ps aux看习惯了,突然执行ps -aux就会得到一堆奇怪的警告或者报错信息,因为-aux这个组合在严格解析下会把你想要的u、x参数拆得面目全非。虽然现代版本的 ps 做了很多兼容处理,但还是强烈建议:要显示全部进程,就固定用ps aux或ps -ef,别混搭。
那ps aux和ps -ef有什么区别呢?ps -ef是 System V 风格,输出的字段比较固定,包含 PID、PPID、CPU 使用时间、启动命令等;ps aux是 BSD 风格,输出里多出%CPU、%MEM、VSZ、RSS这些资源占用相关的列。所以在做资源分析的时候,我几乎总是用ps aux。
2.2 从头读懂 ps aux 输出的每一列
直接看一段典型输出:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND root 1 0.0 0.1 169096 14276 ? Ss Nov27 0:14 /sbin/init splash mysql 12345 1.2 5.6 1280000 450000 ? Ssl 10:30 0:32 /usr/sbin/mysqld nginx 12346 0.0 0.2 32560 16884 ? S 10:30 0:00 nginx: worker process逐列拆解:
- USER:进程属于哪个用户。这个信息很重要,排查安全问题时尤其关键,比如你能一眼看出有没有异常用户启动的进程。
- PID:进程号,是定位进程的唯一标识。
- %CPU:进程占用的 CPU 百分比。注意这个值是累计平均值,并不是瞬时值,它是进程占用 CPU 的时间除以进程存活时间的比例。所以一个刚启动就满负荷运行的进程,这个值会很快接近 100,而一个跑了一段空闲时间的进程,即使此刻突然忙起来,这个值短期也可能不高。
- %MEM:进程占用物理内存的百分比,计算公式是 RSS 除以总内存。
- VSZ(Virtual Size):虚拟内存大小,包括没在物理内存中的部分,比如映射的共享库、文件映射等。这个数字通常很大,别被它吓到。
- RSS(Resident Set Size):实际驻留在物理内存中的大小,单位是 KB。排查内存占用时主要看 RSS,但也要注意共享内存,比如多个进程共享同一个动态库,这个库只会在物理内存里存一份,但每个进程的 RSS 都把它算进去了,所以多个进程 RSS 相加可能超过实际总内存,这是正常现象。
- TTY:进程关联的终端。
?表示没有终端,一般是后台服务进程。 - STAT:进程状态,下一章我会单独展开说明。
- START / TIME:进程启动时间,以及进程累计占用 CPU 的时间。注意 TIME 不是进程已经运行了多久,而是它消耗了多少 CPU 时间。如果一个进程启动了一个月但 TIME 只有 0:01,说明它几乎一直在睡觉。
- COMMAND:启动这个进程的命令行。
2.3 几个我天天在用的 ps 组合
光会ps aux只能及格,真正要高效,得把这些组合记下来:
按关键词找进程,且过滤掉 grep 自身:
ps -ef | grep nginx | grep -v grep这个grep -v grep是为了过滤掉那条grep nginx的临时进程,新手最容易漏掉。不过更推荐直接用pgrep,后面会讲。
按 CPU 或内存倒序看前几名:
ps aux --sort=-%cpu | head -n 10 ps aux --sort=-%mem | head -n 10--sort=-表示按某列倒序,排在最前面的就是当前最吃资源的进程,这是我排查性能问题第一分钟必敲的命令。
只看某个具体进程名的 PID:
ps -C nginx -o pid,cmd-C参数直接按进程名精确筛选,配合-o指定输出字段,比ps aux | grep干净很多。
同时查看线程信息:
ps -L -p 12345这个在看 Java 或 Python 多线程进程的时候用得上,会列出进程内每个线程的 LWP(轻量级进程号)。
3. 动态监控用 top 和 htop,别再刷新不清了
ps只能给一个快照,资源和状态是不断变化的,这时候就得靠实时监控工具。top是系统自带的,htop 则是增强版,我在服务器上通常两个都会装,有需要就交互式查。
3.1 top 的基本操作,很多老手也不一定全知道
运行top之后,默认每 3 秒刷新一次。顶部那几行信息非常关键:
- load average:1、5、15 分钟的平均负载,这是系统整体繁忙程度的指标,数值大于 CPU 核数就要警惕了。比如 8 核机器 load 到了 15,说明有大量任务在排队等待 CPU。
- %Cpu(s):us(用户态)、sy(内核态)、wa(等待 IO)、id(空闲)等,如果你看到 wa 很高,说明磁盘 IO 是瓶颈,这不是单纯加 CPU 能解决的。
- KiB Mem / KiB Swap:内存和交换分区的使用情况,如果 swap 一直被大量使用,通常意味着物理内存不够了。
日常操作记住几个键:
P(大写):按 CPU 使用率排序。M(大写):按内存使用量排序。1(数字 1):展开/折叠每个 CPU 核心的使用率。u:输入用户名,只查看该用户的进程。k:输入 PID 和信号,可以终止进程,默认是发 SIGTERM(15),杀不掉再换 9。r:调整进程的 nice 值(优先级)。H:切换线程视图,配合-p指定 PID 时非常有用。
只看某个 PID 的动态信息:
top -p 12345如果还要看它内部的线程:
top -H -p 12345这两条命令是我定位 Java 线程死循环时的必备武器。
3.2 htop 交互体验好,适合在本地终端用
htop 在 Ubuntu/Debian 上可以用apt install htop安装,CentOS/RHEL 系列用yum install htop,它比 top 更直观:
- 顶部有 CPU、内存、Swap 的彩色进度条,一眼看出饱和度。
- 操作全是功能键:F5 切换进程树,F6 选择排序字段,F9 发送信号,F4 按名称过滤。
- 支持鼠标点击,虽然我极少用鼠标,但给带新人的时候确实友好。
htop 唯一的缺点是比较吃终端特性,如果通过某些精简终端连接,界面可能会渲染异常。所以我在生产服务器上还是习惯用 top,本机调试才用 htop。
3.3 批处理模式,脚本采集不再愁
top通常被认为是交互式工具,但它其实也有批处理模式:
top -b -n 1 -p 12345-b表示批处理模式,不进入交互界面;-n 1表示只取一次快照;-p指定 PID。这个输出可以直接重定向到日志里。我自己写监控脚本的时候,经常这么采集指定进程的 CPU 使用情况,然后用grep提取关键字。如果要对多进程做统一监控,还可以配合-p PID1,PID2一次指定多个 PID。
4. 按名字找进程,pgrep、pidof、pkill 组合拳
ps aux | grep 关键词虽然好用,但有个致命缺点:它匹配的是整条命令行,拿到的是进程本身和 grep 临时进程混杂的结果,而且你还得手动收尾清理中间管道。所以我更推荐用专为“按名查找”设计的工具。
4.1 pgrep 的用法
pgrep就是 process grep,最常用的是-f参数,表示匹配完整命令行:
pgrep -f nginx只输出 PID。如果看不到进程名对应哪个命令,可以加-a:
pgrep -af nginx 12222 nginx: master process /usr/sbin/nginx 12223 nginx: worker process-x可以精确匹配进程名,比如pgrep -x nginx只匹配进程名严格为 nginx 的,不会匹配 nginx_foo。
按用户筛选也很常用:
pgrep -u root -a列出 root 用户启动的所有进程,这个在安全审计时非常有用。
需要注意pgrep的-f是把命令行参数也算进去匹配的,所以如果只是进程名匹配,别带-f,否则可能误伤。反过来,如果你要启动脚本里写了很长的完整路径,用-f才找得到。
4.2 pidof 的适用场景
pidof是另一个查找 PID 的小工具,它只按程序名匹配,不匹配参数,而且会返回一个程序的所有实例 PID:
pidof nginx 12222 12223 12224 12225我一般这样用:先把 PID 抓出来,再传给其他命令:
kill $(pidof nginx)或者查看所有实例的详细信息:
ps -p $(pidof nginx)这个写法比写循环方便太多了,唯一的坑是如果程序没运行,pidof会静默返回空值,导致后面的命令收到空参数,最好在脚本里加上判断。
4.3 pkill、kill、killall 的正确姿势
pkill是 pgrep 的姊妹命令,用法上基本是pkill 进程名就能发 SIGTERM 信号让进程正常退出,适合用于优雅关闭:
pkill -f "python app.py"如果进程不响应,再加-9强制杀掉:
pkill -9 -f "python app.py"killall同样是按进程名批量杀,比如killall nginx。但我个人更喜欢用 pkill,因为它能用-f做完整命令行匹配,更精准。
注意:
pkill -9不要在业务高峰期随意使用,强制杀进程不会给程序清理临时文件和释放资源的机会,可能导致数据损坏。尤其是数据库进程,宁可多等一会儿用优雅停机,也别上来就-9。
5. 进程状态和 /proc 目录下的秘密
5.1 STAT 状态字段:这个进程到底在干嘛
ps aux输出里的 STAT 一列,是最容易被忽略但信息量最大的字段。常见状态码:
| 状态码 | 含义 | 常见场景 |
|---|---|---|
| R | Running,正在运行或可运行 | 正在消耗 CPU |
| S | Sleeping,可中断睡眠 | 等待事件,最常见 |
| D | 不可中断睡眠 | 通常在等待磁盘 IO,难以被杀掉 |
| Z | Zombie,僵尸进程 | 子进程已退出但父进程未回收 |
| T | Stopped,被暂停 | 被 Ctrl+Z 或 SIGSTOP 挂起 |
| I | Idle,内核空闲线程 | 内核线程,一般没问题 |
此外还有辅助字符需要注意:小于号s表示包含子进程的会话首进程,比如 init;小于号<表示高优先级(nice 值为负);大写N表示低优先级(nice 值为正);大写l表示是多线程进程;加号+表示进程位于前台进程组,占用了终端,比如你正在前台运行的 vim 或 top。
看到Z怎么办?僵尸进程本身不再消耗 CPU 和内存,但它会一直占着一个 PID 条目。解决办法是找它的父进程,让父进程回收子进程的状态,如果父进程僵死无法回收,那就只能处理父进程了。通常我先把 PPID 看亮,然后判断父进程是否能正常结束,再决定是否kill -9父进程。系统重启是清理僵尸最有效的终极手段,但日常工作不必做到这一步。
遇到D状态的进程杀不掉?这个状态通常是进程在内核态等待某个不可中断的 IO 操作,比如等待磁盘写入完成,可能源于磁盘故障、网络存储卡住等情况。kill -9对它无效,因为内核根本不会处理这个信号。此时正确做法是排查底层存储和 IO,而不是死磕进程。
5.2 所有进程信息都在 /proc 里
Linux 有一个神奇的伪文件系统/proc,里面每个数字目录就是一个运行中进程的 PID。比如/proc/1就是 init/systemd 进程。
我常用这几个文件:
cat /proc/12345/cmdline cat /proc/12345/status ls -l /proc/12345/fd | headcmdline:进程启动时的完整命令行,参数之间用\0分隔,所以直接cat可能看不到分号,用tr '\0' ' '替换一下更方便。status:包含进程状态、PPID、线程数、内存信息等关键字段。fd目录:进程打开的文件描述符列表,排查文件句柄泄漏必备。cwd软链接:指向进程的当前工作目录,有时候查不清程序跟着哪个配置文件时很有用。
有一个技巧:如果ps显示进程名是方括号加内核线程名,比如[kworker],这种通常不是普通用户进程,而是内核线程,不用太在意。
6. 几段真实的排查记录,浓缩的避坑心得
6.1 服务起来了,但 ps 里找不到进程
有次同事反馈“MySQL 明明配置了开机自启,ps 却查不到"。我到现场一看,执行的是:
ps -ef | grep mysql确实没有输出。但ss -tlnp查看端口,3306 是有监听进程的。后来发现是ps命令输出的 COMMAND 字段被截断了,进程名实际是mariadbd,和 MySQL 的二进制名不一样。所以排查服务是否起来,除了按进程名找,还要结合端口监听:
ss -tlnp | grep 3306如果查到端口对应着一个 PID,再用ps -p PID确认就非常准确。我之前写过一个小检查脚本,同时查进程名、端口、socket 文件,三个条件命中两个就判定服务正常,比单靠一个条件可靠得多。
6.2 在容器和 namespace 里看到的进程缺了一大半
现在很多服务跑在 Docker 容器里,在容器内执行ps -ef经常只看到少数几个进程,这是因为 Linux 的 PID namespace 隔离机制。容器内的 PID 1 并不是宿主机上的 PID 1,而是容器入口进程,所以容器里看不到其他外部进程是正常的。想了解宿主机上所有进程,只能从宿主机视角来看。
这个机制还带来了另一个问题:容器内进程 PID 和宿主机 PID 不同,容器内看到的 PID 1 在宿主上可能是 5000 多。所以在排查跨容器问题时,建议用docker top 容器名查看容器进程在宿主机的真实 PID,或者直接到宿主机上查。
6.3 关于输出过滤的几个坑
用ps aux | grep的时候,有两个常见坑我在这里特别提醒:
- 如果你正在用
pgrep -f搜索含有特殊字符的命令,比如括号或引号,最好用单引号包住整个模式,否则 shell 可能帮你把特殊字符吃掉,结果匹配到一堆无关进程。 - 用
ps --sort做排序时,如果没加head限制行数,输出可能刷屏,建议始终配合head -n使用。
此外还有个非常容易被忽略的点:并不是所有运行中的任务都会出现在 ps 列表里。用户自己使用命令执行的短暂任务,如果执行得太快,ps 可能抓不到。所以有时候你想监控一条秒级完成的任务,需要用循环采样:
while true; do ps aux --sort=-%cpu | head -n 5; sleep 0.5; done这会让几十个短暂的进程也无法逃出你的眼睛。
查进程这件事,说到底就是一套"查找 + 过滤 + 理解状态"的组合拳,命令就那几个,关键是要在正确的场景选对工具。我个人的习惯是:快速确认是否存在用pgrep,看整体资源用ps aux --sort,实时监控用top,深挖单个进程就看/proc/<pid>。把这个套路固化下来,遇到任何“进程不见了”“CPU 满了”“僵尸出来了”的情况,你都能在几分钟内摸清状况。希望这篇多年实战总结能帮你少走点弯路。