欢迎光临
我们一直在努力

Day 09 Linux进程管理实战

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

# 找到占用进程后重启它,空间才会真正释放


十、总结

这一期从概念到实操走完了进程管理的核心链路:

  • 三概念:程序是静态文件,进程是执行实例(资源分配单位),线程是进程内的执行流(CPU 调度单位)。
  • 进程树:PID 定位进程,PPID 指认父亲,根是 systemd(PID 1);孤儿被收养,僵尸要找父进程算账。
  • 查询:静态用 ps aux(看资源)/ pgrep -a(按名找人)/ pidof(脚本取号),动态用 top(P/M 排序、1 看核、k 发信号)。
  • 信号:kill 是发信号不是只杀人——先 TERM(15)礼貌请退,HUP(1)重读配置,KILL(9)永远是最后手段。
  • 作业控制:& 后台、Ctrl+Z 暂停、bg/fg/jobs 调度;长期任务用 nohup、tmux 或 systemd 托管。
  • 负载判读:uptime 三个数除以 nproc 的核数,>1 开始排队;load 高 CPU 低,去查 IO。
  • 内存:看 available 不看 free;vmstat 1 的 si/so 持续非零才是内存真不够的实锤。
  • /proc:所有监控工具的数据源;fd 目录专治"文件删了空间没释放"。
  • 到这里,我们已经能看清"单个进程"的一切。但服务器上跑着几十上百个服务,怎么让 nginx 开机自启、挂了自动拉起、统一管理日志? 这就不能只靠手动 kill 和 nohup 了——下一期我们正式进入 systemd 服务管理:unit 文件编写、systemctl 全家桶、开机自启与崩溃自愈。敬请期待。


    练习环境:Ubuntu 24.04 LTS 虚拟机(命令在 Rocky Linux 9 上同样适用)

    赞(0)
    未经允许不得转载:171主机测评 » Day 09 Linux进程管理实战
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址