天天看点

了解Linux信号

文章目录

          • 什么是信号
          • 信号的类型
          • 发送信号
          • 捕获信号
            • 清理临时文件
            • 忽略信号
            • 重置traps
          • 注意事项
          • 参考文档

本文根据参考文档翻译整理而来,概述了常见的信号以及如何在bash脚本中处理信号。

什么是信号

    信号是系统响应特定条件而产生的事件。

    当系统检测到软件事件时,可以向进程发送信号。信号可以由用户生成,也可以由进程生成。在发生硬件事件(比如硬件故障)或完成I/O事件时,内核也可以发送信号。

    信号与中断类似,区别在于中断由硬件产生,处理器中转并由内核处理,而信号由内核(也可能通过系统调用)中转并由进程处理。内核可以将中断作为信号传递给引发中断的进程,例如SIGSEGV,SIGBUS, SIGILL 和 SIGFPE。

    当一个进程收到一个信号时,它可以采取一个动作。标准信号有一个默认的配置,它决定了进程在接收信号后的行为。标准信号的默认配置可以是man手册中指定的以下选项之一:

  • Term 默认操作是终止进程。
  • Ign 默认操作是忽略信号。
  • Core 默认操作是终止进程并转储内核。这将创建一个名为core的文件,其中包含进程在接收到信号时的内存映像。
  • Stop 默认操作是停止进程。
  • Cont 默认操作是在进程当前停止时继续进程。

    当一个进程收到一个信号时,它可以选择执行这些操作之一:

  • 执行信号的默认配置
  • 通过设置信号屏蔽来阻塞信号
  • 使用信号系统调用将默认配置替换为自定义操作
信号的类型

    每一个信号都属于五类之一:

  • 进程控制
  • 资源控制
  • I/O通知
  • 硬件状态
  • 软件状态

    Linux支持很多信号。你可以在官方的手册页(man 7 signal)上找到很多关于它的信息。要查看系统上所有可用信号的列表,请运行 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
           

    以下是常见的信号:

    SIGHUP (1):如果某个进程正在从某个终端运行,而该终端意外关闭,则该进程接收到该信号。“HUP”是“hang up”的缩写并终止进程。如果不想在终端断开连接时挂断进程,请使用nohup启动进程。

    SIGINT (2):从键盘手动中断进程。当您点击Ctrl-C时,它会向正在运行的进程发送一个SIGINT信号并终止它。

    SIGQUIT(3):当用户在终端中键入 <ctrl>+\ 时,它将强制进程生成核心转储并终止进程。

    SIGKILL(9):强制终止进程。该信号不能被阻止或处理。当进程被SIGKILL信号终止,该进程不能执行清理。

    SIGPIPE (13):损坏的管道。如果一个进程在其输出中通过管道传输到另一个进程(producer | consumer),并且如果consumer死亡,那么producer将从consumer获得SIGPIPE信号,从而终止producer进程。

    SIGTERM (15):软件终止信号。这是kill命令默认发送的信号。

    SIGCHLD(17):子进程将此信号发送给其父进程,告知子进程已停止或终止。如果子进程在父进程调用 wait 系统调用之前终止,内核将尝试保留有关该进程的信息,以便父进程稍后可以调用wait来了解退出状态。子进程的这种状态,即进程终止但子进程的条目仍然存在于内核的进程表中,称为僵尸状态,该进程称为僵尸进程。

    SIGUSR1, SIGUSR2:为开发者保留的信号。

发送信号

    除了像 <ctrl>+\ 和 <ctrl>+c 这样的键盘信号之外,kill 命令还可以通过使用进程的PID向进程发送特定的信号。

# sends a SIGINT
kill -2 <pid> <pid>

# sends a SIGKILL
kill -9 <pid>

#sends a SIGQUIT
kill --signal SIGQUIT <pid>
           

    如果想删除同一类型的所有进程,那么killall就很方便了:

# kills all ssh processes
kill-all ssh
           
捕获信号

    当进程接收到特定信号时,用来做某事的命令是trap命令。在shell程序执行期间,当您在终端上按Ctrl+C或Break键时,通常该程序会立即终止,并返回命令提示符。有时这样并不好,例如,可能会留下一堆无法清理的临时文件。

    Shell脚本中的trap命令有两种常见用途:

  • 清理临时文件
  • 忽略信号

    trap命令有以下语法:

$ trap commands signals
           

    这里的command可以是任何有效的Unix命令,甚至是用户定义的函数,signal可以是要捕获的任意数量的信号的列表。

清理临时文件

    作为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的命令包含多个命令,则必须用引号括起来。还要注意,当trap命令执行时或接收到所列信号时,shell会同时扫描命令行。

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

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

忽略信号

    如果为trap列出的命令为空,则接收到的信号将被忽略。例如:

$ trap '' 2
           

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

$ trap '' 1 2 3 15
           

    请注意,必须为要忽略的信号指定第一个参数。

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

重置traps

    在更改了接收信号时要执行的默认操作之后,如果只是省略第一个参数,就可以使用trap再次更改它:

$ trap 1 2
           

    这将把接收到的信号1或2的动作重置为默认值。

注意事项

    不要使用SIGKILL(9),除非您已经优雅地尝试使用SIGTERM终止进程。

    关于trap的具体shell示例,可以参考Shell部分内置命令。

参考文档

[1]Sahitya Maruvada.Understanding Linux Process Signals[EB/OL].https://medium.com/100-days-of-linux/understanding-linux-process-signals-53d44c85c706,2020-05-21.

[2]www.tutorialspoint.com.Unix/Linux - Signals and Traps[EB/OL].https://www.tutorialspoint.com/unix/unix-signals-traps.htm,2020-01-01.

[3]Andries Brouwer.Signals[EB/OL].https://www.win.tue.nl/~aeb/linux/lk/lk-5.html,2003-02-01.

[4]Wikipedia.Signal (IPC)[EB/OL].https://en.wikipedia.org/wiki/Signal_(IPC),2020-12-23.