Unix/Linux - 信号与陷阱



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

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

下表列出了您可能在程序中遇到并想要使用的常见信号:

信号名称 信号编号 描述
SIGHUP 1 检测到控制终端上的挂起或控制进程的死亡
SIGINT 2 如果用户发送中断信号 (Ctrl + C),则发出该信号
SIGQUIT 3 如果用户发送退出信号 (Ctrl + \),则发出该信号
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** 或 **中断键**。

按下 **Ctrl+C** 键时,会向脚本发送 **SIGINT**,并且根据定义的默认操作,脚本将终止。

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

$ kill -signal pid

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

$ kill -1 1001

上述命令将 HUP 或挂起信号发送到进程 ID 为 **1001** 的程序。要向同一进程发送 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

从执行此 trap 的 shell 程序中的这一点开始,如果程序接收到信号编号 2,则会自动删除 **work1$$** 和 **dataout$$** 这两个文件。

因此,如果用户在此 trap 执行后中断程序的执行,您可以确保这两个文件将被清理。 **rm** 后面的 **exit** 命令是必要的,因为如果没有它,执行将继续在程序中信号被接收时中断的地方继续。

信号编号 1 用于 **挂起**。有人故意挂断线路,或者线路意外断开。

您可以修改前面的 trap,在这种情况下也可以删除这两个指定的文件,方法是将信号编号 1 添加到信号列表中:

$ 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 1 2

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

广告