跳到主要内容

进程管理

问题

请详细介绍 Linux 进程管理,包括进程状态、systemd、信号机制和容器底层原理(namespace/cgroup)。

答案

进程基础概念

进程是程序的一个运行实例。每个进程有唯一的 PID,由父进程 fork() 创建。

进程状态

 ┌──────────────────────────────────────────────────┐
│ R (Running) ←→ S (Sleeping) │
│ ↓ ↓ │
│ T (Stopped) D (Uninterruptible Sleep) │
│ ↓ ↓ │
│ └──────→ Z (Zombie) → 回收 │
└──────────────────────────────────────────────────┘
状态字符说明常见场景
RunningR正在执行或等待 CPU计算密集型任务
SleepingS等待事件(可中断)等待 I/O、网络连接
Disk SleepD不可中断睡眠等待磁盘 I/O、NFS
StoppedT被信号暂停Ctrl+Z、调试
ZombieZ已退出但未被父进程回收父进程未调用 wait()
D 状态和 Z 状态进程
  • D 状态(不可中断睡眠):无法被 kill 杀死,通常是磁盘 I/O 或 NFS 问题。大量 D 状态进程意味着存储系统可能出了问题
  • Z 状态(僵尸进程):已退出但占位不释放。少量无害,大量需排查父进程。杀死父进程可让 init 领养并回收

进程查看与管理

# ps - 查看进程
ps aux # BSD 风格,所有进程
ps -ef # System V 风格
ps aux --sort=-%mem # 按内存排序
ps -eo pid,ppid,user,%cpu,%mem,stat,cmd --sort=-%cpu | head -20

# top/htop - 实时监控
top
# 常用快捷键:
# P - 按 CPU 排序
# M - 按内存排序
# k - kill 进程
# 1 - 显示每个 CPU 核心
# c - 显示完整命令

htop # 更友好的交互式进程查看器

# pgrep/pkill - 按名称查找/杀死进程
pgrep -la nginx # 列出所有 nginx 进程
pkill -f "python app.py" # 按完整命令行匹配杀死

# pstree - 进程树
pstree -p # 显示 PID
pstree -u # 显示用户切换

信号机制

信号是进程间通信的一种方式,也是管理进程的主要手段:

信号编号说明用途
SIGHUP1挂起重载配置(nginx、sshd)
SIGINT2中断Ctrl+C
SIGQUIT3退出并 core dumpCtrl+\
SIGKILL9强制杀死(不可捕获)最后手段
SIGTERM15优雅终止(默认)推荐的终止方式
SIGSTOP19暂停(不可捕获)调试
SIGCONT18继续执行恢复暂停的进程
SIGUSR1/210/12用户自定义应用自定义行为
# kill 发送信号
kill <PID> # 默认发送 SIGTERM (15)
kill -9 <PID> # 强制杀死 SIGKILL
kill -HUP <PID> # 重载配置(如 nginx)
kill -USR1 <PID> # 用户自定义信号(如 nginx 重新打开日志)

# killall/pkill 按名称发信号
killall nginx
pkill -HUP nginx

# 常见操作组合
kill -TERM <PID> # 先优雅关闭
sleep 5
kill -9 <PID> # 不行再强杀
优雅停止 vs 强制杀死

生产环境应**先发 SIGTERM(15)**让进程优雅退出(关闭连接、刷新缓冲区、清理资源),等待一段时间后若仍未退出,再发 SIGKILL(9) 强制杀死。kill -9 不会给进程清理的机会,可能导致数据丢失。

systemd 服务管理

systemd 是现代 Linux 的初始化系统,管理服务(service)、挂载(mount)、定时器(timer)等:

# 服务管理
systemctl start nginx # 启动
systemctl stop nginx # 停止
systemctl restart nginx # 重启
systemctl reload nginx # 重载配置(不中断服务)
systemctl status nginx # 查看状态
systemctl enable nginx # 开机自启
systemctl disable nginx # 取消开机自启

# 查看服务日志
journalctl -u nginx # 某个服务的日志
journalctl -u nginx -f # 实时跟踪
journalctl -u nginx --since "1 hour ago"
journalctl -u nginx --since "2026-03-15" --until "2026-03-16"

# 查看启动耗时
systemd-analyze
systemd-analyze blame # 各服务启动耗时排序
systemd-analyze critical-chain # 关键路径

# 查看所有服务
systemctl list-units --type=service
systemctl list-units --type=service --state=failed

自定义 Service 文件

/etc/systemd/system/myapp.service
[Unit]
Description=My Application
# 在网络就绪后启动
After=network.target
# 依赖 redis
Requires=redis.service
After=redis.service

[Service]
Type=simple
User=app
Group=app
WorkingDirectory=/opt/myapp
ExecStart=/opt/myapp/bin/server --config /etc/myapp/config.yml
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5
# 优雅停止超时
TimeoutStopSec=30
# 资源限制
LimitNOFILE=65535
# 环境变量
Environment=NODE_ENV=production
EnvironmentFile=/etc/myapp/env

[Install]
WantedBy=multi-user.target
# 修改 service 文件后需要重新加载
systemctl daemon-reload
systemctl restart myapp

Service Type 类型

Type说明适用场景
simple默认,ExecStart 即主进程前台运行的程序
forking主进程 fork 后退出传统 daemon(需配 PIDFile)
oneshot执行完就退出初始化脚本
notify通过 sd_notify 通知就绪systemd-aware 程序
idle等其他任务完成后启动控制台输出

namespace 与 cgroup(容器底层)

namespace 和 cgroup 是 Linux 容器技术(Docker、K8s)的底层基础:

namespace(隔离)

namespace 提供进程级别的资源隔离:

