一、进程的本质与 /proc

进程是运行中的程序实例。内核为每个进程维护一个 task_struct 数据结构,记录 PID、PPID、状态、打开的文件、内存映射、CPU 时间、UID/GID 等信息。

Linux 将所有进程信息通过 /proc 虚拟文件系统暴露给用户空间。/proc 存在于内存中,每个进程对应一个 /proc/[PID]/ 目录:

ls /proc/          # 数字目录即 PID
ls /proc/1234/     # 查看 PID 1234 的信息

关键文件:

路径内容
/proc/[PID]/cmdline启动该进程的完整命令行(含参数,\0 分隔)
/proc/[PID]/status进程状态、内存用量、UID/GID、PPID
/proc/[PID]/fd/进程持有的所有文件描述符(含文件、socket、pipe、设备)
/proc/[PID]/maps进程内存映射(加载的共享库、堆、栈)
/proc/[PID]/exe指向可执行文件的软链接
/proc/[PID]/cwd指向当前工作目录的软链接
/proc/cpuinfoCPU 型号、核心数、频率、flags
/proc/meminfo内存总量、空闲、可用、缓存、交换
/proc/loadavg1/5/15 分钟平均负载
/proc/interrupts硬件中断分配统计(排查硬件瓶颈)
/proc/filesystems内核支持的文件系统列表
/proc/sys/内核运行时参数(sysctl 接口)

认知pstophtoppstree 等所有进程查看工具,本质上都是读取 /proc 下的文件并格式化输出。直接访问 /proc 比任何命令都更原始、更可靠,同时也是最低层的接口。

二、进程树:pstree

进程是树形结构,所有进程的祖先是 PID 1(init 进程)。pstree 以树形展示进程关系,ps 的平铺列表直观得多

pstree                     # 树形展示所有进程
pstree -p                  # 带 PID
pstree -u                  # 带所属用户
pstree -a                  # 带完整命令行参数
pstree 1234                # 仅显示该 PID 的子树

示例输出:

systemd─┬─sshd───sshd───bash───pstree
        ├─nginx─┬─nginx
        │       └─nginx
        ├─greetd
        └─systemd-journal

进程树的本质:

  • 父进程终止时,子进程被 PID 1 收养。
  • 孤儿进程、僵尸进程、守护进程的形成机制只有在树形视角下才能真正理解。

三、查看进程:ps 的平铺列表

ps 读取 /proc 后以表格形式输出。不推荐死记 ps -efps aux 的固定字段顺序,推荐按需自定义输出列:

ps -eo pid,ppid,user,comm,stat,pcpu,pmem,time,cmd
ps -p 1234 -o pid,ppid,user,comm,stat,pcpu,pmem,cmd

常用列字段(ps -e -o 可自定义组合):

  • pidppid:进程 ID、父进程 ID
  • usergroup:运行用户、组
  • commcmd:命令名、完整命令行(含参数,推荐 cmd
  • stat:进程状态(见下文)
  • pcpupmem:CPU 占用率、内存占用率百分比
  • timeetime:累计 CPU 时间、进程已运行时间(elapsed time)
  • nicepriority:Nice 值(用户态优先级调整)、内核调度优先级

四、控制进程:kill 的本质是发信号

kill 并非“杀死”,而是向进程发送信号。信号是内核向进程异步通知事件的方式。

信号编号行为用途
SIGTERM15终止(可捕获)默认,优雅请求进程退出
SIGKILL9终止(不可捕获)强制杀死,内核直接回收,进程无法拦截,最后手段
SIGSTOP19暂停(不可捕获)挂起进程
SIGCONT18继续恢复被暂停的进程
SIGHUP1终止/重载终端断开,或通知守护进程重载配置(nginx、sshd 常用)
SIGUSR1/SIGUSR210/12用户自定义应用程序约定的自定义控制信号
kill 1234       # SIGTERM,进程可做清理(释放资源、保存状态)后退出
kill -9 1234    # SIGKILL,强制回收,可能导致数据丢失或锁残留
kill -HUP 5678  # SIGHUP,通知进程重载配置

警告kill -9 是最后手段。进程持有锁文件、共享内存或未落盘数据时强制杀死,可能造成状态不一致或资源泄漏。

五、实时监控:top

top交互式实时进程视图,默认按 CPU 占用排序,刷新一次读一次 /proc

只看这五行:

top - 06:45:48 up 13 min,  3 users,  load average: 0.00, 0.01, 0.03
Tasks: 220 total,   1 running, 219 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.1 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3861520 total,  2349032 free,   707056 used,   805432 buff/cache
KiB Swap:  2097148 total,  2097148 free,        0 used.  2825968 avail Mem
关键字段含义
第 1 行load average: x, y, z1/5/15 分钟平均运行队列长度(含运行 + 等待 CPU/I/O 的进程数),不等于 CPU 占用率。多核按核数折算(8 核满载为 8.0)
第 2 行zombie僵尸进程数
第 3 行us/sy/id/wa用户态/内核态/空闲/等待 I/O 的 CPU 占比——wa 高说明磁盘瓶颈
第 4-5 行avail Mem真正可用的内存(含可回收 cache),比 free 更准确

交互按键top 运行中按):

  • P / M / T:按 CPU / 内存 / 累计 CPU 时间排序
  • k:杀死进程(会提示输入 PID 和信号编号)
  • r:调整进程的 Nice 值
  • q:退出

