Linux ctrl-c explained(ctrl-c 详解)

FingerLiu

目录

  • 1 背景姿势
  • 2 前戏
  • 3 正餐
  • 4 systemctl stop docker
  • 5 demo

1 背景姿势

驱动

驱动程序全称设备驱动程序,是添加到操作系统中的特殊程序,其中包含有关硬件设备的信息。此信息能够使计算机与相应的设备进行通信。

中断

触发系统将运行时间由用户态程序交还给内核态的一种方式。

终端

终端

伪终端(pseudo terminal) --> /dev/pts/8

会话

信号

发送给程序的来表示有重要事件发生的软件中断。
系统收到信号后,会把 CPU 时间交由内核态,然后再进行退出程序,挂起,恢复,或自定义操作。

常见信号

signals

信号 按键 意义
SIGINT Ctrl-C 退出当前 session 所有前台进程
SIGTSTP Ctrl-Z 挂起前台进程
SIGTERM 优雅杀掉指定进程,可被阻塞
SIGKILL 强制杀掉指定进程
Ctrl-D 不是 signal, write EOF to input

发送信号

kill -SIG pid

自定义信号

trap "echo 'signal SIGINT received'" SIGINT

延伸 Tips 1:

nohup 的由来:

nohup python3 manage.py runserver &

当终端中断时(拔网线,wifi 断网等),相关驱动程序会给当前会话
(session, 简单来说一个 login shell 就是一次会话)
中所有程序(前台和后台)发送 hang up(HUP) 信号,使程序退出。

延伸 Tips 2:

Ctrl- signal SIGQUIT
Ctrl+J实际上是一个换行符。
按下Ctrl+I与按下Tab键的效果相同。
Ctrl+[与ESC相同。
Ctrl+H代替BackSpace键。
鼠标右键快捷键:VK_APPS (93)


2 前戏 - linux 进程生命周期

process-flow


3 正餐

flow


4 systemctl stop docker

案发现场

耗子叔的一篇博客

reason

systemd 是个啥么玩意

系统的 init 程序,进程调度管理程序,id 为 1,所有进程的爹

systemctl 是systemd 的 shell interface,常见的操作: systemctl start|stop|status|reload

systemctl stop

docker service 的行为定义在/usr/lib/systemd/system/docker.service

systemd stop 的 man: the process is terminated by sending the signal specified in KillSignal=(SIGTERM) when service stop is requested.

dockerd trap

systemctl stop dockerd 执行后会给 dockerd 发送 SIGTERM 信号,dockerd 捕获到这个信号后,会去调用 cleanup 清理程序清理掉它下面的容器,同时计数器加一,当收到3次 SIGTERM 或 SIGINT(比如按下 ctrl-c)后,会出发 “force exit without cleanup”,强行退出 dockerd ,就会导致 dockerd 退出,但容器不退出,因而容器占用但资源(ip 地址等)不会被释放。此外,如果 kill -9,会发送 SIGKILL,强行杀掉 dockerd,也是不会清除容器但。所以慎用 ctrl-c ,慎用 kill -9 啊!!

// * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated.
// * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is
//   skipped and the process is terminated immediately (allows force quit of stuck daemon)
// * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit.
c := make(chan os.Signal, 1)
    // we will handle INT, TERM, QUIT, SIGPIPE here
    signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE}
    gosignal.Notify(c, signals...)
    go func() {
        interruptCount := uint32(0)
        for sig := range c {
            if sig == syscall.SIGPIPE {
                continue
            }

            go func(sig os.Signal) {
                logger.Info(fmt.Sprintf("Processing signal '%v'", sig))
                switch sig {
                case os.Interrupt, syscall.SIGTERM:
                    if atomic.LoadUint32(&interruptCount) < 3 {
                        // Initiate the cleanup only once
                        if atomic.AddUint32(&interruptCount, 1) == 1 {
                            // Call the provided cleanup handler
                            cleanup()
                            os.Exit(0)
                        } else {
                            return
                        }
                    } else {
                        // 3 SIGTERM/INT signals received; force exit without cleanup
                        logger.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received")
                    }
                case syscall.SIGQUIT:
                    DumpStacks("")
                    logger.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT")
                }

code is here.

延伸 Tips3:

如何删除进程和子进程:

进程和子进程在同一组中,可根据 PGID kill 掉一组进程

kill -- -$PGID   Kill using the default signal (TERM = 15)
kill -9 -$PGID   Kill using the KILL signal (9)

other methods


5 I want more

《Unix 环境高级编程》
Learn C!

阅读 7.1k

FingerLiu
先广后精。Explore the whole world,and then do one thing but do it best.

先广后精。Explore the whole world,and then do one thing but do it best.

562 声望
28 粉丝
0 条评论

先广后精。Explore the whole world,and then do one thing but do it best.

562 声望
28 粉丝
文章目录
宣传栏