Namespace隔离内容系统调用标志
PID进程 IDCLONE_NEWPID
Network网络栈CLONE_NEWNET
Mount文件系统挂载CLONE_NEWNS
UTS主机名和域名CLONE_NEWUTS
IPC进程间通信CLONE_NEWIPC
User用户和组CLONE_NEWUSER
Cgroupcgroup 根目录CLONE_NEWCGROUP
# 查看进程的 namespace
ls -la /proc/<PID>/ns/

# 使用 unshare 创建新 namespace
# 在新的 PID + Mount namespace 中运行 bash
unshare --pid --mount --fork /bin/bash

# 使用 nsenter 进入容器的 namespace
nsenter -t <PID> -m -u -i -n -p -- /bin/bash
# 这就是 docker exec 的底层原理

cgroup(资源限制)

cgroup(Control Group)限制进程组可使用的资源:

资源控制器说明
CPUcpu/cpuset限制 CPU 使用时间/核心
内存memory限制内存使用上限
I/Oblkio限制磁盘 I/O 带宽
网络net_cls标记网络包优先级
进程数pids限制进程数量
# cgroup v2(现代 Linux 默认)
# 查看 cgroup 层次
cat /proc/cgroups
ls /sys/fs/cgroup/

# 查看某个容器的资源限制
# Docker 容器的 cgroup 路径
cat /sys/fs/cgroup/system.slice/docker-<容器ID>.scope/memory.max
cat /sys/fs/cgroup/system.slice/docker-<容器ID>.scope/cpu.max

# 手动创建 cgroup 并限制资源(cgroup v2)
mkdir /sys/fs/cgroup/mygroup
echo "100000 100000" > /sys/fs/cgroup/mygroup/cpu.max # 限制 1 CPU
echo "536870912" > /sys/fs/cgroup/mygroup/memory.max # 限制 512MB
echo $$ > /sys/fs/cgroup/mygroup/cgroup.procs # 将当前 shell 加入
Docker 与 cgroup 的关系

Docker 使用 cgroup 实现 --memory--cpus 等资源限制参数。当容器内存超过 cgroup 限制时,内核的 OOM Killer 会杀死容器内的进程。

前台/后台任务管理

# 后台运行
command & # 后台执行
nohup command & # 后台执行且忽略 SIGHUP(终端关闭不影响)
nohup command > /dev/null 2>&1 & # 丢弃所有输出

# 任务管理
jobs # 查看后台任务
fg %1 # 将任务 1 放到前台
bg %1 # 将暂停的任务 1 放到后台继续
Ctrl+Z # 暂停当前前台任务

# screen/tmux(推荐方式)
screen -S mysession # 创建会话
screen -r mysession # 恢复会话
tmux new -s mysession # tmux 创建会话
tmux attach -t mysession # tmux 恢复会话

常见面试问题

Q1: 如何排查僵尸进程?

答案

# 1. 查找僵尸进程
ps aux | awk '$8=="Z" {print}'
# 或
ps -eo pid,ppid,stat,cmd | grep -w Z

# 2. 找到僵尸进程的父进程
ps -eo pid,ppid,stat,cmd | grep Z
# 然后根据 PPID 找父进程

# 3. 解决方案
# 方案 A:向父进程发 SIGCHLD,提醒它回收子进程
kill -SIGCHLD <PPID>

# 方案 B:杀掉父进程,让 init (PID 1) 接管并回收
kill <PPID>

# 4. 预防:代码中正确处理子进程退出
# - 调用 wait()/waitpid()
# - 注册 SIGCHLD 信号处理器

Q2: 如何查看系统的 CPU 和内存使用情况?

答案

# CPU
top # 实时监控
mpstat -P ALL 1 # 每秒显示每个 CPU 核心
uptime # 负载均衡(1/5/15 分钟)
cat /proc/loadavg # 负载

# 内存
free -h # 内存概览
cat /proc/meminfo # 详细内存信息
vmstat 1 # 虚拟内存统计

# 综合
sar -u 1 5 # CPU 使用率(5 次,间隔 1 秒)
sar -r 1 5 # 内存使用率

Q3: systemd 的 Restart 策略有哪些?

答案

说明
no不重启(默认)
on-success正常退出(exit 0)时重启
on-failure异常退出时重启(非 0 退出码、信号杀死、超时等)
on-abnormal被信号杀死或超时时重启
on-abort被未捕获信号杀死时重启
on-watchdog看门狗超时时重启
always任何退出情况都重启

生产推荐Restart=on-failure + RestartSec=5(5 秒后重启,避免频繁重启风暴)。

Q4: Docker 容器是如何实现资源隔离的?

答案

Docker 利用 Linux 内核的两大特性:

  1. namespace:提供 6 种隔离(PID、Network、Mount、UTS、IPC、User),让容器看到的是独立的进程空间、网络栈、文件系统
  2. cgroup:限制容器可使用的 CPU、内存、I/O 等资源上限

本质上容器就是一个有特殊隔离和限制的普通 Linux 进程,并非虚拟机。这也是容器比虚拟机轻量的根本原因。

Q5: 如何限制某个进程的 CPU 使用?

答案

# 方法 1:cgroup v2
mkdir /sys/fs/cgroup/limit_cpu
echo "50000 100000" > /sys/fs/cgroup/limit_cpu/cpu.max # 限制 50% CPU
echo <PID> > /sys/fs/cgroup/limit_cpu/cgroup.procs

# 方法 2:cpulimit 工具
cpulimit -p <PID> -l 50 # 限制 50% CPU

# 方法 3:nice/renice 调整优先级
nice -n 19 command # 最低优先级运行
renice 19 -p <PID> # 调整已运行进程的优先级

# 方法 4:systemd service 中限制
# CPUQuota=50%

相关链接