Linux 进程与资源管理
Chen Kai BOSS

线上排障时,最关键的能力不是"会背命令",而是能把现象快速映射到资源与进程: 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(并行计算)

进程的五大特性

  1. 独立性:每个进程都有自己的内存空间和系统资源,彼此隔离(进程 A 的变量不会影响进程 B)
  2. 并发性:操作系统允许多个进程同时运行,通过多任务调度实现并发处理
  3. 动态性:进程不断创建、执行、终止,状态实时变化(操作系统的运行就是不断创建和销毁进程)
  4. 父子关系:进程由父进程通过 fork() 调用创建,形成父子结构( PPID 字段指明父进程)
  5. 调度性:操作系统使用调度算法(如时间片轮转、优先级调度)决定进程执行顺序

进程的父子关系( PID 和 PPID)

每个进程都有两个重要的 ID:

  • PID( Process ID):进程 ID,唯一标识一个进程
  • PPID( Parent Process ID):父进程 ID,标识创建这个进程的父进程

示例

1
ps -ef | grep bash

输出示例:

1
2
3
UID   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
2
lscpu | 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
3
df -h  # 查看分区使用情况
lsblk # 列出块设备及挂载点
du -sh /* # 查看根目录下各目录占用空间

查看磁盘 I/O

1
2
iostat -x 1  # 每秒刷新一次(需要 sysstat 包)
iotop # 实时查看各进程的磁盘 I/O(需要 root 权限)

4. 网络资源

  • 带宽:网络接口的最大传输速率(如 1 Gbps 、 10 Gbps)
  • 吞吐量:实际传输速率
  • 延迟:数据包往返时间( RTT)

查看网络流量

1
2
iftop -i eth0  # 实时显示网络流量(需要安装 iftop)
ip -s link # 查看接口统计信息(收发包数、丢包数)

查看网络连接

1
2
ss -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
9
top - 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 的增强版

htoptop 的彩色增强版,支持鼠标操作、树状视图、直接终止进程。

安装与使用

1
2
3
sudo apt install htop  # Debian/Ubuntu
sudo dnf install htop # CentOS/RHEL
htop

优点

  • 彩色界面,更直观
  • 支持鼠标点击选择进程
  • 显示进程树( F5 切换树状视图)
  • 可以直接选择并终止进程( F9 发送信号)

3. ps:静态进程快照

ps 提供当前进程的静态快照(不像 top 那样实时刷新)。

常用用法

1
2
ps -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
3
ps -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
2
pstree -p  # -p 显示 PID
pstree -ap # -a 显示命令参数

5. lsof:查看打开的文件

lsof( List Open Files)用于列出系统中所有打开的文件,包括常规文件、网络连接、设备等。

为什么要用 lsof?

  • 查看进程打开了哪些文件(如配置文件、日志文件、数据库文件)
  • 查看端口被哪个进程占用(如 80 端口被谁占用)
  • 查看文件被哪个进程占用(如某个文件删不掉,可能被进程占用)
  • 恢复被误删的文件(如果进程还在运行,文件句柄还在,可以通过 /proc/<pid>/fd/ 恢复)

常用用法

1
2
3
4
5
6
7
8
lsof  # 列出所有打开的文件(输出很长)
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
6
COMMAND   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
2
lsof -i :80  # 查看 80 端口被哪个进程占用
lsof -i tcp # 查看所有 TCP 连接

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
3
sudo 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:使用 screentmux(最佳实践)

1
2
3
4
5
6
screen -S mysession  # 创建一个 screen 会话
./long_task.sh # 在 screen 里运行任务
# 按 Ctrl+A+D 分离会话(任务继续运行)
# 退出 SSH 后,任务仍然运行

screen -r mysession # 重新连接会话

管理后台任务

1
2
3
jobs  # 查看后台任务
fg %1 # 把任务 1 调到前台
bg %1 # 把任务 1 继续在后台运行(通常在 Ctrl+Z 暂停后使用)

3. 调整进程优先级( nice/renice)

Linux 使用 nice 值控制进程优先级:

  • nice 值范围:-20(最高优先级)到 19(最低优先级)
  • 默认 nice 值:0
  • nice 值越低,优先级越高(更容易抢到 CPU)

启动时指定优先级( nice)

1
2
nice -n 10 ./cpu_intensive_task.sh  # 以 nice 值 10 启动(降低优先级)
nice -n -10 ./important_task.sh # 以 nice 值 -10 启动(提高优先级,需要 root)

调整运行中进程的优先级( renice)

1
2
renice -n 10 -p <PID>  # 把进程 PID 的 nice 值改为 10
renice -n -5 -p <PID> # 提高优先级(需要 root)

使用场景

  • 后台备份任务:用 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
17
import 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
3
Parent: 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
15
import 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
2
uptime  # 查看 load average
top # 实时查看 CPU 、内存、进程

判断

  • load average 高?可能 CPU 满载或 I/O 慢
  • CPU idle 低? CPU 瓶颈
  • CPU wa 高?磁盘 I/O 慢

2. 找出占用资源的进程

1
2
3
top  # 按 P 排序 CPU,按 M 排序内存
ps aux --sort=-%cpu | head -10 # 查看 CPU 占用前 10 的进程
ps aux --sort=-%mem | head -10 # 查看内存占用前 10 的进程

3. 查看进程详情

1
2
3
lsof -p <PID>  # 查看进程打开的文件
ls -l /proc/<PID>/fd/ # 查看进程的文件描述符
cat /proc/<PID>/status # 查看进程的详细状态

4. 查看磁盘 I/O

1
2
iostat -x 1  # 查看磁盘 I/O
sudo iotop -o # 查看哪个进程在读写磁盘

5. 查看网络连接

1
2
ss -tulnp  # 查看监听的端口
lsof -i # 查看所有网络连接

6. 优化或终止进程

1
2
3
renice -n 10 -p <PID>  # 降低进程优先级
kill <PID> # 温和终止
kill -9 <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 许可协议。转载请注明出处!
 评论