Unix/Linux - 信号与捕获

你好,未来的计算机巫师们!今天,我们将深入探讨Unix/Linux信号和捕获的迷人世界。如果你是编程新手,不用担心——我会一步一步引导你完成这次冒险,就像我多年来教导无数学生一样。那么,让我们一起踏上这段激动人心的旅程吧!

Unix / Linux - Signals and Traps

什么是信号?

想象你在一个忙碌的餐厅厨房里。厨师(我们称他为Unix大厨)需要快速地与他的员工沟通。他不会在嘈杂声中大喊,而是使用一套手势信号系统。在Unix/Linux世界中,信号的作用与此类似——是进程之间快速高效沟通的一种方式。

信号是发送给程序的软件中断,用以指示发生了重要事件。这些事件可能从用户请求到异常运行时发生。

信号列表

就像Unix大厨可能有不同的手势信号表示“多放盐”、“快点”或“休息一下”,Unix/Linux系统也有用于不同目的的各种信号。让我们看看一些最常见的信号:

信号名称 信号编号 描述
SIGHUP 1 在控制终端检测到挂起或控制进程死亡
SIGINT 2 来自键盘的中断(Ctrl+C)
SIGQUIT 3 来自键盘的退出(Ctrl+\)
SIGKILL 9 杀死信号(无法捕获或忽略)
SIGTERM 15 终止信号
SIGSTOP 17, 19, 23 停止进程(无法捕获或忽略)

默认行为

当一个信号发送给进程时,除非有其他指示,否则进程将采取默认行为。这些默认行为就像我们厨房员工的直觉反应。例如:

  1. 终止进程
  2. 忽略信号
  3. 转储核心
  4. 停止进程
  5. 继续停止的进程

发送信号

现在,让我们学习如何发送这些信号。在Unix/Linux中,我们使用kill命令向进程发送信号。不要被这个名字骗了——kill并不总是终止进程;这只是发送信号的一种方式。

下面是如何使用它的方法:

kill -信号名称 进程ID

例如,向进程1234发送SIGTERM信号:

kill -SIGTERM 1234

或者,使用信号编号:

kill -15 1234

捕获信号

如果我们的厨房员工能够决定如何响应Unix大厨的信号,而不是总是遵循默认反应,这在编程中就是信号捕获的作用。

在shell脚本中,我们使用trap命令来捕捉信号并指定接收到信号时要做什么。这是基本语法:

trap 命令 信号(们)

让我们看一个例子:

#!/bin/bash

trap "echo 你好!" SIGINT SIGTERM

echo "这是一个陷阱!"
while true
do
sleep 60
done

在这个脚本中,如果它接收到SIGINT或SIGTERM,它将打印"你好!"而不是终止。就像告诉我们的厨房员工:"当厨师发出停止信号时,说'你好!'而不是真的停止。"

清理临时文件

捕获的一个常见用途是在脚本退出前清理临时文件。这里是一个例子:

#!/bin/bash

# 创建一个临时文件
temp_file=$(mktemp)

# 设置一个捕获,退出时删除临时文件
trap "rm -f $temp_file" EXIT

# 使用临时文件
echo "你好,世界!" > $temp_file
cat $temp_file

# 当脚本退出时,临时文件将被自动删除

这个脚本创建了一个临时文件,向其写入数据,从中读取数据,然后在脚本退出时自动删除它,多亏了捕获。

忽略信号

有时,你可能想要忽略某些信号。在我们的厨房比喻中,这就相当于告诉厨师:"无论你多少次示意要更多盐,我都不会再加!"

下面是如何忽略一个信号的方法:

trap "" SIGINT

这告诉脚本在接收到SIGINT信号时什么也不做。

重置捕获

如果你在捕获信号后想要恢复默认行为,你可以这样重置捕获:

trap - SIGINT

这将移除对SIGINT的捕获,恢复默认行为。

这里有一个更完整的例子:

#!/bin/bash

# 初始捕获SIGINT
trap "echo 你不能阻止我!" SIGINT

echo "试着用Ctrl+C停止我..."
sleep 10

# 现在,重置捕获
trap - SIGINT

echo "好吧,现在你可以用Ctrl+C停止我了..."
sleep 10

echo "如果你看到这句话,说明你没有阻止我!"

这个脚本首先捕获SIGINT,然后在10秒后重置捕获。就像告诉我们的厨房员工:"在10秒内忽略停止信号,然后回到正常反应。"

那么,各位!我们已经穿越了Unix/Linux信号和捕获的土地。记住,熟能生巧。试着写你自己的脚本,玩转不同的信号,很快你就能像一个真正的指挥家一样指挥你的进程乐队。快乐的编码!

Credits: Image by storyset