Linux【9】-进程管理2-2-捕获信号trap

有同事跟我反映,ctrl+c不好用,不能终止进程。本着探索的精神,我们想了解什么原因导致的

  • 在script的执行过程中,有时候希望能实时处理系统传来的信号,如用户按了Ctrl-C。处理的方式分为两种:接受信号或忽略信号。
  • 接受信号通常会对信号安排执行特定的命令,忽略信号主要是避免意外的状况干扰script的运行。像这种能谱捉特定信息,并做出反应的机制称为trap(陷阱触发)。trap的应用和信号有关。
  • trap命令用于指定在接收到信号后将要采取的动作。常见的用途是在脚本程序被中断时完成清理工作

例如:

	trap "exit 1" HUP INT PIPE QUIT TERM

表示当shell收到HUP INT PIPE QUIT TERM这几个命令时,当前执行的程序会读取参数“exit 1”,并将它作为命令执行。

一、信号

1.1 进程编号PID

每个进程都是独一无二的代码,称为进程编号(Process IDentification),简称PID。

ps auxw 或 ps -ef 列出内存所有进程
ps auxw | grep named | grep -v grep  找出包含named的行,排除包含grep 的行。

1.2 信号列表

历史上,shell总是用数字来代表信号,而新的脚本程序应该使用信号的名字,它们保存在用#include命令包含进来的signal.h头文件中,在使用信号名时需要省略SIG前缀。“信号”是指那些被异步发送到一个程序的事件。默认情况下,它们通常会终止一个程序的运行。

kill -l 或 trap -l 列出系统定义的信号的列表

每个信号都有个特定的数字代码,如SIGINT的代码为2(信号名称已SIG开头,可以不写,SIGINT和INT是相同的),信号名称不分大小写,在传递信号时,可用数字代码,也可使用信号名称。

“kill -1 进程号” 、“kill -HUP 进程号”、“kill -SIGHUP 进程号” 让此进程号的进程重启,3种写法效果一样。

1.3 传送信号的方法

“kill -信号 进程编号” 或 “kill -s 信号 进程编号” 或 “kill -n 信号 进程编号”

“-信号"可使用信号代码或信号名称。kill后可跟多个进程编号,kill会把指定的信号都传递给这些进程。如果省略”-信号"则默认传送SIGTERM(代码15,终止进程)信号给进程。

	kill 2359(终止这个进程) kill -9 2359(强制终止进制) kill -KILL 2359(强制终止进程)

另外killall也可以传送信号给进程,只不过killall传送的对象是进程的名称。

	killall -信号 进程名称

1.4 常用的信号

最好的方式是使用信号名称,因为生产环境不同的unix-like系统进程代码可能不一样,具体要用kill -l 或trap -l 查看

信号名称 信号数 描述
SIGHUP 1 本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。 登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
SIGINT 2 程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl C)时发出。
SIGQUIT 3 和SIGINT类似, 但由QUIT字符(通常是Ctrl /)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。以内核转储的方式离开进程
ABRT 6 放弃进程,并转存内核信息;中止,通常因某些严重的执行错误而引发
SIGFPE 8 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
SIGKILL 9 用来立即结束程序的运行. 本信号不能被阻塞, 处理和忽略。不执行清理动作,马上结束
SEGV 11 内存区段错误,并转存核心信息
SIGALRM 14 时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号。
SIGTERM 15 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理. 通常用来要求程序自己正常退出. shell命令kill缺省产生这个信号。结束进程前有机会执行清理动作,没有强制性(未必会结束)。
CONT 18 继续执行
STOP 19 暂停进程;后台休眠
TSTP 20 按Crtl-Z键;在终端中暂停进程。对进程发出TSTP信号,通常把一个程序丢到后台去执行 ,按Ctrl-Z暂停,在执行bg,把它丢到后台执行,fg再调回前台。

需要注意的是如果把第一个进程放后台后,再把第二个进程放后台,此时bg只显示第二个进程,待第二个进程结束的时候,bg才会显示第一个进程。可以使用jobs 来查看当前放进后台暂停的进程,用fg n n为编号可以调出指定的后台暂停的进程。

二、trap的运用

2.1 列出默认的信号

[sam@c04 ~]$ trap -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

2.2 列出目前trap向shell注册的信号列表

	trap -p

2.3 定义信号

trap “触发指令” 信号串行

只要系统产生特定信号,trap就会触发指令。

trap 'echo 123' ABRT ; kill -ABRT $$ 屏幕会出现123 $$代表当前shell进程编号

2.4 恢复信号原本的意义

trap - 信号串行

如果trap语法中没有触发指令,而是-或空白,则可恢复信号串行的原本的作用。trap '-' ABRT 恢复ABRT原本的作用。

