Ctrl+C与Kill杀死进程的区别

在linux系统中,通过bash中输入<Ctrl+C>或者使用命令kill -9 $pid都可以杀死进程,但是它们有很大的不同。

先放结论:kill命令只会杀死目标进程,而bash快捷键则会杀死整个前台进程组!

不管使用那种方式,杀死进程都是通过发送信号(signal)来完成的,kill命令其实就是向目标pid进程发送信号:

  • kill -9 - 发送SIGKILL
  • kill -2 - 发送SIGINT
  • kill -15 - 发送SIGTERM

完整列表如下:

1
2
# kill -l
HUP INT QUIT ILL TRAP ABRT EMT FPE KILL BUS SEGV SYS PIPE ALRM TERM URG STOP TSTP CONT CHLD TTIN TTOU IO XCPU XFSZ VTALRM PROF WINCH INFO USR1 USR2

bash快捷键发送信号方式如下:

  • INT - <Ctrl+C>
  • KILL - <Ctrl+\>

会话是一个或多个进程组的集合,每登陆一个终端就相当于一个新会话,一个会话可以有一个前台进程组和多个后台进程组。

默认通过bash启动的程序,都会放在前台进程组,包括这个程序的子进程。

如果要放在后台进行组,可以使用&指定

1
echo 123 &            

(另外,只有前台启动才会绑定标准输入输出。)

在bash中通过<Ctrl+C><Ctrl+\>杀死进程,信号会被发送至前台进程组中的每一个进程。

而通过kill杀死程序,信号只会发送给目标pid进程。

程序1:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import (
    "time"
    "os/exec"
)

func main(){
    cmd := exec.Command("sleep", "100000")
    cmd.Start()
    time.Sleep(time.Second * 99999)
    return
}

上面的程序启动子进程运行sleep命令,然后睡眠。

测试结果:

  • 用 kill -9、-2、-15 分别杀死主进程,sleep子进程存活。
  • <Ctrl+C><Ctrl+\>分别杀死主进程,sleep子进程被杀。

程序2

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package main

import(
    "time"
    "os/exec"
    "syscall"
)

func main(){
    cmd := exec.Command("sleep", "100000")
    // 将子进程放入新的进程组
    cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true, Pgid: 0}
    cmd.Start()
    time.Sleep(time.Second * 99999)
    return
}

上面的程序同程序1一样,区别是给子进程设置了单独的进程组(此时子进程就不在前台进程组中了)。

测试结果:

  • 用 kill -9、-2、-15 分别杀死主进程,sleep子进程存活。
  • <Ctrl+C><Ctrl+\>分别杀死主进程,sleep子进程存活!