Unix / Linux - 信号和陷阱

在本章中,我们将详细讨论 Unix 中的信号和陷阱。

信号是发送到程序的软件中断,表示发生了重要事件。 这些事件可能从用户请求到非法内存访问错误不等。 某些信号(例如中断信号)表示用户已要求程序执行一些不在通常控制流程中的操作。

下表列出了您可能会遇到并希望在程序中使用的常见信号 −

信号名称 信号编号 说明
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-CINTERRUPT 键

当您按下 Ctrl+C 键时,SIGINT 会发送到脚本并根据定义的默认操作终止脚本。

另一种传递信号的常用方法是使用kill 命令,其语法如下 −

$ kill -signal pid

这里的 signal 是要传递的信号的编号或名称,pid 是信号应该发送到的进程 ID。 例如 −

$ kill -1 1001

上述命令将 HUP 或挂断信号发送到正在以 进程 ID 1001 运行的程序。 要向同一进程发送终止信号,请使用以下命令 −

$ 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 程序开始,如果程序收到 2 号信号,work1$$dataout$$ 两个文件将被自动删除。

因此,如果用户在执行完这个trap后中断了程序的执行,可以放心,这两个文件都会被清理掉。 rm 之后的 exit 命令是必需的,因为如果没有它,程序将在收到信号时从中断点继续执行。

hangup 生成信号编号 1。 要么有人故意挂断信号,要么信号被意外断开。

在这种情况下,您可以修改前面的陷阱,通过将信号编号 1 添加到信号列表中来删除两个指定的文件 −

$ trap "rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 1 2

现在,如果线路挂断或按下 Ctrl+C 键,这些文件将被删除。

如果指定给 trap 的命令包含多个命令,则它们必须用引号引起来。 另请注意,shell 在执行 trap 命令时以及收到列出的信号之一时扫描命令行。

因此,在前面的示例中,WORKDIR$$ 的值将在执行陷阱命令时被替换。 如果您希望在收到信号 1 或 2 时发生此替换,您可以将命令放在单引号内 −

$ trap 'rm $WORKDIR/work1$$ $WORKDIR/dataout$$; exit' 1 2

忽略信号

如果为 trap 列出的命令为空,则指定的信号在接收到时将被忽略。 例如,命令 −

$ trap '' 2

这指定要忽略中断信号。 在执行您不想被打断的操作时,您可能希望忽略某些信号。 您可以指定要忽略的多个信号,如下所示 −

$ trap '' 1 2 3 15

请注意,必须为要忽略的信号指定第一个参数,并且不等同于编写以下内容,后者具有其自身的单独含义 −

$ trap  2

如果您忽略一个信号,所有子 shell 也会忽略该信号。 但是,如果您指定在收到信号时要采取的操作,则所有子 shell 仍将在收到该信号时采取默认操作。


重置陷阱

在您更改了接收信号时采取的默认操作后,如果您只是省略第一个参数,则可以使用陷阱再次将其更改回来; 所以 −

$ trap 1 2

这会将收到信号 1 或 2 时要采取的操作重置为默认值。