Unix / Linux - 信号和陷阱


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

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

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

信号名称 信号编号 描述
叹息 1 控制终端检测到挂起或控制进程死亡
信号情报 2 如果用户发送中断信号 (Ctrl + C),则发出
信号退出 3 如果用户发送退出信号 (Ctrl + D),则发出
SIGFPE 8 如果尝试进行非法数学运算则发出
信号杀死 9 如果进程收到此信号,它必须立即退出并且不会执行任何清理操作
西格勒姆 14 闹钟信号(用于定时器)
信号术语 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$$这两个文件将被自动删除。

因此,如果用户在执行此陷阱后中断程序的执行,则可以放心这两个文件将被清除。rm后面的exit命令是必要的,因为如果没有它,程序将在收到信号时停止的位置继续执行。

为挂断生成 1 号信号。要么有人故意挂断线路,要么线路意外断开。

您可以修改前面的陷阱,通过将信号号 1 添加到信号列表中来删除本例中的两个指定文件 -

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

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

如果指定捕获的命令包含多个命令,则必须将其括在引号中。另请注意,shell 在执行 trap 命令时以及收到列出的信号之一时扫描命令行。

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

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

忽略信号

如果为陷阱列出的命令为空,则接收到的指定信号将被忽略。例如,命令 -

$ trap '' 2

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

$ trap '' 1 2 3 15

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

$ trap  2

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

重置陷阱

更改收到信号时要执行的默认操作后,如果您只是省略第一个参数,则可以使用陷阱再次将其更改回来;所以 -

$ trap 1 2

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