4.2.4 信号机制概述:信号分类与默认行为

信号(signal)是内核向进程发送的异步事件通知机制,用于告知进程发生了特定事件或请求其采取动作。信号既是进程间通信手段,也是内核控制进程的重要方式。每个信号都有编号、名称与默认行为,进程可选择捕获、忽略或使用默认处理方式(部分信号不可被捕获或忽略)。

原理草图:信号流转与默认行为#

文章图片

信号分类#

  • 致命类(Terminating):默认终止进程,常用于异常或结束指令,如 SIGTERMSIGINTSIGHUP
  • 致命并生成核心转储(Core Dump):默认终止并生成 core 文件,便于排查,如 SIGSEGVSIGABRTSIGFPESIGILL
  • 停止类(Stop):使进程暂停运行,需 SIGCONT 恢复,如 SIGSTOPSIGTSTPSIGTTINSIGTTOU
  • 继续类(Continue):使停止的进程继续运行,如 SIGCONT
  • 用户自定义类(User-defined):预留给应用自定义用途,如 SIGUSR1SIGUSR2

默认行为与可处理性#

  • 可捕获/可忽略:大多数信号可通过处理函数或忽略改变默认行为。
  • 不可捕获/不可忽略SIGKILLSIGSTOP 由内核强制执行,常用于无法被应用拦截的强制终止或停止。
  • 默认行为可变:不同信号默认行为不同,应用应明确处理关键业务信号(如优雅退出、重载配置)。

常见信号与默认行为速览#

  • SIGTERM(15):请求终止,默认终止;适合优雅退出。
  • SIGKILL(9):强制终止,不可捕获/忽略。
  • SIGINT(2):终端中断(Ctrl+C),默认终止。
  • SIGHUP(1):挂起/重载,默认终止,常用于重载配置。
  • SIGQUIT(3):退出并生成 core。
  • SIGSEGV(11):段错误,终止并生成 core。
  • SIGABRT(6):异常终止,终止并生成 core。
  • SIGSTOP(19):停止进程,不可捕获/忽略。
  • SIGTSTP(20):终端停止(Ctrl+Z),可捕获/忽略。
  • SIGCONT(18):继续进程运行。

命令示例:查看信号与发送信号#

# 1) 查看系统支持的信号列表
kill -l

# 2) 启动一个示例进程(占位)
sleep 300 &
echo "PID=$!"

# 3) 发送优雅终止信号
kill -TERM <PID>

# 4) 发送强制终止信号(仅在无响应时使用)
kill -KILL <PID>

命令解释
- kill -l:列出信号名称与编号。
- kill -TERM <PID>:发送 SIGTERM,应用可捕获做清理。
- kill -KILL <PID>:发送 SIGKILL,内核强制终止。

示例:模拟停止与继续#

# 启动进程
sleep 300 &
PID=$!

# 发送停止信号
kill -STOP $PID

# 查看进程状态,T 表示停止
ps -o pid,stat,cmd -p $PID

# 继续运行
kill -CONT $PID

示例:自定义信号处理(bash)#

# 文件: /tmp/signal_demo.sh
#!/usr/bin/env bash
trap 'echo "收到 SIGTERM,执行清理"; exit 0' TERM
trap 'echo "收到 SIGUSR1,执行自定义动作"' USR1

echo "PID=$$,运行中..."
while true; do
  sleep 2
done
chmod +x /tmp/signal_demo.sh
/tmp/signal_demo.sh &
PID=$!

# 触发自定义信号
kill -USR1 $PID

# 优雅退出
kill -TERM $PID

运维视角要点#

  • 优雅退出:优先使用 SIGTERM,便于应用释放资源、写入状态。
  • 强制终止:仅在 SIGTERM 无效时使用 SIGKILL
  • 配置重载:许多服务将 SIGHUP 作为重载信号。
  • 诊断崩溃:core dump 类信号有助于定位程序缺陷。

排错清单#

  • 信号未生效:确认 PID 正确、权限足够(非 root 无法操作他人进程)。
  • 进程“无法杀死”:检查是否处于 D 状态(不可中断睡眠),需排查底层 I/O。
  • 重载无效:确认服务是否支持 SIGHUP,必要时查看官方文档。
  • core 不生成:检查 ulimit -ccore_pattern 配置。

练习#

  1. 使用 sleep 300 启动进程,分别发送 SIGSTOPSIGCONT,观察 ps 状态变化。
  2. 编写一个 trap 脚本捕获 SIGUSR1,并记录到日志文件中。
  3. 使用 kill -l 查询 SIGTSTP 的编号,并用编号方式发送信号。