Skip to content

进程管理

服务出问题时,排查通常从进程是否存在、资源占用是否异常、由谁启动、受什么管理器管开始。

一、进程基础概念

进程是正在运行的程序实例。一个程序可以启动多个进程,每个进程有唯一的 PID(进程 ID)。进程可以继续创建子进程,形成父子关系——排查时不能只看命令名,还要看 PID、PPID(父进程 ID)、运行用户和启动来源。

查看当前 Shell 自己的进程信息:

bash
echo $$            # 当前 Shell 的 PID
ps -p $$           # 查看该进程详情

查看进程树(父子关系):

bash
pstree -p

pstree 不是默认安装的:

bash
yum install psmisc -y    # RHEL 系
apt install psmisc -y    # Debian 系

进程由谁启动直接决定排查入口。systemd 拉起的进程、Shell 手动启动的进程、supervisor 托管的进程、容器运行时管理的进程——它们的日志位置、重启策略、环境变量都不同。

二、ps 查看进程

bash
ps aux           # BSD 风格,显示所有用户的所有进程
ps -ef           # Unix 风格,功能类似
ps aux | grep nginx

查看指定 PID 的进程,并只显示关心的字段:

bash
ps -p 1234 -o pid,ppid,user,stat,pcpu,pmem,cmd

常用输出字段:

字段含义
PID进程 ID
PPID父进程 ID
USER进程的运行用户
STAT进程状态(R=运行中、S=休眠、D=不可中断睡眠、Z=僵尸)
%CPUCPU 使用百分比
%MEM内存使用百分比
CMD启动命令行

CMD 列可能被终端宽度截断。要看完整启动命令,直接读 /proc/PID/cmdline

bash
tr '\0' ' ' </proc/1234/cmdline

/proc/PID/cmdline 中的参数以 null 字符分隔,tr 把它们转成空格。

三、top 和 htop

top 提供实时的进程信息刷新:

bash
top

top 界面中的常用交互键:

功能
P按 CPU 使用率排序
M按内存使用率排序
1展开显示每个 CPU 核心
kkill 指定进程(会提示输入 PID)
q退出

htoptop 的增强版本,支持鼠标操作、颜色显示和更直观的进程树。不一定默认安装:

bash
yum install htop -y    # RHEL 系
apt install htop -y    # Debian 系

CPU 使用率飙高时,先 top 定位进程,然后看该进程内的线程分布:

bash
top -H -p <pid>

Java、Go、Node.js 这类多线程服务排 CPU 瓶颈时,看线程级别比看进程级别更有效。

四、信号和 kill

信号是 Linux 进程间通信的一种方式,最常用的场景是通知进程终止或重新加载配置。

信号编号含义
SIGTERM15请求进程正常退出(可被进程捕获并处理)
SIGKILL9强制终止(不可被捕获,内核直接终止进程)
SIGHUP1常用于通知进程重新加载配置
bash
kill 1234              # 默认发送 SIGTERM(15)
kill -15 1234
kill -9 1234           # 强制终止

SIGTERM 是"请你自己停掉"——进程收到后可以清理临时文件、关闭连接、释放资源再退出。SIGKILL 是内核直接终止进程,进程没有机会做任何清理。如果某个服务经常需要用 kill -9 才能停,说明它的信号处理和退出逻辑可能有问题。

按名称操作进程:

bash
pkill nginx             # 向所有名为 nginx 的进程发 SIGTERM
pgrep -a nginx          # 先看有哪些匹配的进程

pkill 的匹配范围可能比预期大——pkill nginx 可能也匹配到 nginx-exporter 之类的进程。执行前先 pgrep -a 看清楚匹配结果。

五、前台和后台任务

在 Shell 中,命令默认在前台运行,会阻塞 Shell 直到执行完成。可以在命令末尾加 & 放到后台:

bash
sleep 100 &
jobs                # 查看当前 Shell 的后台任务

前后台切换:

bash
fg %1               # 把编号为 1 的后台任务切回前台
Ctrl+z              # 暂停当前前台任务
bg %1               # 继续在后台运行被暂停的任务

临时命令用 &Ctrl+z 处理没问题。但长期运行的任务靠 & 管理有隐患:SSH 断开后进程可能被 HUP 信号终止、输出没地方去、进程状态靠记忆追踪。长期任务建议按场景选工具:

