线上排障时,最关键的能力不是"会背命令",而是能把现象快速映射到资源与进程:
CPU 是不是被打满、内存是不是被 cache 吃掉、磁盘 I/O
在不在等、到底是哪一个进程/文件/端口在拖慢系统。本文从进程/线程与父子关系的基本概念出发,解释
Linux 的资源视角(尤其是 buffer/cache
的含义与"内存不够用"的误判),然后系统梳理一套常用监控与定位工具链(top/htop/ps/pstree/lsof、端口/网络、
I/O
、负载与压力测试)。接着补齐"把进程控住"的操作:信号与后台任务、nice/renice
优先级、孤儿/僵尸进程的成因与处理;最后用一个完整的排障案例( Nginx
日志文件被误删怎么办)把"资源视角"落到实战层面,方便你把一次完整排障流程跑通。如果你是运维人员或需要排查性能问题,这篇文章会让你从"会看
top"升级到"能快速定位资源瓶颈、能优化进程优先级、能处理异常进程状态"。
进程与程序的基本概念
进程 vs 程序 vs 线程
这三个概念经常混淆,但理解它们的区别对理解 Linux 系统很重要:
| 概念 | 定义 | 比喻 |
|---|---|---|
| 程序 | 存储在硬盘上的静态可执行文件(如 /usr/bin/vim) |
建筑设计图纸 |
| 进程 | 程序加载到内存后运行的实例(有 PID 、内存空间、打开的文件) | 正在施工的建筑工地(包工头) |
| 线程 | 进程内的执行单元(共享进程的内存空间,但有独立的执行流程) | 工地上干活的工人 |
示例:
- 当你运行
vim myfile.txt时,vim程序从硬盘加载到内存,产生一个进程,该进程负责编辑myfile.txt。 - 同一个程序可以同时启动多个进程,例如浏览器中打开多个标签页时,每个标签页可能对应一个独立的进程(或多个线程)。
- 一个进程可以包含多个线程,例如网易云音乐进程可能包含两个线程:一个下载音乐,一个播放音乐。
为什么要有线程?
- 线程比进程更轻量(创建和销毁开销小)
- 线程共享进程的内存空间(通信方便)
- 多线程可以充分利用多核 CPU(并行计算)
进程的五大特性
- 独立性:每个进程都有自己的内存空间和系统资源,彼此隔离(进程 A 的变量不会影响进程 B)
- 并发性:操作系统允许多个进程同时运行,通过多任务调度实现并发处理
- 动态性:进程不断创建、执行、终止,状态实时变化(操作系统的运行就是不断创建和销毁进程)
- 父子关系:进程由父进程通过
fork()调用创建,形成父子结构( PPID 字段指明父进程) - 调度性:操作系统使用调度算法(如时间片轮转、优先级调度)决定进程执行顺序
进程的父子关系( PID 和 PPID)
每个进程都有两个重要的 ID:
- PID( Process ID):进程 ID,唯一标识一个进程
- PPID( Parent Process ID):父进程 ID,标识创建这个进程的父进程
示例: 1
ps -ef | grep bash
输出示例: 1
2
3UID PID PPID C STIME TTY TIME CMD
root 1234 1 0 12:00 ? 00:00:00 /bin/bash /usr/local/bin/startup.sh
user 5678 1234 0 12:05 pts/0 00:00:00 bash
- PID 5678 的进程是
bash,它的 PPID 是 1234(父进程是/bin/bash /usr/local/bin/startup.sh) - 所有进程最终都能追溯到 PID 1( systemd 或 init)
查看进程树: 1
pstree -p # 以树状结构显示进程父子关系(-p 显示 PID)
Linux 资源管理概述: CPU/内存/磁盘/网络
运维工作围绕硬件和软件资源展开,合理管理这些资源可确保系统高效稳定运行。
硬件资源的四大类
1. CPU 资源
- 核心数:现代 CPU 通常是多核(如 4 核、 8 核、 16 核)
- 负载:等待执行的进程数( load average)
- 占用率:进程对 CPU 的占用百分比
查看 CPU 核心数: 1
2lscpu | grep '^CPU(s)' # 输出: CPU(s): 4
nproc # 输出核心数: 4
查看 CPU 负载: 1
uptime # 输出: 06:56:12 up 12 days, 3:45, 3 users, load average: 0.22, 0.45, 0.56
load average 解读(以 4 核 CPU 为例):
load average: 0.22, 0.45, 0.56:分别是 1 分钟、 5 分钟、 15 分钟的平均负载- 负载值 < 核心数(如 4 核时 load < 4):系统空闲
- 负载值 = 核心数(如 4 核时 load = 4):系统满载
- 负载值 > 核心数(如 4 核时 load > 4):系统过载(有进程在等待 CPU)
负载高但 CPU 使用率低?这通常表示进程在等待 I/O(磁盘读写、网络等),不是 CPU 瓶颈。
2. 内存资源
- 总内存( Total):物理内存总量
- 已用内存( Used):已分配的内存
- 可用内存( Available):实际可用的内存(包括可回收的 buffer/cache)
- Swap:交换空间(硬盘上的虚拟内存,速度慢)
查看内存使用: 1
free -h # -h 人性化显示( MB/GB)
输出示例: 1
2
3 total used free shared buff/cache available
Mem: 15Gi 2.5Gi 8.0Gi 100Mi 4.5Gi 12Gi
Swap: 2.0Gi 0B 2.0Gi
重要概念: buffer 和 cache(下一节详细讲)
3. 磁盘资源
- 容量:硬盘或 SSD 提供的总存储空间
- 读写性能:
- 机械硬盘( HDD):容量大、价格低、速度慢( 100-200 MB/s)
- 固态硬盘( SSD):速度快、价格高、容量相对小( 500-3000 MB/s)
- NVMe SSD:更快( 3000-7000 MB/s)
查看磁盘使用: 1
2
3df -h # 查看分区使用情况
lsblk # 列出块设备及挂载点
du -sh /* # 查看根目录下各目录占用空间
查看磁盘 I/O: 1
2iostat -x 1 # 每秒刷新一次(需要 sysstat 包)
iotop # 实时查看各进程的磁盘 I/O(需要 root 权限)
4. 网络资源
- 带宽:网络接口的最大传输速率(如 1 Gbps 、 10 Gbps)
- 吞吐量:实际传输速率
- 延迟:数据包往返时间( RTT)
查看网络流量: 1
2iftop -i eth0 # 实时显示网络流量(需要安装 iftop)
ip -s link # 查看接口统计信息(收发包数、丢包数)
查看网络连接: 1
2ss -tulnp # 查看监听的端口和连接(替代 netstat)
lsof -i :80 # 查看 80 端口被哪个进程占用
Buffer 和 Cache 详解:为什么"内存不够用"经常是误判
Linux
的内存管理很激进:尽可能多地使用内存来缓存数据,提高性能。所以你会发现
free 显示的 free
很小,但这不代表内存不够用。
Buffer vs Cache
| 类型 | 作用 | 示例 |
|---|---|---|
| Buffer | 写入数据时的缓冲区(数据从内存写入磁盘前的临时存储) | 写文件时,数据先存在 buffer,批量写入磁盘 |
| Cache | 读取数据时的缓存(从磁盘读取的数据缓存在内存,下次直接从内存读) | 读文件时,内容缓存在 cache,下次秒读 |
为什么要这么设计?
- Buffer:减少磁盘写入次数。如果每次写入都直接写磁盘,太慢了(尤其是大量零碎文件)。先攒一批数据,再一次性写入磁盘,快很多。
- Cache:减少磁盘读取次数。频繁访问的文件缓存在内存里,读取速度快几百倍。
重要:Buffer 和 Cache
是可回收的。当程序需要更多内存时,内核会自动释放 buffer/cache
给程序用。所以你看到 free 显示的 available
才是真正可用的内存(包括可回收的 buffer/cache)。
误判示例: 1
2 total used free shared buff/cache available
Mem: 15Gi 2.5Gi 1.0Gi 100Mi 11.5Gi 12Gi
新手看到 free 只有 1.0Gi,会觉得内存不够用了。但实际
available 有 12Gi(因为 11.5Gi 的 buff/cache
是可回收的)。
什么时候内存真的不够用?
available接近 0- Swap 使用率很高(说明内存不够,开始用硬盘当内存)
- 进程被 OOM killer 杀掉(内核的内存不足杀手)
进程监控工具链:从整体到细节
1. top:实时监控的"万能工具"
top 是最常用的实时监控工具,显示 CPU
、内存、进程等信息。
基本用法: 1
top
界面解读: 1
2
3
4
5
6
7
8
9top - 12:00:00 up 10 days, 3:45, 2 users, load average: 1.23, 0.87, 0.45
Tasks: 150 total, 2 running, 148 sleeping, 0 stopped, 0 zombie
%Cpu(s): 5.2 us, 2.1 sy, 0.0 ni, 92.3 id, 0.3 wa, 0.0 hi, 0.1 si, 0.0 st
MiB Mem : 15872.0 total, 8234.5 free, 3456.2 used, 4181.3 buff/cache
MiB Swap: 2048.0 total, 2048.0 free, 0.0 used. 11234.5 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
1234 root 20 0 123456 12345 1234 R 50.0 0.8 1:23.45 python3
5678 www-data 20 0 234567 23456 2345 S 10.0 1.5 0:12.34 nginx
关键指标:
- load average: 1/5/15 分钟的平均负载(接近 CPU 核心数说明系统满载)
- Tasks:总进程数、运行/睡眠/停止/僵尸进程数
- %Cpu(s):
us( user):用户空间 CPU 占用sy( system):内核空间 CPU 占用ni( nice):低优先级进程 CPU 占用id( idle):空闲 CPU(越高越好)wa( wait):等待 I/O 的 CPU 时间(高说明磁盘/网络慢)
- Mem/Swap:内存和交换空间使用情况
常用快捷键:
P:按 CPU 占用排序M:按内存占用排序k:输入 PID 发送信号终止进程1:显示每个 CPU 核心的使用率q:退出
2. htop: top 的增强版
htop 是 top
的彩色增强版,支持鼠标操作、树状视图、直接终止进程。
安装与使用: 1
2
3sudo apt install htop # Debian/Ubuntu
sudo dnf install htop # CentOS/RHEL
htop
优点:
- 彩色界面,更直观
- 支持鼠标点击选择进程
- 显示进程树( F5 切换树状视图)
- 可以直接选择并终止进程( F9 发送信号)
3. ps:静态进程快照
ps 提供当前进程的静态快照(不像 top 那样实时刷新)。
常用用法: 1
2ps -ef # Unix 风格,显示所有进程(-e)的完整信息(-f)
ps aux # BSD 风格,显示所有进程( a)的用户信息( u)和后台进程( x)
输出字段解释(ps aux):
- USER:进程所属用户
- PID:进程 ID
- %CPU: CPU 占用率
- %MEM:内存占用率
- VSZ:虚拟内存大小(进程申请的总内存)
- RSS:常驻内存大小(实际占用的物理内存)
- STAT:进程状态
R:运行中( Running)S:睡眠中( Sleeping,等待事件)D:不可中断睡眠(通常在等待磁盘 I/O)Z:僵尸进程( Zombie,已退出但未被父进程回收)T:停止( Stopped,通常被 Ctrl+Z 暂停)
- TIME:进程累计 CPU 时间
- COMMAND:进程命令
高级用法: 1
2
3ps -ef | grep nginx # 查看 nginx 相关的进程
ps -ef | grep -v grep # 去掉 grep 自己的进程
ps -eo pid,ppid,cmd,%cpu,%mem --sort=-%cpu | head -10 # 按 CPU 占用排序,显示前 10
4. pstree:进程树
pstree
以树状结构展示进程父子关系,帮助理解进程层级。
基本用法: 1
2pstree -p # -p 显示 PID
pstree -ap # -a 显示命令参数
5. lsof:查看打开的文件
lsof( List Open
Files)用于列出系统中所有打开的文件,包括常规文件、网络连接、设备等。
为什么要用 lsof?
- 查看进程打开了哪些文件(如配置文件、日志文件、数据库文件)
- 查看端口被哪个进程占用(如 80 端口被谁占用)
- 查看文件被哪个进程占用(如某个文件删不掉,可能被进程占用)
- 恢复被误删的文件(如果进程还在运行,文件句柄还在,可以通过
/proc/<pid>/fd/恢复)
常用用法: 1
2
3
4
5
6
7
8lsof # 列出所有打开的文件(输出很长)
lsof -p <PID> # 查看指定进程打开的文件
lsof -u <user> # 查看指定用户打开的文件
lsof -c <command> # 查看指定命令打开的文件
lsof -i :80 # 查看 80 端口被哪个进程占用
lsof -i tcp # 查看所有 TCP 连接
lsof +D /var/log # 查看 /var/log 目录下被打开的文件
lsof +L1 # 查看链接数小于 1 的文件(通常是已删除但仍被进程占用的文件)
示例:查看 nginx 打开的所有文件 1
lsof -c nginx
输出示例: 1
2
3
4
5
6COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nginx 1234 root cwd DIR 8,1 4096 2 /
nginx 1234 root txt REG 8,1 123456 789 /usr/sbin/nginx
nginx 1234 root 1w REG 8,1 12345 1011 /var/log/nginx/access.log
nginx 1234 root 2w REG 8,1 6789 1012 /var/log/nginx/error.log
nginx 1234 root 6u IPv4 12345 0t0 TCP *:80 (LISTEN)
FD:文件描述符(cwd是当前目录,txt是程序文件,1w是 stdout,2w是 stderr,6u是打开的套接字)TYPE:类型(DIR是目录,REG是常规文件,IPv4是网络套接字)
6. 网络端口监控
ss:查看网络连接(替代 netstat)
1 | ss -tulnp # 查看监听的端口和连接 |
-t: TCP 连接-u: UDP 连接-l:监听端口( LISTEN 状态)-n:显示数字形式的地址和端口(不解析主机名)-p:显示进程 PID 和名称
示例:查看 80 端口的监听情况 1
ss -tulnp | grep :80
输出示例: 1
tcp LISTEN 0 128 *:80 *:* users:(("nginx",pid=1234,fd=6))
lsof:查看端口占用
1 | lsof -i :80 # 查看 80 端口被哪个进程占用 |
7. 磁盘 I/O 监控
iostat:磁盘 I/O 统计
1 | iostat -x 1 # 每秒刷新一次,显示扩展信息 |
关键指标:
- %util:磁盘使用率(接近 100% 说明磁盘很忙)
- await:平均等待时间(毫秒)
- r/s, w/s:每秒读写次数
iotop:实时查看进程磁盘 I/O
1 | sudo iotop -o # -o 只显示有 I/O 的进程 |
进程控制:信号、后台任务、优先级
1. kill:发送信号
kill
不只是"杀进程",它的本质是向进程发送信号。
常用信号:
| 信号编号 | 信号名称 | 作用 | 示例 |
|---|---|---|---|
| 1 | SIGHUP | 重新加载配置(不终止进程) | kill -1 <PID> |
| 2 | SIGINT | 中断(等同于 Ctrl+C) | kill -2 <PID> |
| 9 | SIGKILL | 强制终止(进程无法捕获,立即终止) | kill -9 <PID> |
| 15 | SIGTERM | 温和终止(进程可以捕获,做清理后退出) | kill <PID>(默认) |
| 20 | SIGTSTP | 暂停(等同于 Ctrl+Z) | kill -20 <PID> |
最佳实践: 1. 先用 kill <PID>(
SIGTERM),让进程有机会做清理(如保存数据、关闭连接) 2.
如果进程不响应,再用 kill -9 <PID>(
SIGKILL)强制终止
示例:重新加载 nginx 配置(不停止服务)
1
2
3sudo kill -1 $(pidof nginx | awk '{print $1}') # 发送 SIGHUP 信号
# 或者
sudo nginx -s reload # nginx 提供的便捷命令
2. 后台运行任务
方法 1:使用 &
1 | ./long_task.sh & # 在后台运行(但退出 SSH 后会被终止) |
方法 2:使用
nohup(推荐)
1 | nohup ./long_task.sh & # 在后台运行,退出 SSH 后仍然继续 |
nohup: No Hangup,忽略 SIGHUP 信号( SSH 断开时发送的信号)- 输出默认重定向到
nohup.out
更好的方式: 1
nohup ./long_task.sh > /dev/null 2>&1 & # 输出丢弃,不保存到文件
方法 3:使用
screen 或 tmux(最佳实践)
1 | screen -S mysession # 创建一个 screen 会话 |
管理后台任务
1 | jobs # 查看后台任务 |
3. 调整进程优先级( nice/renice)
Linux 使用 nice 值控制进程优先级:
- nice 值范围:
-20(最高优先级)到19(最低优先级) - 默认 nice 值:
0 - nice 值越低,优先级越高(更容易抢到 CPU)
启动时指定优先级( nice)
1 | nice -n 10 ./cpu_intensive_task.sh # 以 nice 值 10 启动(降低优先级) |
调整运行中进程的优先级( renice)
1 | renice -n 10 -p <PID> # 把进程 PID 的 nice 值改为 10 |
使用场景:
- 后台备份任务:用
nice -n 19启动,不影响正常业务 - 关键业务进程:用
renice -n -10提高优先级
特殊进程状态:孤儿进程和僵尸进程
孤儿进程( Orphan Process)
定义:父进程退出后,子进程由 PID 1( systemd 或 init)接管。
示例代码( Python): 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import os
import time
def child_process():
print(f"Child: PID={os.getpid()}, PPID={os.getppid()}")
time.sleep(3) # 等待父进程退出
print(f"Child after parent exit: PID={os.getpid()}, PPID={os.getppid()}")
if __name__ == "__main__":
pid = os.fork()
if pid > 0:
# 父进程
print(f"Parent: PID={os.getpid()}, Child PID={pid}")
os._exit(0) # 父进程立即退出
else:
# 子进程
child_process()
输出示例: 1
2
3Parent: PID=1234, Child PID=1235
Child: PID=1235, PPID=1234
Child after parent exit: PID=1235, PPID=1 # PPID 变成 1(被 systemd 接管)
孤儿进程有害吗?不一定。 systemd 会接管孤儿进程,正常管理它们。
僵尸进程( Zombie Process)
定义:进程已退出,但父进程未调用 wait()
回收其退出状态,导致进程信息仍保留在进程表中。
特点:
- 不占用 CPU 和内存(已退出)
- 但占用进程表项(过多会耗尽系统进程表)
- 状态显示为
Z( Zombie)
查看僵尸进程: 1
ps aux | grep ' Z '
示例代码( Python): 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import os
import time
if __name__ == '__main__':
pid = os.fork()
if pid > 0:
# 父进程
print(f"Parent: PID={os.getpid()}, Child PID={pid} (will become zombie)")
time.sleep(15) # 父进程暂停 15 秒,子进程退出但未被回收(僵尸状态)
os.wait() # 回收僵尸进程
print("Zombie child has been reaped.")
else:
# 子进程
print(f"Child: PID={os.getpid()}, PPID={os.getppid()}")
os._exit(0) # 子进程立即退出,进入僵尸状态
如何解决僵尸进程? 1. 让父进程调用
wait()(如果父进程是你写的程序,修复代码) 2.
杀掉父进程(父进程退出后,子进程被 systemd 接管并回收) 3.
重启系统(最后手段)
实战:完整的性能排障流程
场景:系统变慢了,怎么排查
1. 看整体负载
1 | uptime # 查看 load average |
判断:
- load average 高?可能 CPU 满载或 I/O 慢
- CPU idle 低? CPU 瓶颈
- CPU wa 高?磁盘 I/O 慢
2. 找出占用资源的进程
1 | top # 按 P 排序 CPU,按 M 排序内存 |
3. 查看进程详情
1 | lsof -p <PID> # 查看进程打开的文件 |
4. 查看磁盘 I/O
1 | iostat -x 1 # 查看磁盘 I/O |
5. 查看网络连接
1 | ss -tulnp # 查看监听的端口 |
6. 优化或终止进程
1 | renice -n 10 -p <PID> # 降低进程优先级 |
实战案例: Nginx 日志文件被误删怎么办
场景
运维人员不小心 rm -rf /var/log/nginx/access.log,但
nginx 进程还在运行。
问题
虽然文件被删了,但 nginx 进程仍然持有文件句柄(在
/proc/<pid>/fd/
下),继续往"已删除的文件"写数据。此时:
df -h显示磁盘占用没有减少(因为文件还在占用空间)ls /var/log/nginx/看不到access.log(因为目录项已删除)
解决方案
1. 找到 nginx 进程的 PID
1 | pidof nginx # 或 ps aux | grep nginx |
假设主进程 PID 是 1234 。
2. 查看进程打开的文件
1 | lsof -p 1234 | grep access.log |
输出示例: 1
nginx 1234 root 6w REG 8,1 123456789 /var/log/nginx/access.log (deleted)
6w:文件描述符是 6,模式是w(写)(deleted):文件已被删除但进程仍持有句柄
3. 恢复文件
1 | sudo cp /proc/1234/fd/6 /var/log/nginx/access.log |
4. 重新加载 nginx
1 | sudo nginx -s reload # 让 nginx 重新打开日志文件 |
原理:
- 删除文件只是删除目录项(文件名), inode 和数据块还在(因为进程还在用)
- 通过
/proc/<pid>/fd/<fd>可以访问进程打开的文件(即使已删除) cp复制文件后,重新加载 nginx,让它重新打开日志文件
总结与扩展阅读
这篇文章涵盖了 Linux 进程与资源管理的核心内容: 1. ✅ 进程与程序的基本概念(进程 vs 程序 vs 线程、父子关系) 2. ✅ Linux 资源管理概述( CPU/内存/磁盘/网络) 3. ✅ Buffer 和 Cache 详解(为什么"内存不够用"经常是误判) 4. ✅ 进程监控工具链( top/htop/ps/pstree/lsof/ss/iostat) 5. ✅ 进程控制( kill 信号、后台任务、优先级调整) 6. ✅ 特殊进程状态(孤儿进程、僵尸进程) 7. ✅ 实战案例(性能排障流程、 Nginx 日志恢复)
扩展阅读:
- Linux Performance( Brendan Gregg): http://www.brendangregg.com/linuxperf.html
man proc:查看/proc文件系统的详细说明man 7 signal:查看所有信号的说明
下一步:
- 《 Linux 磁盘管理》:学习分区、格式化、挂载、 LVM 、 RAID 等
- 《 Linux 用户管理》:学习如何管理用户/组/权限
到这里,建议已经从"会看 top"升级到"能快速定位资源瓶颈、能优化进程优先级、能处理异常进程状态"。进程与资源管理是 Linux 运维的核心技能,掌握了它,你就能更好地排查性能问题。
- 本文标题:Linux 进程与资源管理
- 本文作者:Chen Kai
- 创建时间:2019-12-07 16:00:00
- 本文链接:https://www.chenk.top/Linux-%E8%BF%9B%E7%A8%8B%E4%B8%8E%E8%B5%84%E6%BA%90%E7%AE%A1%E7%90%86/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!