备选工具htop(更直观,支持鼠标交互和树形视图),btm(Rust 编写,更现代的资源监控),glances(跨平台,支持 Web 界面)。

六、进程状态

进程状态(ps stat 列可见):

状态含义
R正在运行或可运行(等待 CPU 调度)
S可中断睡眠(等待事件,如键盘输入、网络数据)
D不可中断睡眠(通常等待 I/O,如磁盘读写,无法被信号唤醒
Z僵尸——子进程已退出,父进程未调用 wait() 回收其 task_struct
T被信号暂停(如 SIGSTOP

僵尸进程的形成机制:子进程终止时,内核保留其 task_struct 等待父进程读取退出状态(wait())。若父进程不调用,该结构体无法释放,进程表项残存。少量无影响,持续增多说明父进程有 bug。D 状态进程无法被 kill,通常需等待 I/O 完成或重启系统。

七、Init 系统:PID 1 的非绝对性

PID 1 是 init 进程,不一定是 systemd。具体实现因发行版而异:

Init 系统发行版
systemd(主流)RHEL、Debian、Ubuntu、Arch、Fedora、openSUSE
OpenRCGentoo、Alpine Linux
runitVoid Linux
s6部分嵌入式或极简发行版
SysV init传统 UNIX 风格,现代发行版已基本弃用

查看当前 init:pstree -p 1cat /proc/1/comm

systemd 的职责(若为 PID 1):

  1. 进程树根:所有用户态进程的终极祖先,收养孤儿进程。
  2. 服务编排(unit):定义启动顺序、依赖关系、失败重启策略。
  3. 资源控制(cgroup v2):通过 /sys/fs/cgroup 限制进程组的 CPU/内存/IO——容器化(Docker)的资源隔离基座
  4. 设备与登录(logind + udev):管理硬件设备权限移交和多座位切换。
  5. 日志(journald):结构化二进制日志。

八、扩展:资源控制(cgroups)

cgroups(control groups)是 Linux 内核提供的资源隔离机制,systemd 通过挂载到 /sys/fs/cgroup/ 的接口统一管理。/proc/1/fd/ 中出现的 io.pressurecpu.pressurememory.pressure 文件即对应 cgroup v2 的资源统计接口。容器(Docker)本质上是利用 cgroup + namespace 构建隔离环境,并受 systemd 的资源配额管理。

查看当前 cgroup 树:systemd-cgtop(实时按 cgroup 分组显示资源占用),或 ls /sys/fs/cgroup/ 查看内核接口文件。

docker 本身不直接与 cgroup 交互,而是在启动容器时向 systemd 申请创建子 cgroup(/sys/fs/cgroup/system.slice/docker-<container-id>.scope 或类似路径),由 systemd 代为应用资源限制。