Linux进程
进程是程序的一次运行实例:程序是磁盘上的静态文件,进程是内核为它分配的内存、文件句柄、执行状态的集合。同一个程序可以跑出多个进程(各自独立的 PID 和数据)。ps/top 的用法见 Linux工具,本文讲背后的机制。
进程状态机
内核用单字母标记进程状态(ps 的 STAT 列),核心转换如下:
| 状态 | 含义 | 运维关注点 |
|---|---|---|
| R (Running) | 正在运行或可运行 | 正常 |
| S (Sleeping) | 可中断睡眠,等事件 | 绝大多数进程的日常状态,正常 |
| D (Disk sleep) | 不可中断睡眠,通常在等 IO | 大量 D 状态进程 = 存储故障或 IO 瓶颈,kill 也杀不掉 |
| Z (Zombie) | 已退出,等父进程回收 | 少量正常,大量堆积见下节 |
| T (Stopped) | 被信号暂停 | Ctrl+Z 或调试时出现 |
僵尸与孤儿进程
进程退出时内核保留它的一小块元数据(PID、退出码),等父进程通过 wait 读取后才彻底释放。这段窗口期就是僵尸状态——短暂的僵尸完全正常。
问题出在父进程从不 wait(代码 bug):僵尸一直占着 PID 表项。僵尸进程已经死了,kill 无效(杀一个不存在的运行体没有意义),唯一解法是杀掉它的父进程,僵尸随即被 PID 1 收养并回收。
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /^Z/' # 找僵尸及其父进程孤儿进程则是反过来的情况:父进程先死了,子进程被 PID 1(systemd)收养。孤儿本身无害,systemd 会负责回收,它只是"没爹"不是"坏死"。
PID 1 与进程树
所有进程构成一棵树:PID 1 是 systemd,是一切用户进程的祖先。进程通过 fork 复制自身创建子进程(先复制再 exec 加载新程序)。这棵树决定了服务管理的两个事实:
- systemctl 管理服务本质是管理这棵树上的节点,所以能追踪服务的完整子进程
- 杀进程组时要注意连坐:
kill -- -PGID会杀掉整个进程组
pstree -p # 树状查看所有进程
ps -ef # PPID 列揭示父子关系
cat /proc/1/cmdline | tr '\0' ' ' # PID 1 是什么信号:进程间最原始的通知
信号是内核发给进程的异步中断通知,共 60 余种。终止进程的正确理解是"发信号请求",而非强制抹除:
| 信号 | 编号 | 行为 | 能否被捕获 |
|---|---|---|---|
| SIGTERM | 15 | 礼貌请求退出,进程可捕获并做清理(flush 缓冲、关连接、删临时文件) | ✅ |
| SIGKILL | 9 | 内核直接销毁,进程无机会执行任何清理 | ❌ |
| SIGHUP | 1 | 终端断开;守护进程约定为"重读配置" | ✅ |
| SIGSTOP / SIGCONT | 19 / 18 | 暂停 / 恢复 | ❌ / ❌ |
先 15 后 9,别上来就 kill -9
kill PID 默认发 SIGTERM。直接 -9 会丢失未落盘数据、留下锁文件和损坏的中间状态;进程卡死不响应 SIGTERM(常见于 D 状态)时才用 -9。
Shell 脚本里用 trap 捕获信号做清理:trap 'rm -f /tmp/lock' EXIT TERM INT。
优先级与守护进程
nice 值决定调度优待程度,范围 -20 到 19,值越小优先级越高。普通用户只能调高(放慢)自己的进程,root 才能调低(加快):
nice -n 10 ./slow_job # 启动时降优先级
renice -n 5 -p PID # 运行中调整守护进程(daemon)是常驻后台、无控制终端的服务进程,名字常带 d 后缀(sshd、crond)。特征:由 systemd 托管(现代)或主动脱离终端(传统 nohup/setsid)、输出重定向到日志、工作目录切到 /。服务的启动与开机自启用 systemctl 管理,见 Linux系统管理 的 systemd 章节。
负载(load average)
top 和 uptime 显示的三个数是过去 1/5/15 分钟处于 R+D 状态的平均进程数。注意包含 D 状态:负载高不一定是 CPU 忙,IO 等待同样推高负载。判断标准:负载持续高于 CPU 核数才需要关注;负载高而 CPU 空闲,优先查磁盘 IO(iostat、iotop)。