Skip to content

信号处理

信号(Signal)是 Linux 进程间通信的一种机制,用于通知进程发生了某个事件。

常见信号

信号编号说明
SIGHUP1挂起(终端关闭)
SIGINT2中断(Ctrl+C)
SIGQUIT3退出(Ctrl+\)
SIGKILL9强制终止(不可捕获)
SIGTERM15终止(默认 kill 信号)
SIGSTOP17暂停进程(不可捕获)
SIGCONT18继续运行
SIGCHLD17子进程状态改变
SIGUSR110用户自定义信号 1
SIGUSR212用户自定义信号 2
bash
# 查看所有信号
kill -l

发送信号

bash
# 使用 kill 发送信号(默认 SIGTERM)
kill PID
kill -15 PID
kill -TERM PID

# 强制终止进程
kill -9 PID
kill -KILL PID

# 发送给进程组
kill -TERM -PID

# 按名称发送(杀死所有同名进程)
killall process_name
killall -9 nginx

# 更精确的名称匹配
pkill process_name
pkill -f "python script.py"   # 匹配完整命令行

捕获信号(trap)

trap 命令用于在 Shell 脚本中捕获信号并执行自定义处理:

bash
# 基本语法
trap '命令' 信号列表
trap '函数名' 信号列表

捕获 Ctrl+C(SIGINT)

bash
#!/bin/bash

trap 'echo "捕获到 Ctrl+C,正在退出..."; exit 1' INT

echo "按 Ctrl+C 测试信号捕获"
while true; do
    echo "运行中..."
    sleep 2
done

清理临时文件

bash
#!/bin/bash

TEMP_FILE="/tmp/myapp_$$.tmp"

# 注册清理函数
cleanup() {
    echo "清理临时文件..."
    rm -f "$TEMP_FILE"
    exit 0
}

trap cleanup EXIT INT TERM

# 创建临时文件
echo "临时数据" > "$TEMP_FILE"
echo "脚本运行中,按 Ctrl+C 退出"

sleep 30

忽略信号

bash
#!/bin/bash

# 忽略 SIGHUP(终端断开时不退出)
trap '' HUP

echo "此脚本不会因终端关闭而退出"
sleep 60

恢复默认信号处理

bash
#!/bin/bash

trap 'echo "收到信号"' INT

# 恢复默认处理(不再捕获)
trap - INT

sleep 10   # 此时 Ctrl+C 将正常终止脚本

多信号处理

bash
#!/bin/bash

handle_int() {
    echo "收到 SIGINT,优雅退出"
    exit 130
}

handle_term() {
    echo "收到 SIGTERM,执行清理"
    # 执行清理操作
    exit 0
}

handle_usr1() {
    echo "收到 SIGUSR1,重新加载配置"
    # 重新加载逻辑
}

trap handle_int INT
trap handle_term TERM
trap handle_usr1 USR1

echo "PID: $$,等待信号..."
while true; do
    sleep 1
done

常用 trap 模式

EXIT 钩子(脚本退出时必定执行)

bash
#!/bin/bash

on_exit() {
    local exit_code=$?
    echo "脚本退出,退出码:$exit_code"
    # 清理资源
    rm -f /tmp/lock_$$
}

trap on_exit EXIT

# 正常或异常退出都会触发 on_exit
echo "正在执行..."
exit 0

防止重复运行(锁文件 + 信号)

bash
#!/bin/bash

LOCK_FILE="/tmp/myapp.lock"

if [ -f "$LOCK_FILE" ]; then
    echo "脚本已在运行,退出"
    exit 1
fi

touch "$LOCK_FILE"
trap 'rm -f "$LOCK_FILE"' EXIT INT TERM

echo "脚本开始运行,PID: $$"
sleep 30

向进程发送 SIGUSR1 实现通信

bash
#!/bin/bash
# 守护进程示例:接收 SIGUSR1 重新加载配置

CONFIG_FILE="/etc/myapp.conf"

reload_config() {
    echo "$(date): 重新加载配置 $CONFIG_FILE"
}

trap reload_config USR1

echo "守护进程 PID: $$"
while true; do
    sleep 1
done

# 另一个终端执行:kill -USR1 <PID> 触发重载