[Linux Shell学习系列十三]捕获-1.信号
D25
本章学习如何实现:
1)健壮的脚本,在被强制终结时具有清除任何临时生成的日志或文件的能力。
2)当脚本收到一个来自用户的中断时,应该采取什么样的行动。
1. Linux中的信号
在Linux或其他类Unix操作系统中,信号被用于进程间的通信。信号是一个发送到某个进程或同一进程中的特定线程的异步通知,用于通知发生的一个事件。现在被定义在POSIX标准中。
当一个事件发生时,会产生一个信号,然后内核会将事件传递到接收的进程。
有时,进程可以发送一个信号到其他进程;
其他:如文件大小达到限额、一个I/O设备就绪或用户发送了Ctrl+C或Ctrl+Z的终端中断等。
运行在用户模式下的进程会接收信号。
如果接收的进程正运行在内核模式,那么信号的执行只有在该进程返回到用户模式时才会开始。
发送到非运行进程的信息一定是内核保存,直到进程重新执行为止。
休眠的进程可以是终端的,也可以是不可中断的。
如果可中断休眠状态的进程收到了一个信号,内核会唤醒这个进程来处理信息。
如果一个不可中断休眠状态的进程收到了一个信号,内核会拖延此信号,直到该事件完成为止。
当进程收到一个信号时,可能发生3种情况:
1)进程可能忽略此信号。有些没有默认行为的信号,默认会被忽略。而有些信号不能被忽略。
2)进程可能会捕获此信号,并执行一个被称为信号处理器的特殊函数。
3)进程可能会执行信号的默认行为,如信号15(SIGTERM)的默认行为是结束进程。
当一个进程执行信号处理时,如果还有其他信号到达,那么新的信号会被阻断知道处理器返回为止。
2. 信号的名称和值
每个信号都以SIG开头命名,并定义为唯一的正整数。使用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
信号值被定义在/usr/include/bits/signum.h中,其源文件是/user/src/linux/kernel/signal.c。
在手册中查看信号名列表、信号值、默认行为和是否可以被捕获:
$ man 7 signal
下表中列出的信号是POSIX标准的一部分,通常被缩写成不带SIG前缀,如SIGHUP通常被简称为HUP。
| 信号 | 默认行为 | 描述 | 信号值 |
| SIGABRT | 生成core文件然后终止进程 |
这个信号告诉进程终止操作。 ABRT通常由进程本身发送,即进程调用abort()函数发出一个非正常终止信号时 |
6 |
| SIGALRM | 终止 | 警告时钟 | 14 |
| SIGBUS | 生成core文件然后终止进程 |
当进程引起一个总线错误时,BUS信号将被发送到进程。 如,访问了一部分未定义的内存对象 |
10 |
| SIGCHLD | 忽略 | 当子进程结束、被终端或在被中断后重新恢复时,CHLD信号会被发送到进程 | 20 |
| SIGCONT | 继续进程 | CONT信号指示操作系统重新开始先前被STOP或TSTP暂停的进程 | 19 |
| SIGFPE | 生成core文件然后终止进程 | 当一个进程执行一个错误的算术运算时,FPE信号会被发送到进程 | 8 |
| SIGHUP | 终止 | 当进程的控制终端关闭时,HUP信号会被发送到进程 | 8 |
| SIGILL | 生成core文件然后终止进程 | 当一个进程尝试执行一个非法指令时,ILL信号会被发送到进程 | 4 |
| SIGINT | 终止 | 当用户想要终端进程时,INT信号被进程的控制终端发送到进程 | 2 |
| SIGKILL | 终止 | 发送到进程的KILL信号会使进程立即终止。KILL信号不能被捕获或忽略 | 9 |
| SIGPIPE | 终止 | 当一个进程尝试向一个没有连接到其他目标的管道写入时,PIPE信号会被发送到进程 | 13 |
| SIGQUIT | 终止 | 当用户要求进程执行core dump时,QUIT信号由进程的控制终端发送到进程 | 3 |
| SIGSEGV | 生成core文件然后终止进程 | 当进程生成了一个无效的内存引用时,SEGV信号会被发送到进程 | 11 |
| SIGSTOP | 停止进程 | STOP信号指示操作系统停止进程的执行 | 17 |
| SIGTERM | 终止 | 发送到进程的TERM信号用于要求进程终止 | 15 |
| SIGTSTP | 停止进程 | TSTP信号由进程的控制终端发送到进程来要求它立即终止 | 18 |
| SIGTTIN | 停止进程 | 后台进程尝试读取时,TTIN信号会被发送到进程 | 21 |
| SIGTTOU | 停止进程 | 后台进程尝试输出时,TTOU信号会被发送到进程 | 22 |
| SIGUSR1 | 终止 | 发送到进程的USR1信号用于指示用户定义的条件 | 30 |
| SIGUSR2 | 终止 | 同上 | 31 |
| SIGPOLL | 终止 | 当一个异步输入/输出时间事件发生时,POLL信号会被发送到进程 | 23 |
| SIGPROF | 终止 | 当仿形计时器过期时,PROF信号会被发送到进程 | 27 |
| SIGSYS | 生成core文件然后终止进程 | 发生有错的系统调用时,SYS信号会被发送到进程 | 12 |
| SIGTRAP | 生成core文件然后终止进程 | 追踪捕获/断点捕获时,会产生TRAP信号 | 5 |
| SIGURG | 忽略 | 当有一个socket有紧急的或是带外数据可被读取时,URG信号会被发送到进程 | 16 |
| SIGVTALRM | 终止 | 当进程使用的虚拟计时器过期时,VTALRM信号会被发送到进程 | 26 |
| SIGXCPU | 终止 | 当进程使用的CPU时间超出限制时,XCPU信号会被发送到进程 | 24 |
| SIGXFSZ | 生成core文件然后终止进程 | 当文件大小超过限制时,会产生XFSZ信号 | 25 |
3. Bash中的信号
当没有任何捕获时,一个交互式Bash Shell会忽略SIGTERM和SIGQUIT信号。
由Bash运行的非内部命令会使用Shell从其父进程继承的信号程序。
如果没有启用作业控制,异步执行的命令会忽略除了有这些信号处理程序之外的SIGINT、SIGQUIT信号。
由于命令替换而运行的命令,会忽略键盘产生的作业控制信号SIGTTIN、SIGTTOU和SIGTSTP。
默认情况下,Shell接收到SIGHUP信号后会退出。
在退出之前,一个交互式的Shell会向所有的作业,不管是正在运行的还是停止的,重新发送SIGHUP信号。
对已停止的作业,Shell还会发送SIGCONT信号以确保它能够接收到SIGHUP信号。
如要阻止Shell对某个特定的作业发送SIGHUP信号,可以使用内部命令disown将它从作业表中移除,或使用disown -h阻止Shell向特定的作业发送SIGHUP信号,但不会将其从作业表移除。
disown命令:
#将sleep命令放在后台执行 $ sleep 30 & [1] 22545 #列出当前Shell下所有作业的信息 $ jobs -l [1]+ 22545 Running sleep 30 & #将作业1从作业表中移除 $ disown %1 #再次列出当前Shell下所有作业的信息 #可以看到已经没有了作业1 $ jobs -l #查找sleep进程 #可以看到sleep进程仍然存在 #此时,如果Shell接到SIGHUP信号,就不会向作业1发送SIGHUP信号
#此时如果我们退出Shell,这个作业仍将继续运行,而不会被终止 $ ps -ef | grep sleep user1 22545 22434 0 10:27 pts/0 00:00:00 sleep 30 #打印当前Shell的进程号 $ echo $$ 22434
disown -h:
#将sleep命令放在后台执行 $ sleep 30 & [1] 22548 #列出当前Shell下所有作业的信息 $ jobs -l [1]+ 22548 Running sleep 30 & #将作业1从作业表中移除 $ disown -h %1 #再次列出当前Shell下所有作业的信息 #可以看到作业1,但它已经被标记,即使Shell收到SIGHUP信号,也不会向它发送SIGHUP信号。
#因此,如果此时我们退出Shell,这个作业也仍将继续运行,而不会被终止 $ jobs -l [1]+ 22548 Running sleep 30 &
注:如果使用内部命令shopt打开了Shell的huponexit选项,当一个交互式登录Shell退出时,会向所有的作业发送SIGHUP信号。
本节结束

浙公网安备 33010602011771号