场景合适的工具
临时长命令tmux / screen
服务进程systemd
容器服务容器运行时 / Kubernetes
定时任务systemd timer / cron

六、systemd

systemd 是目前大多数 Linux 发行版的服务管理器,负责在系统启动时按依赖关系拉起服务、监控运行状态、接管日志、在进程异常退出时按策略重启。

systemd 管理的对象叫 unit,常见的 unit 类型:

类型用途
.service定义服务进程
.timer定时触发,替代 cron 的另一种方式
.socket基于 socket 激活服务(先监听再按需启动)
.mount管理挂载点
.target一组 unit 的集合,相当于运行级别的概念

查看服务状态:

bash
systemctl status nginx
systemctl is-active nginx          # 只看是否在运行
systemctl is-enabled nginx         # 只看是否设了开机自启

启动和停止:

bash
systemctl start nginx
systemctl stop nginx
systemctl restart nginx
systemctl reload nginx             # 重新加载配置(不重启进程)

开机自启:

bash
systemctl enable nginx             # 设为开机自启
systemctl disable nginx            # 取消开机自启

查看服务日志:

bash
journalctl -u nginx -f                          # 持续跟踪
journalctl -u nginx --since "1 hour ago"        # 只看最近一小时

服务启动失败的排查流程:

bash
systemctl status service-name --no-pager        # 看当前状态和最后几行日志
journalctl -u service-name -n 100 --no-pager    # 看最近 100 行日志
systemctl cat service-name                      # 看实际加载到的 unit 内容

systemctl cat 比手动找 unit 文件更准确——它会把 /etc/systemd/system/ 下的覆盖配置(drop-in)也合并显示出来。

七、unit 文件

unit 文件定义了 systemd 如何管理一个服务。文件位置:

路径用途
/usr/lib/systemd/system/(RHEL 系)软件包安装的 unit
/lib/systemd/system/(Debian 系)软件包安装的 unit
/etc/systemd/system/自定义 unit 和覆盖配置

一个最小可用的 service 文件:

ini
# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/app
Restart=always
RestartSec=5
User=app
Group=app

[Install]
WantedBy=multi-user.target

三个段的职责:

作用
[Unit]服务描述、依赖关系和启动顺序
[Service]进程如何启动、停止、重启,以什么身份运行
[Install]systemctl enable 时挂在哪个 target 下面

常用字段详解:

字段含义备注
Description服务说明systemctl status 中显示
After排在哪个服务之后启动只表示顺序,不表示依赖;对方启动失败不会影响自己
Requires强依赖,对方失败则自己也会失败适合"没网络这个服务没意义"的场景
Wants弱依赖,对方失败不影响自己适合"对方在最好,没有也能接受"
Type进程启动类型决定了 systemd 怎么判断服务是否"已启动"
ExecStart启动命令写绝对路径,systemd 的 PATH 和登录 Shell 不同
Restart退出后的重启策略always / on-failure / no
RestartSec重启前等待时间防止失败后疯狂重启
User / Group以哪个用户身份运行服务长期用 root 跑,风险偏高
EnvironmentFile从这里读取环境变量端口、路径、密钥等配置适合放单独文件
WantedByenable 时的挂载目标通常用 multi-user.target

Type 的选择直接影响 systemd 能否正确判断服务状态:

Type适用情况
simple程序以前台方式运行,启动即就绪(最常见)
forking程序自己 fork 到后台(老式守护进程常用)
oneshot任务执行完就退出,用于初始化脚本
notify程序主动通过 socket 通知 systemd"我已就绪"

现在写服务优先让程序以前台方式运行,用 Type=simple。把守护化(daemonize)交给 systemd 管,日志和退出状态更好追踪。

编写和启用的流程:

bash
vim /etc/systemd/system/myapp.service
systemctl daemon-reload          # 让 systemd 重新读取 unit 文件
systemctl start myapp
systemctl status myapp --no-pager
systemctl enable myapp           # 设置开机自启

修改 unit 后同样需要 systemctl daemon-reloadrestart

常见启动失败的排查:

现象常见原因
手动执行正常,systemd 启动失败缺少环境变量、工作目录不对、路径不是绝对路径
服务启动后立刻 failed程序自己退出了,或者 Type 写错
修改 unit 后不生效忘了 systemctl daemon-reload
服务反复重启Restart=always 配合程序快速失败
日志找不到程序把日志写到了文件而不是 stdout/stderr,或者 journald 配置了不持久化