Unix / Linux - 信号和 Traps
在本章中,我们将详细讨论 Unix 中的 Signals 和 Traps。
Signals 是发送给程序的软件中断,用于指示已发生重要事件。这些事件可能从用户请求到非法内存访问错误不等。某些信号,如 interrupt signal,表示用户要求程序执行不在常规控制流中的操作。
下表列出了您在程序中可能遇到并想要使用的常见信号 −
| 信号名称 | 信号编号 | 描述 |
|---|---|---|
| SIGHUP | 1 | 控制终端检测到挂起或控制进程死亡 |
| SIGINT | 2 | 用户发送中断信号(Ctrl + C)时发出 |
| SIGQUIT | 3 | 用户发送退出信号(Ctrl + D)时发出 |
| SIGFPE | 8 | 尝试非法数学运算时发出 |
| SIGKILL | 9 | 进程收到此信号必须立即退出,不会执行任何清理操作 |
| SIGALRM | 14 | 闹钟信号(用于定时器) |
| SIGTERM | 15 | 软件终止信号(kill 默认发送) |
信号列表
有一种简单的方法可以列出系统支持的所有信号。只需执行 kill -l 命令,它将显示所有支持的信号 −
$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
不同系统如 Solaris、HP-UX 和 Linux 支持的信号列表有所不同。
默认动作
每个信号都有一个与之关联的默认动作。信号的默认动作是指脚本或程序收到信号时执行的操作。
一些可能的默认动作包括 −
终止进程。
忽略信号。
转储核心文件。这会创建一个名为 core 的文件,其中包含进程收到信号时的内存映像。
停止进程。
继续已停止的进程。
发送信号
有几种方法可以将信号发送给程序或脚本。最常见的方法之一是用户在脚本执行时输入 CONTROL-C 或 INTERRUPT key。
当您按下 Ctrl+C 键时,会向脚本发送 SIGINT,根据定义的默认动作,脚本将终止。
另一种常见的发送信号的方法是使用 kill 命令,其语法如下 −
$ kill -signal pid
其中 signal 是要发送的信号编号或名称,pid 是应接收信号的进程 ID。例如 −
$ kill -1 1001
上述命令向运行中 进程 ID 1001 的程序发送 HUP 或挂起信号。要向同一进程发送 kill 信号,请使用以下命令 −
$ kill -9 1001
这将终止运行中 进程 ID 1001 的进程。
捕获信号
当你在终端执行 shell 程序时按下 Ctrl+C 或 Break 键,通常该程序会立即终止,并返回命令提示符。这并不总是理想的。例如,你可能会留下一些不会被清理的临时文件。
捕获这些信号非常简单,trap 命令的语法如下 −
$ trap commands signals
这里的 command 可以是任何有效的 Unix 命令,甚至是用户定义的函数,而 signal 可以是你想要捕获的任意数量信号的列表。
在 shell 脚本中,trap 有两种常见用法 −
- 清理临时文件
- 忽略信号
清理临时文件
作为 trap 命令的示例,以下展示了如何在有人试图从终端中止程序时删除某些文件并退出 −
$ trap "rm -f $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 2
从 shell 程序中执行此 trap 的那一刻起,如果程序收到信号编号 2,这两个文件 work1$$ 和 dataout$$ 将被自动删除。
因此,如果用户在执行此 trap 后中断程序执行,你可以确保这两个文件会被清理。rm 后面的 exit 命令是必要的,因为没有它,程序会在收到信号时中断的位置继续执行。
信号编号 1 表示 hangup。可能是有人故意挂断线路,或者线路意外断开。
你可以通过将信号编号 1 添加到信号列表中,修改前面的 trap 以在这种情况下也删除指定的两个文件 −
$ trap "rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 1 2
现在,如果线路被挂断或按下 Ctrl+C 键,这些文件将被删除。
指定用于 trap 的命令如果包含多个命令,必须用引号括起来。另外请注意,shell 会在执行 trap 命令时以及在收到列出的信号之一时扫描命令行。
因此,在前面的示例中,WORKDIR 和 $$ 的值会在执行 trap 命令时被替换。如果你希望这种替换发生在收到信号 1 或 2 时,可以将命令放在单引号内 −
$ trap 'rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit' 1 2
忽略信号
如果为 trap 指定的命令为空,则收到指定的信号时将被忽略。例如,以下命令 −
$ trap '' 2
这指定忽略中断信号。在执行你不希望被中断的操作时,你可能想要忽略某些信号。你可以按以下方式指定多个要忽略的信号 −
$ trap '' 1 2 3 15
请注意,必须指定第一个参数才能忽略信号,这与以下写法不等价,后者有其自身独立的含义 −
$ trap 2
如果你忽略了一个信号,所有子 shell 也会忽略该信号。但是,如果你指定了收到信号时要采取的动作,所有子 shell 仍会采取该信号的默认动作。
重置 Trap
在你更改了收到信号时的默认动作后,可以通过在 trap 中省略第一个参数来将其重置为默认值;所以 −
$ trap 1 2
这将信号 1 或 2 收到时的动作重置为默认值。