例如:

[root@c04 ~]# trap -p
trap -- '' SIGINT
trap -- '' SIGQUIT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

[root@c04 ~]# trap SIGINT

[root@c04 ~]# trap
trap -- '' SIGQUIT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

2.5 让信号失效

trap '' 信号串行

	忽略系统发出的信号串行  trap '' INT Ctrl-C讲失效,无法终止进程

1,2,3,15信号失效

	trap '' 1 2 3 15

三、案例分析

3.1 示例

# 显示当前设置的trap
[root@new55 ~]# trap -p

# 定义INT信号的动作 
[root@new55 ~]# trap "echo hello" INT 
[root@new55 ~]# trap -p 
trap -- 'echo hello' SIGINT
[root@new55 ~]# trap -p INT 
trap -- 'echo hello' SIGINT
[root@new55 ~]# trap -p QUIT

# 试验设置的效果
[root@new55 ~]# Ctrl+C
[root@new55 ~]# hello 

3.2 脚本测试

下面的例子中,三个函数分别处理信号SIGINT、SIGTSTP和SIGTERM,

qingsong@db2a:/tmp$ cat signal.sh

#!/bin/bash
#filename: signal.sh

function handle_INT()
{
        echo I received signal : SIGINT, will do nothing
}

function handle_STP()
{
        echo I received signal : SIGTSTP, will do nothing
}

function handle_TERM()
{
        echo I received signal : SIGTERM. I will exit now
        exit
}

echo "My PID is $$"

trap 'handle_INT' SIGINT
trap 'handle_STP' SIGTSTP
trap 'handle_TERM' SIGTERM

while :
do
        sleep 1
done

qingsong@db2a:/tmp$ bash signal.sh 
My PID is 51100
^CI received signal : SIGINT, will do nothing <--当前session里按下了Ctrl+c
I received signal : SIGINT, will do nothing   <--另一个session里发出命令kill -SIGINT 51100
I received signal : SIGTSTP, will do nothing  <--另一个session里发出命令kill -SIGTSTP 51100
I received signal : SIGTERM. I will exit now  <--另一个session里发出命令kill -SIGTERM 51100
qingsong@db2a:/tmp$

3.3. 应用示例

当你按下 Ctrl + C 键或 Break 键在终端一个shell程序的执行过程中,正常程序将立即终止,并返回命令提示符。这可能并不总是可取的。例如,你可能最终留下了一堆临时文件,将不会清理。

捕获这些信号是很容易的,trap命令的语法如下:

	trap commands signals

这里的命令可以是任何有效的Linux命令,或一个用户定义的函数,信号可以是任意数量的信号,你想来捕获的列表。

在shell脚本中的陷阱有三种常见的用途:

  1. 清理临时文件
  2. 忽略信号

1、清理临时文件:

trap命令作为一个例子,下面展示了如何可以删除一些文件,然后退出,如果有人试图从终端中止程序:

trap "rm -f $WORKDIR/work1$$ $WORKDIR/dataout$$; exit" 2

执行shell程序,这个陷阱的角度,这两个文件work1$$ 和 dataout$$将被自动删除,如果程序接收信号数为2。

因此,用户中断执行,如果执行的程序后,这个陷阱你可以放心,这两个文件将被清理。 exit 命令如下。 rm 是必要的,因为没有它的执行将继续在节目中的一点,它离开时收到信号。

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

2、忽略信号

如果陷阱列出的命令是空的,指定的信号接收时,将被忽略。例如,下面的命令:

	$ trap '' 2

指定的中断信号是被忽略的。你可能要忽略某些信号时进行一些操作,不希望打断。可以指定多个信号被忽略如下:

	$ trap '' 1 2 3 15

四、我的案例

还是回到ctrl+c不能用的问题,因为ctrl+c 对应的SIGINT的信号,而我们通过 trap (trap -p) 可以看到现在被设置了成了"",相当于被禁用了,通过 trap SIGINT 重置了一下,然后就好了呀。

[root@c04 ~]# trap
trap -- '' SIGINT
trap -- '' SIGQUIT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU
[root@c04 ~]# trap SIGINT
[root@c04 ~]# pwd
/root
[root@c04 ~]# trap
trap -- '' SIGQUIT
trap -- '' SIGTSTP
trap -- '' SIGTTIN
trap -- '' SIGTTOU

现在的问题是,一旦重新登录,SIGINT又会被重新定义。。。。尴尬呀

参考资料

药企,独角兽,苏州。团队长期招人,感兴趣的都可以发邮件聊聊:tiehan@sina.cn
个人公众号,比较懒,很少更新,可以在上面提问题,如果回复不及时,可发邮件给我: tiehan@sina.cn