如果需要降低系统负载或是重启系统(如果进程行为失常,开始耗费过多资源),就得杀死进程。作为一种进程间通信机制,信号可以中断进程运行并强迫进程执行某些操作。这些操作就包括以受控的方式终止进程或立刻终止进程。

10.4.1 预备知识

信号能够中断正在运行的程序。当进程接收到一个信号时,它会执行对应的信号处理程序(signal handler)作为响应。编译型的应用程序使用系统调用kill生成信号。在命令行(或是shell脚本)中是通过kill命令来实现的。trap命令可以在脚本中用来处理所接收的信号。

每个信号都有对应的名字以及整数值。SIGKILL``(9)信号会立即终止进程。Ctrl+C会发送信号中断任务Ctrl+Z会发送信号将任务置入后台。

Ctrl+C发送的是SIGINT信号。它和SIGKILL信号的区别在于后者不能被捕获,也不能被忽略。

10.4.2 实战演练

(1) kill -l 命令可以列出所有可用的信号

  1. $ kill -l
  2. SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
  3. ...

(2) 终止进程

  1. $ kill PROCESS_ID_LIST
  2. kill 命令默认发送 SIGTERM 信号。进程 ID 列表中使用空格来分隔各个进程 ID

(3) 选项 -s 可以指定发送给进程的信号:

  1. $ kill -s SIGNAL PID

参数 SIGNAL 可以是信号名或编号。尽管信号的用途各种各样,但常用的其实也就是那么几个。

  • SIGHUP 1:对控制进程或终端的结束进行挂起检测(hangup detection)。
  • SIGINT 2:当按下Ctrl+C时发送该信号。
  • SIGKILL 9:用于强行杀死进程。
  • SIGTERM 15:默认用于终止进程。
  • SIGTSTP 20:当按下Ctrl+Z时发送该信号。

(4) SIGTERM 选项

我们经常需要强行杀死进程,这样做的时候要小心。这种做法立刻生效,根本没有机会保存数据或执行通常的清理工作。应该先尝试使用 SIGTERM,将SIGKILL留作最后一招:

  1. $ kill -s SIGKILL PROCESS_ID

也可以使用下面的命令执行清理操作:

  1. $ kill -9 PROCESS_ID

10.4.3 补充内容

Linux中还有其他一些可以发送信号或终止进程的命令。

kill 命令系列

kill命令以进程ID作为参数。killall命令可以通过名字来终止进程:

  1. $ killall process_name

选项-s可以指定要发送的信号。killall默认发送SIGTERM信号:

  1. $ killall -s SIGNAL process_name

选项-9可以依照名字强行杀死进程:

  1. $ killall -9 process_name

例如:

  1. $ killall -9 gedit

选项-u可以指定进程所属用户:

  1. $ killall -u USERNAME process_name

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

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

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

SIGNAL是信号编号。pkill不支持信号名,该命令的很多选项和kill一样。要了解更多详细信息,请参阅pkill的命令手册。

捕获并响应信号

设计良好的程序在接收到SIGTERM信号时会保存好数据,然后放心地结束(shut down cleanly)。trap命令在脚本中用来为信号分配信号处理程序。一旦使用trap将某个函数分配给一个信号,那么当脚本运行收到该信号时,就会执行相应的函数。

命令语法如下:

  1. trap 'signal_handler_function_name' SIGNAL_LIST

SIGNAL_LIST以空格分隔,它可以是信号编号或信号名。

下面是一个能够响应信号SIGINTshell脚本:

  1. #/bin/bash
  2. #文件名:sighandle.sh
  3. #用途:信号处理程序
  4. function handler()
  5. {
  6. echo Hey, received signal : SIGINT
  7. }
  8. # $$是一个特殊变量,它可以返回当前进程/脚本的进程ID
  9. echo My process ID is $$
  10. #handler是信号SIGINT的信号处理程序的名称
  11. trap 'handler' SIGINT
  12. while true;
  13. do
  14. sleep 1
  15. done
  1. [root@dev workspace]# ls -al sighandle.sh
  2. -rwxr-xr-x 1 root root 331 Jan 28 21:59 sighandle.sh
  3. [root@dev workspace]# ./sighandle.sh
  4. My process ID is 109
  5. ^CHey, received signal : SIGINT

在终端中运行该脚本。当脚本运行时,如果按Ctrl+C,就会显示一条消息,这是通过执行与信号关联的信号处理程序实现的。Ctrl+C会发出一个SIGINT信号。通过使用一个无限循环while来保持进程运行。这样就可以使它能够响应另一个进程以异步方式发送的信号。用来保持进程一直处于活动状态的循环通常称为事件循环(event loop)。如果给出了脚本的进程ID,我们可以用kill命令向其发送信号:

  1. $ kill -s SIGINT PROCESS_ID
  1. $ kill -s SIGINT 109

image.png
脚本sighandle.sh会在运行时输出自己的进程ID,或者也可以用ps命令找出它的进程ID。如果没有为信号指定信号处理程序,那么将会调用由操作系统默认分配的信号处理程序。一般来说,按下Ctrl+C会终止程序,因为这是操作系统提供的处理程序的默认行为。不过这里我们自定义的信号处理程序覆盖了默认的信号处理程序。

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