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 -C 或者 INTERRUPT key 当脚本正在执行时。
    当你按下 Ctrl+C 关键,一个 SIGINT 发送到脚本并根据定义的默认操作脚本终止。
    传递信号的另一种常用方法是使用 kill command,其语法如下 -
    
    $ kill -signal pid
    
    这里 signal 是要传递的信号的编号或名称,并且 pid是信号应发送到的进程 ID。例如 -
    
    $ kill -1 1001
    
    上述命令向正在运行的程序发送 HUP 或挂断信号 process ID 1001. 要向同一进程发送终止信号,请使用以下命令 -
    
    $ kill -9 1001
    
    这会杀死正在运行的进程 process 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 程序中执行此陷阱的点来看,这两个文件 work1$$dataout$$ 如果程序接收到第 2 号信号,将被自动删除。
    因此,如果用户在执行此陷阱后中断程序的执行,您可以放心,这两个文件将被清除。这exit 跟随的命令 rm 是必要的,因为没有它,程序将在接收到信号时停止的点继续执行。
    信号编号 1 是为 hangup. 要么有人故意挂断线路,要么线路意外断开。
    在这种情况下,您可以修改前面的陷阱以通过将信号编号 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 '' 2
    
    这指定中断信号将被忽略。在执行不想被中断的操作时,您可能希望忽略某些信号。您可以指定多个要忽略的信号,如下所示 -
    
    $ trap '' 1 2 3 15
    
    请注意,必须为要忽略的信号指定第一个参数,并且不等同于编写以下内容,其具有单独的含义 -
    
    $ trap  2
    
    如果您忽略一个信号,则所有子 shell 也会忽略该信号。但是,如果您指定在接收到信号时要采取的操作,则所有子 shell 仍将在接收到该信号时采取默认操作。
  • 重置陷阱

    在您更改了接收信号时要采取的默认操作后,如果您只是省略第一个参数,则可以使用陷阱再次将其更改回来;所以 -
    
    $ trap 1 2
    
    这会将在接收到信号 1 或 2 时采取的操作重置为默认值。