Ctrl+C最基本的了解
相信对于大部分人来说知道ctrl+c同时按下,当前进程会收到SIGINT信号,这个是最基本的,尤其是在执行自己编译的c程序,或者python进程的时候,想关闭的时候直接摁下就退出了,对吧:) ,接下来写几个shell来测试一下不同条件下的执行结果
1 | !/bin/bash |
1 | 如下为第二种情况 |
1 | 第三种情况两个脚本 |
信号
在linux中信号用以向一个进程发送某种信息告知进程某种情况发生了,可以理解成是一种相对低级基本的进程间通信。
常用的信号有15个,可以通过kill -l来将所有信号列出来,常用的kill默认发送的是15,该信号告知进程退出,所以一般来说kill 都会杀死进程,同时信号可以被捕捉到,否则会按照操作系统内核设置进行信号的处理,其中有一个例外,那就是-9信号,该信号不能被捕捉且进程会立刻退出(不会进行必要的清理工作),因此信号9不到迫不得已不推荐使用,一种最普遍的常景就是MySQL这种服务,在卡死无响应的时候如果使用-9强制杀死,那么一般的情况就是索引由于没有正常关闭,发生损坏。
在学习操作系统的时候书中有介绍,一般情况使用进程友好的15来杀死进程,如果进程需要对特定的信号进行处理可以使用操作系统预留给用户使用的SIGUSR2、SIGUSR1
nginx 就是使用自定义信号进行日志的rotate,以及二进制文件的热更新
常用信号
常用的信号除了有以上的SIGKILL、SIGTERM之外目前还记得的比较特殊的就是SIGHUP,这个信号会挂起进程直至进程收到SIGCONT,当需要挂起暂停某个进程时,这两个信号尤其有用。
同时还有SIGABRT该信号会导致进程退出并产生core文件,如果没有记错的话,当进程发生段错误时就是由于操作系统像进程发送了该信号。印象中是这样
SIGINT与CTRL+C
正常情况下在shell中摁下ctrl+c的同时会向该进程组发送SIGINT,这里注意一下是进程组而不是像当前进程,这两个的区别就在于如果使用的是shell脚本产生了很多子进程,这种情况下如果子进程对SIGINT进行了捕获处理那么除了当前进程之外,其他进程也会退出。
这里可以写个代码验证一下,由于系统自带工具可能对信号做了处理,这里自己来编写测试,代码较少使用c实现,具体signal.h的使用可以在man中找到详细文档,这里不再赘述
1 |
|
1 |
|
1 | !/bin/env bash |
如下为执行结果,如果此时摁下ctrl+c可以看到test.sh退出,这个符合预期,同时catch_sig捕获到信号输出exiting。。。也输出了,执行ps可以看到sleep 120还在运行,同时向其发送kill -2无效,仍然运行
sh test.sh
running…
Tue Oct 24 15:30:52 UTC 2017
running…
running…
running…
Tue Oct 24 15:30:53 UTC 2017
running…
running…
Tue Oct 24 15:30:54 UTC 2017^Cexiting…
[root@alarm clang]# running…
running…
running…
running…
running…
running…
running…
running…
running…
running…
running…
从上面的结果可以看出,ctrl+c验证了向进程组发送信号这件事。
细心地同学一定发现了一件事情,那就是既然ignore_sig sleep忽略信号SIGINT,那为啥在shell中直接执行他们之后ctrl+c会杀死当前进程?
经过一番查阅资料,发现这个其实和shell本身的机制是有关系的,我们知道,其实在shell中执行命令,本质上是shell fork一个子进程,之后调用exec运行输入的命令,其实在这个过程中shell fork之后就设置了对于SIGINT信号的处理方法,那就是Terminate,因此这就解释了为什么在前台运行sleep 或者ignore_sig的时候如果调用kill -2 或者ctrl+c是可以杀死他们的,而在脚本中放置在后台则不会(因为脚本后台运行不能算作是shell fork出来的)
Signals by default are handled by the kernel. Old Unix systems had 15 signals; now they have more. You can check
</usr/include/signal.h>
(or kill -l). CTRL+C is the signal with nameSIGINT
.The default action for handling each signal is defined in the kernel too, and usually it terminates the process that received the signal.
All signals (but
SIGKILL
) can be handled by program.And this is what the shell does:
When the shell running in interactive mode, it has a special signal handling for this mode.
When you run a program, for example
1
2 > find
>
, the shell:
fork
s itself- and for the child set the default signal handling
- replace the child with the given command (e.g. with find)
- when you press CTRL+C, parent shell handle this signal but the child will receive it - with the default action - terminate. (the child can implement signal handling too)
You can
trap
signals in your shell script too…And you can set signal handling for your interactive shell too, try enter this at the top of you
~/.profile
. (Ensure than you’re a already logged in and test it with another terminal - you can lock out yourself)
1
2 > trap 'echo "Dont do this"' 2
>
Now, every time you press CTRL+C in your shell, it will print a message. Don’t forget to remove the line!
If interested, you can check the plain old
/bin/sh
signal handling in the source code here.At the above there were some misinformations in the comments (now deleted), so if someone interested here is a very nice link - how the signal handling works.
其他一些比较好的连接
- http://ajhaupt.blogspot.jp/2011/01/whats-difference-between-ctrl-c-and.html
- https://unix.stackexchange.com/questions/45426/why-would-ctrl-c-behave-differently-than-kill-2
- https://unix.stackexchange.com/questions/149741/why-is-sigint-not-propagated-to-child-process-when-sent-to-its-parent-process
- https://unix.stackexchange.com/questions/163561/control-which-process-gets-cancelled-by-ctrlc
- http://www.vidarholen.net/contents/blog/?p=34
转载请注明来源链接 http://just4fun.im/2017/10/22/信号与ctrl c/ 尊重知识,谢谢:)