Linux 进程管理实战:从概念到 /proc 透视的完整指南
上一期我们讲了软件包的交付,解决了"软件怎么装上来"的问题。软件装好了,接下来自然是:怎么让它跑起来、看清它跑得怎么样、出问题怎么收拾它——这就是进程管理。
本文以 Ubuntu 24.04(同样适用于 Rocky 9)为演示环境,覆盖:程序/进程/线程的概念辨析、PID 与进程树、ps/top 两大查看神器、信号与作业控制、CPU 负载与内存的判读,最后深入到 /proc 文件系统这个"进程透视窗"。
文章目录
- Linux 进程管理实战:从概念到 /proc 透视的完整指南
-
- 一、程序、进程、线程:三个必须分清的概念
- 二、PID、PPID 与进程树
-
- 2.1 PID 是什么
- 2.2 用 pstree 看进程树
- 2.3 两个衍生命题:孤儿与僵尸
- 2.4 PID 的数量上限
- 三、静态查询三剑客:ps / pgrep / pidof
-
- 3.1 ps:进程查询的瑞士军刀
- 3.2 pgrep:按名字找人
- 3.3 pidof:只要 PID,别的不要
- 四、top:动态监控的驾驶舱
-
- 4.1 顶部五行信息区逐行读
- 4.2 交互按键(top 启动后按)
- 4.3 脚本里用 top
- 五、信号:和进程"说话"的方式
-
- 5.1 信号是什么
- 5.2 正确用法演示
- 5.3 批量发信号
- 六、前台、后台与暂停:作业控制
-
- 6.1 四个基本操作
- 6.2 关终端不掉线:nohup 与 disown
- 七、CPU 负载:nproc 与 uptime
-
- 7.1 先知道有几个核
- 7.2 load average 到底怎么看
- 八、内存与 Swap:free 和 vmstat
-
- 8.1 free -h:先看对列
- 8.2 Swap:最后的缓冲垫
- 8.3 vmstat:一个命令看全局
- 九、/proc:进程的透视窗
-
- 9.1 /proc 是什么
- 9.2 进程级目录里最常用的几个
- 9.3 系统级的三个常用文件
- 9.4 删了文件空间没释放?/proc 破案
- 十、总结
一、程序、进程、线程:三个必须分清的概念
面试常考,干活常用,但很多人工作几年还是糊的。
程序(Program):躺在磁盘上的一个静态文件。比如 /usr/sbin/nginx,你不运行它,它就是一堆字节,不占 CPU、不占内存(只占磁盘)。
进程(Process):程序的一次执行实例。当你启动 nginx,内核把程序加载进内存、分配资源、开始执行——这个"活起来的实例"就是进程。同一个程序可以同时起多个进程(比如你可以开三个 vim 编辑三个文件),它们互相独立。
线程(Thread):进程内部的执行流。一个进程至少有一个线程(主线程),也可以有多个线程并发干活。同一进程内的线程共享内存空间,这是线程和进程最本质的区别。
打个生活化的比方:
程序 = 菜谱(纸上的静态文字)
进程 = 按菜谱做菜的整个过程(开火、备料、占用厨房)
线程 = 厨房里同时干活的多个人(一个切菜、一个看火,共用同一个厨房)
两个硬核知识点,记住就够用了:
- 进程是资源分配的最小单位(内存、文件句柄按进程分配)
- 线程是 CPU 调度的最小单位(CPU 核心上真正跑来跑去的是线程)
二、PID、PPID 与进程树
2.1 PID 是什么
每个进程一出生,内核就给它发一个身份证号——PID(Process ID)。所有进程操作(查看、发信号、调优先级)都靠 PID 定位。
PPID(Parent PID) 是这个进程的"爹"的编号。Linux 的进程不是一盘散沙,而是一棵树:每个进程都有父亲,树的根是 PID 1(现代发行版上是 systemd)。
# 一眼看清:PID 1 是谁
ps -p 1 -o pid,ppid,comm
# PID PPID COMMAND
# 1 0 systemd
2.2 用 pstree 看进程树
# 带 PID 显示整棵进程树
pstree -p
# 只看某个进程的子树(比如 nginx 的 master 和 workers)
pstree -p $(pidof nginx | awk '{print $1}')
nginx 的典型结构就是一个 master 进程带几个 worker 子进程,在 pstree 里一目了然:
nginx(1000)───┬──nginx(1001)
└──nginx(1002)
2.3 两个衍生命题:孤儿与僵尸
- 孤儿进程:爹先死了,孩子还在跑。不用担心——内核会让 PID 1(systemd)收养它,负责善后。
- 僵尸进程(Zombie):孩子死了,爹却不去"收尸"(不调用 wait 读退出状态),子进程的尸体( task_struct 残留)就留在进程表里,状态显示为 Z。僵尸不占 CPU 和内存,但占 PID 名额,大量僵尸说明父进程代码有 bug——解法不是 kill 僵尸(它已经死了,杀不动),而是处理它的父进程。
2.4 PID 的数量上限
cat /proc/sys/kernel/pid_max
# 默认通常是 4194304
PID 是有限资源。这也是为什么僵尸进程多了会出事——PID 耗尽,新进程就 fork 不出来了。
三、静态查询三剑客:ps / pgrep / pidof
3.1 ps:进程查询的瑞士军刀
两种经典风格,输出各有拥趸,建议都认识:
ps aux # BSD 风格(无短横线),最常用
ps -ef # SysV 风格,能直接看到 PPID 列
ps aux 的输出逐列拆解:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1000 0.1 0.5 71256 10520 ? Ss 10:00 0:01 nginx: master
- %CPU / %MEM:CPU、内存占用百分比
- VSZ:虚拟内存总量(申请的,虚的);RSS:实际常驻物理内存(真实的,看它)
- STAT:进程状态码,重点记这几个:
- R 运行中/排队运行
- S 可中断睡眠(大多数进程的正常状态,在等事件)
- D 不可中断睡眠(通常在等磁盘 IO,load 飙升的常客)
- T 停止(被 Ctrl+Z 或 SIGSTOP 暂停)
- Z 僵尸
- 附加字符:s 会话首领、< 高优先级、+ 前台进程
实战组合拳:
# 按 CPU 占用排序,取前 10
ps aux –sort=-%cpu | head
# 按内存占用排序
ps aux –sort=-%mem | head
# 只看指定用户的进程
ps -u www-data
3.2 pgrep:按名字找人
ps aux | grep nginx 再用 awk 取 PID 是老写法,pgrep 一步到位:
# 找出 nginx 的所有 PID
pgrep nginx
# 连进程名一起显示(推荐养成加 -a 的习惯)
pgrep -a nginx
# 1000 nginx: master process
# 1001 nginx: worker process
# 只看某个用户的进程
pgrep -u www-data -a
3.3 pidof:只要 PID,别的不要
pidof nginx
# 1001 1000
输出最干净,适合写脚本:
# 脚本里判断进程是否在跑
if pidof nginx > /dev/null; then
echo "nginx is running"
fi
三者怎么选:要看详细资源占用用 ps,要按名字拿 PID 并带点信息用 pgrep -a,脚本里纯取 PID 用 pidof。
四、top:动态监控的驾驶舱
静态查询是照片,top 是直播:
top
4.1 顶部五行信息区逐行读
top – 21:00:01 up 2 days, 3:15, 2 users, load average: 0.52, 0.38, 0.29
Tasks: 218 total, 1 running, 217 sleeping, 0 stopped, 0 zombie
%Cpu(s): 3.1 us, 1.2 sy, 0.0 ni, 95.2 id, 0.4 wa, 0.0 hi, 0.1 si, 0.0 st
MiB Mem : 7973.2 total, 4210.5 free, 1865.4 used, 1897.3 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 5800.1 avail Mem
-
load average:1/5/15 分钟平均负载(下一章专门讲怎么判读)
-
Tasks:重点盯 zombie 不为 0
-
%Cpu(s) 各缩写:
- us 用户态(应用程序干活)
- sy 内核态(系统调用)
- id 空闲
- wa 等 IO(wa 持续高 = 磁盘瓶颈信号)
- st 被虚拟化宿主机偷走的时间(虚拟机里才有,st 高说明宿主机超卖严重)
-
Mem:重点看最后的 avail Mem(可用内存),不是 free! 原因: 应用程序读写磁盘时,并不会每次都直接操作磁盘——磁盘比内存慢几个数量级。 为了加速,内核会在内存中划出两块区域做"中转站":
-
Cache(页缓存):缓存读操作。程序读过的文件内容会留在内存里, 下次再读同一个文件,直接从内存返回,不再访问磁盘。
-
Buffer(缓冲区):缓冲写操作。程序要写磁盘的数据先暂存在内存里, 由内核攒批后统一刷盘,减少磁盘 IO 次数。
通俗一点的来讲就是:应用程序读写磁盘时,为了加快读写,会在内存中输入缓存或者输出缓存,我们称为cache和buff 缓存也会占用内存
这也是 free -h 里要看 available 而不是 free 的原因—— available 已经把可回收的 buff/cache 算进去了, 它才是"应用程序真正还能用多少内存"的答案。
4.2 交互按键(top 启动后按)
| P | 按 CPU 排序(默认) |
| M | 按内存排序 |
| 1 | 展开/收起每个 CPU 核心的详情 |
| H | 切换线程视图 |
| k | 给某进程发信号(输入 PID,默认发 15) |
| r | 调整进程优先级(renice) |
| c | 显示完整命令行 |
| q | 退出 |
4.3 脚本里用 top
top 默认是交互式全屏,写脚本采集数据要加批处理模式:
# 只采样一次后退出,输出到文本
top -b -n 1 > /tmp/top_snapshot.txt
如果机器上装了 htop(sudo apt install htop),体验更好:彩色、鼠标可点、树形视图。但服务器最小化安装环境往往只有 top,所以 top 必须会。
五、信号:和进程"说话"的方式
5.1 信号是什么
信号是内核/进程之间传递的异步通知:“请你退出”、“请你重读配置”、“终端断了”……kill 命令的中文名误导了无数人——kill 的本职是"发信号",默认发的是"请你退出",强杀只是其中一种用法。
# 列出全部 64 种信号
kill -l
64 种里日常只需要记三个:
| STERM | 15 | 礼貌地请进程退出(默认) | “收拾东西,正常下班” |
| KILL | 9 | 内核直接杀死,进程无法拦截 | “保安架出去” |
| HUP | 1 | 终端挂断;对守护进程约定俗成 = 重读配置 | “电话断了” / “重新看一遍规则” |
5.2 正确用法演示
# 第一步永远是礼貌请求(不带 -9!)
kill 1000
# 等价于
kill -TERM 1000
# 等几秒,进程还在?再升级手段
kill -9 1000
# 让 nginx 平滑重读配置文件(不中断服务!)
kill -HUP $(pgrep -o nginx)
# 等价于 nginx -s reload
为什么 -9 要放到最后:TERM 会给进程机会执行清理动作(落盘数据、关闭连接、删除锁文件、通知子进程);KILL 是内核直接回收,进程一句话都来不及说——数据没写完、临时文件残留、锁没释放,都是这么来的。kill -9 是最后手段,不是第一选择。
5.3 批量发信号
# 按名字杀(killall 按精确进程名)
killall nginx
# pkill 更灵活,可按用户、终端等条件
pkill -u testuser # 踢掉某用户的所有进程
pkill -f "python app.py" # 按完整命令行匹配
pkill -f 匹配整个命令行,方便但危险——模式写宽了可能误杀,动手前先 pgrep -af "模式" 看看会命中谁。
六、前台、后台与暂停:作业控制
在终端里跑的命令默认是前台任务:霸占终端,你得等它跑完。作业控制(Job Control)让你在同一终端里调度多个任务。
6.1 四个基本操作
# 1. 启动时就放后台:命令后加 &
sleep 300 &
# 2. 前台任务暂停并扔回后台:先 Ctrl+Z,再 bg
sleep 300
# 按 Ctrl+Z → [1]+ Stopped sleep 300
bg %1
# 3. 查看当前终端的所有作业
jobs
# [1]- Running sleep 300 &
# [2]+ Stopped vim notes.txt
# 4. 拉回前台继续操作
fg %2
记一组对照键位:
| Ctrl+C | SIGINT | 中断(请求退出) |
| Ctrl+Z | SIGTSTP | 暂停(扔到后台停着) |
| Ctrl+\\ | SIGQUIT | 退出并产生 core dump |
6.2 关终端不掉线:nohup 与 disown
后台任务有个坑:终端一关,HUP 会发给这个终端的所有作业,后台任务跟着死。两种解法:
# 方法一:nohup 免疫 HUP,输出重定向到 nohup.out
nohup ./long_task.sh > task.log 2>&1 &
# 方法二:已在跑的任务,用 disown 把它从作业表移除
sleep 300 &
disown %1
生产环境更推荐用 systemd 服务或 tmux/screen 会话来托管长任务(tmux 里 Ctrl+B 再按 D 脱离会话,任务照跑,tmux attach 随时回来)——终端作业控制适合临时场景,不适合长期驻留。
七、CPU 负载:nproc 与 uptime
7.1 先知道有几个核
nproc
# 4
# 详细版
lscpu | grep "^CPU(s):"
7.2 load average 到底怎么看
uptime
# 21:05:12 up 2 days, 3:20, 2 users, load average: 0.52, 0.38, 0.29
三个数分别是 1 分钟、5 分钟、15 分钟的平均负载。负载的准确含义是:处于"运行中(R)+ 不可中断睡眠(D)"状态的进程平均数。
判读规则一句话:负载 ÷ 核数。
- 4 核机器,load = 4.0 → 每个核刚好满负荷,没有排队
- 4 核机器,load = 8.0 → 平均有 4 个进程在排队等 CPU,开始拥堵
- 长期负载/核数 > 1 → 该扩容或优化了
三个数结合看趋势:
load: 8.0, 4.0, 2.0 ← 负载在飙升(1分钟远高于15分钟),赶紧查
load: 2.0, 4.0, 8.0 ← 高峰已过,正在回落
高频误区:负载高 ≠ CPU 忙。load 把 **D 状态(等磁盘 IO)**也算进去了。遇到"load 很高但 us+sy 很低",别盯 CPU 了,去看 wa(IO 等待)和磁盘——这就是 vmstat 要干的事。
八、内存与 Swap:free 和 vmstat
8.1 free -h:先看对列
free -h
# total used free shared buff/cache available
# Mem: 7.8Gi 1.8Gi 4.1Gi 12Mi 1.9Gi 5.7Gi
# Swap: 2.0Gi 0Bi 2.0Gi
新手最常犯的错:看到 free 只剩很少就惊呼"内存爆了"。错! Linux 会把空闲内存拿去做文件缓存(buff/cache),加速读写,需要时立刻释放。判断内存够不够,看的是 available(可回收后真正可用的量),不是 free。
8.2 Swap:最后的缓冲垫
内存真不够时,内核把不活跃的内存页换到磁盘上的 Swap 区——机器不会立刻 OOM(out of memory),但会明显变慢(磁盘比内存慢几个数量级)。
# 查看 swap 使用情况
swapon –show
8.3 vmstat:一个命令看全局
# 每 1 秒采样一次,共 5 次
vmstat 1 5
procs ———–memory———- —swap– —–io—- -system– ——cpu—–
r b swpd free buff cache si so bi bo in cs us sy id wa st
2 0 0 4313084 180520 1764580 0 0 12 28 210 380 3 1 95 0 0
按列分组记:
- procs:r 排队等 CPU 的进程数(持续 > 核数 = CPU 不够);b 等 IO 的进程数(持续 > 0 = 磁盘瓶颈)
- swap:si(swap in)/ so(swap out)持续大于 0 = 内存真的不够了,在频繁换页,这是内存不足的实锤
- io:bi 读磁盘 / bo 写磁盘(块/秒)
- cpu:us 用户、sy 内核、id 空闲、wa 等 IO、st 虚拟化被偷——和 top 里的含义一致
一个 vmstat 1 就能把"CPU 忙不忙、内存缺不缺、磁盘卡不卡"三件事同时盯起来,是性能排查的第一站。
九、/proc:进程的透视窗
9.1 /proc 是什么
/proc 是一个伪文件系统(procfs):不占磁盘,里面的"文件"是内核实时生成的数据视图。ps、top、free 这些命令的数据,全部来自读 /proc。学会直接读 /proc,你就在用和这些工具相同的一手数据。
# 每个运行中的进程都有一个以 PID 命名的目录
ls /proc/1000/
# cmdline environ fd limits status …
9.2 进程级目录里最常用的几个
PID=1000
# 进程的完整启动命令(参数用 \\0 分隔,tr 转换一下)
cat /proc/$PID/cmdline | tr '\\0' ' '; echo
# nginx: master process /usr/sbin/nginx
# 状态汇总:状态、PPID、内存、线程数(比 ps 更细)
cat /proc/$PID/status | head -20
# 进程打开的所有文件/网络连接(fd = 文件描述符)
ls -l /proc/$PID/fd
# 资源限制(能看到"打开文件数上限"这类关键限制)
cat /proc/$PID/limits
# 进程的工作目录
ls -l /proc/$PID/cwd
fd 目录实战价值最高:进程报"Too many open files"、文件删了但磁盘空间没释放(文件还被进程占着),都是靠它定位的。
9.3 系统级的三个常用文件
cat /proc/cpuinfo # CPU 型号、核数、主频
cat /proc/meminfo # 内存全量数据(free 的数据源)
cat /proc/loadavg # 负载(uptime 的数据源)
9.4 删了文件空间没释放?/proc 破案
经典故障:rm 删了一个大日志文件,df -h 一看空间没回来。原因是文件还被某进程打开着,磁盘块要等最后一个引用关闭才释放:
# 找出所有"已删除但仍被占用"的文件
ls -l /proc/*/fd 2>/dev/null | grep deleted
# 找到占用进程后重启它,空间才会真正释放
十、总结
这一期从概念到实操走完了进程管理的核心链路:
到这里,我们已经能看清"单个进程"的一切。但服务器上跑着几十上百个服务,怎么让 nginx 开机自启、挂了自动拉起、统一管理日志? 这就不能只靠手动 kill 和 nohup 了——下一期我们正式进入 systemd 服务管理:unit 文件编写、systemctl 全家桶、开机自启与崩溃自愈。敬请期待。
练习环境:Ubuntu 24.04 LTS 虚拟机(命令在 Rocky Linux 9 上同样适用)


