### 信号是什么?
* 信号是一种很古老的IPC(进程间通信)方式。在早期的类Unix操作系统中,信号只能传递很简单的信息,即一个int(信号的编号)值。
* 信号是一种异步的消息传递方式或者说通知机制。
举一个不恰当的例子,你正在很兴奋地解决一个BUG,突然手机响了,女朋友说要分手。你立马放下手上的活,去处理女朋友那件事。手机随时都会响,随时都会中断你当下的事情。所以称之为异步事件。这个例子中的信号就体现在手机铃声响了。你去处理女朋友分手就是对信号做出的响应。
* 虽然信号是一种低级的IPC方式,但同时它保持了很简单的特性。在一些大型服务端程序中,很多时候也要考虑信号造成的影响。还是值得一学的。
从上面的例子可以看出,当产生一个信号(如:手机铃声响),之后就要去响应这个信号,如何响应这个信号则需要编程人员来写一个函数。当产生信号时,程序去执行之这个函数(如:处理女朋友分手)。
### Linux下信号分两种.
* 第一种: 用于内核向进程通知事件.即传统或者标准信号.(信号编号由1~31)
* 第二种: 实时信号(信号编号由34~64)
* 0号信号用来测试对应进程是否存在或者是否由权限给其发送信号
### 一般程序收到信号时进程的现象:
1. 忽略信号
2. 终止(杀死)进程
3. 产生核心转储文件,同时进程终止(核心转储文件即保存了程序跪掉的现场信息的文件,用来分析程序为何跪掉)。
4. 停止进程
5. 于之前暂停后再度恢复执行
### 编程可以做到的
1. 设置信号到来时采取默认的行为.
2. 忽略掉该信号.
3. 执行信号处理器程序.
### 常用信号类型即默认行为
|信号名称|产生的效果|对进程默认的效果|
|-----------|---------------------|----------------|
|SIGINT | Ctrl-C终端下产生|终止当前进程|
|SIGABRT|产生SIGABRT信号|默认终止进程,并产生core(核心转储)文件|
|SIGALRM|由定时器如alarm(),setitimer()等产生的定时器超时触发的信号|终止当前进程|
|SIGCHLD|子进程结束后向父进程发送| 忽略|
|SIGBUS|总线错误,即发生了某种内存访问错误|终止当前进程并产生核心转储文件|
|SIGKILL|必杀信号,收到信号的进程一定结束,不能捕获|终止进程|
|SIGPIPE|管道断裂,向已关闭的管道写操作|进程终止|
|SIGIO|使用fcntl注册I/O事件,当管道或者socket上由I/O时产生此信号|终止当前进程|
|SIGQUIT|在终端下Ctrl-\产生|终止当前进程,并产生core文件|
|SIGSEGV|对内存无效的访问导致即常见的“段错误”|终止当前进程,并产生core文件|
|SIGSTOP|必停信号,不能被阻塞,不能被捕捉|停止当前进程|
|SIGTERM|终止进程的标准信号 |终止当前进程|
0号信号用来测试对应进程是否存在或者是否有权限向对应进程发送该信号 (后面就理解了)
### 如何设计信号处理函数?
* 一般而言,信号处理函数设计的越简单越好,因为当前代码的执行逻辑被打断,最好尽快恢复到刚才被打断之前的状态。从而避免竞争条件的产生。
* 在信号处理函数中,建议不要调用printf等与I/O相关的函数。以及一些慢设备操作。这样会使得信号处理函数的执行时间变长,可能,操作系统就会切换其它程序去在CPU上执行。但如果有特殊需要,则也可以使用。
* 在信号处理函数中,不要使用任何不可重入的函数后面会说到。保证信号处理函数可以安全地执行完。并不会影响主逻辑执行。
### 信号的发送与处理
来简单看一个实例,看看信号最简单的使用方式(当键入Ctrl-C时候即程序收到SIGINT信号时进行处理。):
```C
// signal 的函数原型
// void (*signal(int sig , void (*func)(int)))(int);
// 至于这个怎么理解,这里就不再赘述了,请参考 《C Traps and Pitfalls》2.1节即理解函数声明。
// filename : simple_signal.cpp
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#define MSG "Catch signal SIGINT processing \n"
#define MSG_END "Finished process SIGINT return \n"
void do_too_heavy_work () {
long long s = 0 ;
for (long long i = 0 ; i < 500000000L ; i++ ) {
s += i ;
}
}
void sig_handler (int signuum ) {
// 本程序只是为了来进行演示,
// 在信号处理程序中,尽量不要调用与标准IO相关的和不可重入的函数。
write ( STDOUT_FILENO , MSG , strlen (MSG) ) ;
do_too_heavy_work();
write ( STDOUT_FILENO , MSG_END , strlen (MSG_END) ) ;
}
int main() {
// 注册信号处理函数
if ( SIG_ERR == signal ( SIGINT , sig_handler ) ) {
fprintf (stderr , "signal error ") , perror ("") ;
exit (1) ;
}
// 让主程序不退出,挂起,等待信号产生
while (1) {
pause () ;
}
return EXIT_SUCCESS ;
}
```
程序会一直停着等待用户行为。当我们键入Ctrl-C时程序打印相关信息,之后程序自己退出。那么程序的执行流程就类似这样:
```shell
[tutu@localhost Linux-Book]$ gcc simple_signal.cpp
[tutu@localhost Linux-Book]$ ./a.out
^CCatch signal SIGINT processing
Finished process SIGINT return
^CCatch signal SIGINT processing
Finished process SIGINT return
^CCatch signal SIGINT processing
Finished process SIGINT return
```
这是一种古老的注册信号并设置信号处理函数的方式。现在我们使用新的信号注册函数即sigaction函数。它提供了更多的控制字段(旧的signal已经使用sigaction进行了实现。祥见glibc源码,我自己的Fedora 22 glibc 版本2.21可以在glibc官网[Glibc](http://ftp.gnu.org/gnu/glibc/)下载Glibc源码,对应路径下glibc/glibc-2.21/sysdeps/posix/signal.c看到)包括屏蔽信号集,设置高级信号处理函数等(稍后会详细讲述,别担心).
#### 为什么不是用signal来进行信号注册了?
* signal 无法设置在执行信号处理程序时要屏蔽哪些信号的产生。
* signal 函数注册的信号处理函数只能携带很少的信息(也不常用),在信号处理函数进行信号处理时。
* signal 无法设置一些标志位来执行一些动作(后面再讲)。
* signal 只能设置所给信号的处理方式但sigaction还可以获取之前这个信号的处理方式
#### 废话这么多,大家都嫌弃了,来一个真实的例子吧,和上面的程序功能一样,但是使用sigaction进行处理。
```C
// filename : simple_sigaction.cpp
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#define MSG "Catch signal SIGINT processing \n"
#define MSG_END "Finished process SIGINT return \n"
void do_too_heavy_work () {
long long s = 0 ;
for (long long i = 0 ; i < 500000000L ; i++ ) {
s += i ;
}
}
void sig_handler (int signuum ) {
// 本程序只是为了来进行演示,
// 在信号处理程序中,尽量不要调用与标准IO相关的,不可重入的函数。
write ( STDOUT_FILENO , MSG , strlen (MSG) ) ;
do_too_heavy_work();
write ( STDOUT_FILENO , MSG_END , strlen (MSG_END) ) ;
}
int main() {
// 注册信号处理函数
struct sigaction newact ;
// 将信号处理函数执行期间掩码设置为空
sigemptyset (&newact.sa_mask ) ;
// 将标志设置为0即默认
newact.sa_flags = 0 ;
// 注册信号处理函数
newact.sa_handler = sig_handler ;
if ( 0 > sigaction ( SIGINT , &newact , NULL ) ) {
fprintf (stderr , "sigaction error ") , perror ("") ;
exit (1) ;
}
// 让主程序不退出,挂起,等待信号产生
while (1) {
pause () ;
}
return EXIT_SUCCESS ;
}
```
执行效果和刚才的一样,在这里就不再贴过来了。
#
妄北y
- 粉丝: 2w+
- 资源: 1万+
最新资源
- 基于springboot+vue的在线拍卖系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的学生网上请假系统设计与实现(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的新冠病毒密接者跟踪系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的新闻稿件管理系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的学生选课系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的疫苗发布和接种预约系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的在线商城系统设计与开发-代码(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的疫情打卡健康评测系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的智能物流管理系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的知识管理系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的在线文档管理系统的设计与实现(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的中小型医院网站(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的“衣依”服装销售平台的设计与实现(Java毕业设计,附源码,部署教程).zip
- 基于springboot的房屋租赁系统(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的车辆管理系统设计与实现(Java毕业设计,附源码,部署教程).zip
- 基于springboot+vue的it技术交流和分享平台的设计与实现(Java毕业设计,附源码,部署教程).zip
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