信号,kill和killall命令

-- TOC --

kill命令不是真的kill,而是发送一个信号给进程,默认信号为SIGTERM。我们先来看看什么是信号?

信号

信号是进程间的一种简单明了的通信机制,相对于其它通信方式,信号只发送一个数字,只是此数字被赋予了特别的含义。

signal is not semaphore! 信号不是信号量...

信号由操作系统内核(kernel)管理。

信号的产生方式多种多样,它可以是内核自身产生的,比如出现硬件错误(比如出现分母为0的除法运算,或者出现segmentation fault),内核需要通知某一进程;也可以是其它进程产生的,发送给内核,再由内核传递给目标进程。

内核中针对每一个进程都有一个表,存储着相关信息(房间的信箱)。

当内核需要将信号传递给某个进程时,就在该进程相对应的表中的适当位置写入信号,这样,就生成(generate)了信号。当该进程的系统调用完成后退出内核态时(或者调度切换时),会顺便查看信箱里的信息。如果有信号,进程会执行对应该信号的操作(signal action), 也叫做信号处理(signal disposition),此时叫做执行(deliver)信号。从信号的生成到信号的传递的时间,信号处于等待(pending)状态(纸条还没有被查看)。我们同样可以设计程序,让进程阻塞(block)某些信号,也就是让这些信号始终处于pending的状态,直到进程取消阻塞(unblock)或者无视信号。

进程可以自己给自己发信号!

进程收到信号后,有几个选项:

  1. 执行默认动作;
  2. 执行自定义动作;
  3. 忽略。(有几个信号的默认动作就是忽略)

学习$ man 7 signal,很详细。

The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

系统定义的五个信号默认动作:

Action Desc
Term Default action is to terminate the process.
Ign Default action is to ignore the signal.
Core Default action is to terminate the process and dump core (see core(5))
Stop Default action is to stop the process. (暂停运行)
Cont Default action is to continue the process if it is currently stopped.

The signal disposition is a per-process attribute: in a multithreaded application, the disposition of a particular signal is the same for all threads.

信号在进程级别处理。

Your shell is using a UNIX communication mechanism called a signal to communicate information to the process. When a process receives a signal it stops its execution, deals with the signal and potentially changes the flow of execution based on the information that the signal delivered. For this reason, signals are software interrupts.

信号是UNIX环境下进程间的一种通信机制,也被称为软中断

kill命令

kill - send a signal to a process

kill命令,用来给一个进程发信的。这个命令使用不复杂,但需要我们了解系统中的信号。

下面的命令,会列出系统中所有支持的信号名称及其编号:

$ 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

当我们在shell执行kill <pid>时,其实就是给此pid的process发送了一个SIGTERM信号,值15。

命令行的一些快捷键,实际上就是在给前台进程发信号:

快捷键 signal defalut action
Ctrl-C SIGINT(2) Term
Ctrl-\ SIGQUIT(3) Core
Ctrl-Z SIGTSTP(20) Stop

常用的Ctrl-D,不是发信号,而是EOF。

Ctrl-Z发送SIGTSTP,short for Terminal Stop (i.e. the terminal’s version of SIGSTOP)。

用户logout,terminal关闭,发出SIGHUP信号。

用kill命令,可以用来给进程发送更多不同的信号,比如发送SIGCONT来重新将一个暂停的进程运行起来:

$ ping maixj.net -c 4
PING maixj.net (114.215.183.12) 56(84) bytes of data.
^Z  # Ctrl-Z
[1]+  Stopped                 ping maixj.net -c 4
$ ps # get pid
    PID TTY          TIME CMD
  13621 pts/2    00:00:00 bash
  13944 pts/2    00:00:00 ping
  13946 pts/2    00:00:00 ps
$ kill -SIGCONT 13944
$ 64 bytes from 114.215.183.12: icmp_seq=1 ttl=128 time=17.3 ms
64 bytes from 114.215.183.12: icmp_seq=2 ttl=128 time=17.5 ms
64 bytes from 114.215.183.12: icmp_seq=3 ttl=128 time=17.4 ms
64 bytes from 114.215.183.12: icmp_seq=4 ttl=128 time=17.1 ms

--- maixj.net ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 28248ms
rtt min/avg/max/mdev = 17.108/17.332/17.457/0.138 ms

[1]+  Done                    ping maixj.net -c 4

kill -SIGCONT,可以让进程重新在后台运行。(如果想让进程重新在前台执行,请参考Job Control

killall命令

killall - kill processes by name, killall sends a signal to all processes running any of the specified commands. If no signal name is specfied, SIGTERM is sent.

process name,就是启动process的command line中的程序名称。

$ killall python3  # kill all python3 process

-w,等到所有进程都结束了才退回,返回命令提示符,有可能造成死等。

kill -9

到此,就可以很容易理解著名的命令kill -9 <pid>的含义,它给进程发SIGKILL信号,信号值为9。

Linux系统下的进程,可以忽略SIGTERM和SIGINT,但是SIGKILL是无法忽略的,是必须要执行的!这就是为什么kill -9著名的原因,常规方法(SIGINT或SIGTERM)杀不掉的进程,基本都可以用kill -9来强行杀掉。但是,我们也要谨慎使用SIGKILL,进程收到SIGTERM还可以保存数据然后做点清理工作后再自己死掉(需要进程自己接管SIGTERM),而收到SIGKILL就没有时间保存数据和做清理了,系统会把进程直接干掉。

kill -9 进程号
kill -KILL 进程号

killall -9 进程名称
killall -KILL 进程名称
killall -SIGKILL 进程名称

如何踢人?

kill掉用户登录的那个进程,就是踢人。

Ctrl-C是谁发出的

假设你在前台ping一个ip地址,一直ping,此时你按Ctrl-C,ping进程终止。这个Ctrl-C是谁发出的?

答:显然是shell发出的....

错!!

当ping进程在前台持续运行之时,你的键盘输入是关联到ping进程的标准输入(stdin),在这种情况下,shell根本无法获取你的按键信息。

实际上,是terminal获取了你的Ctrl-C组合键信息,并发送了SIGINT 信号。因为terminal处于更底层,它负责承载你所有的输入输出。因此,它当然可以截获用户的某个特殊的组合键,并执行某些特定的动作。(我理解,是pty中的master,将按键信息发给了pts)

类似的,还有发出暂停信号的Ctrl-Z(SIGTSTP)。

忽略SIGINT的Python脚本

import signal, time

def handler(signum, time):
    print("\nI got a SIGINT, but I am not stopping")

signal.signal(signal.SIGINT, handler)
i = 0
while True:
    time.sleep(.1)
    print("\r{}".format(i), end="")
    i += 1

Ctrl-C不能终止这个脚本,你可以使用Ctrl-\。

每次按Ctrl-C之后,程序的执行被暂停,handler运行,然后程序继续。

kill %

kill命令后面跟pid,也可以跟job id,如果是后者,要使用%前缀。这就就可以非常方便地给job发送任何信号了!

bash内置了一个kill命令,我们大部分时候使用的都是这个builtin kill。

本文链接:https://cs.pynote.net/sf/linux/shell/202111266/

-- EOF --

-- MORE --