9.3 杀死进程以及发送或响应信号

终结进程是我们通常都会碰到的事儿。有时,我们可能需要终结某个程序的所有实例。命令行提供了多种用于终结程序的方法。在类UNIX环境中与进程相关的一个重要概念就是信号。信号是一种进程间通信机制,它用来中断运行的进程以执行某些操作。终止程序也是通过使用信号技术来实现的。这则攻略介绍了信号及其用法。

9.3.1 新手上路

信号是Linux中的一种进程间通信机制。我们可以使用特定的信号来中断进程。每一种信号都同一个整数值相关联。当进程接收到一个信号时,它会通过执行对应的信号处理程序(signal handler)来进行响应。在shell脚本中同样可以发送、接收信号,并对其进行处理。KILL是用于终止进程的信号。像Ctrl+C、Ctrl+Z这种作业都属于信号。kill命令可用来向进程发送信号,而trap命令用来处理所接收的信号。

9.3.2 实战演练

列出所有可用的信号:

  1. $ kill -l

该命令会打印出信号数(signal number)和信号名称。

终止一个进程:

  1. $ kill PROCESS_ID_LIST

kill命令默认发出一个TERM信号。进程ID列表使用空格作为进程ID之间的定界符。

要通过kill命令向进程发送指定的信号,可以使用:

  1. $ kill -s SIGNAL PID

参数SIGNAL要么是信号名称,要么是信号数。尽管可以指定很多信号用于不同的目的,我们经常用到的其实只有少数几个,具体如下所示。

  • SIGHUP 1——对控制进程或终端进行挂起检测(hangup detection)。

  • SIGINT 2——当按下Ctrl + C时发送该信号。

  • SIGKILL 9——用于强行杀死进程。

  • SIGTERM 15——默认用于终止进程。

  • SIGTSTP 20——当按下Ctrl + Z时发送该信号。

我们经常要强行杀死进程,那么可以使用:

  1. $ kill -s SIGKILL PROCESS_ID

或者

  1. $ kill -9 PROCESS_ID

9.3.3 补充内容

让我们看看用于终止以及向进程发送信号的其他命令。

  • 杀死一组命令

kill命令以进程ID作为参数。在kill命令系列中还有其他命令可以接受命令名作为参数,并向对应的进程发送信号。

killall命令通过命令名终止进程:

  1. $ killall process_name

通过名称向进程发送信号:

  1. $ killall -s SIGNAL process_name

通过名称强行杀死进程:

  1. $ killall -9 process_name

例如:

  1. $ killall -9 gedit

通过名称以及所属用户名指定进程:

  1. $ killall -u USERNAME process_name

如果需要在杀死进程前进行确认,可以使用killall-i选项。

pkill命令和kill命令类似,不过默认情况下pkill接受的是进程名,而非进程ID。例如:

  1. $ pkill process_name
  2. $ pkill -s SIGNAL process_name

SIGNAL是信号数字。pkill不支持信号名称。

pkill提供了很多和kill相同的选项。要了解更多详细信息,请参阅pkill的命令手册。

  • 捕捉并响应信号

trap命令在脚本中用来为信号分配信号处理程序。一旦使用trap将某个函数分配给一个信号,那么当脚本运行时收到这个信号,对应于信号的函数就会开始执行。

命令语法如下:

  1. trap 'signal_handler_function_name' SIGNAL LIST

SIGNAL LIST以空格分隔,它可以是信号数字或者信号名称。

写一个能够响应信号SIGINT的shell脚本:

  1. #/bin/bash
  2. #文件名: sighandle.sh
  3. #用途: 信号处理程序
  4. function handler()
  5. {
  6. echo Hey, received signal : SIGINT
  7. }
  8. echo My process ID is $$
  9. # $$是一个特殊变量,它可以返回当前进程的进程ID
  10. trap 'handler' SIGINT
  11. #handler是信号SIGINT的信号处理程序的名称
  12. while true;
  13. do
  14. sleep 1
  15. done

在终端运行这个脚本。当脚本运行时,如果按Ctrl+C,就会显示一条消息,这是通过执行与信号关联的信号处理程序实现的。Ctrl+C就是一个SIGINT信号。

通过使用一个无限循环while来保持进程一直运行。这样就可以使它能够响应另一个进程以异步方式发送的信号。用来保持进程一直处于活动状态的循环通常称为事件循环(event loop)。如果不使用无限循环,脚本在执行完所有语句之后就会终止。对于信号处理程序脚本而言,它必须等待并响应信号。

我们可以用kill命令以及脚本的进程ID向脚本发送信号:

  1. $ kill -s SIGINT PROCESS_ID

上面的脚本在执行的时候会打印出 PROCESS_ID 。或者,你也可以用ps命令找出脚本的进程ID。

如果没有为信号指定信号处理程序,那么将会调用操作系统默认分配的信号处理程序。一般来说,按下Ctrl+C会终止程序,这是因为操作系统提供的处理程序的默认行为。但是,在这里我们自行定义的信号处理程序指定了在接收到信号后所执行的特定行为。

通过trap命令,我们能够为任意可用的信号(kill -l)定义处理程序,也可以为多个信号指定单个信号处理程序。