linux高级编程-信号(1)-信号的基本概念
-
中断的概念
中断指处理机处理程序运行中出现的紧急事件的整个过程.程序运行过程中,系统外部、系统内部或者现行程序本身若出现紧急事件,处理机立即中止现行程序的运行,
自动转入相应的处理程序(中断处理函数),待处理完后,再返回原来的程序运行,这整个过程称为程序中断。我举个简单的例子,一个人正在看书,突然有人在敲门,这个
时候他就要中断看书的动作去开门,等开门的动作完成之后就继续回去看书,这就是一次中断。
-
信号的概念
有很多人都说信号是软中断,但它的本质是对中断的一种模拟,为linux提供了一种处理异步事件的方法。
-
信号与中断的比较
相同点:
都支持异步通信方式
当检测出有信号或中断请求时,都暂停正在执行的程序而去执行相应的中断处理函数
中断处理完成之后都会继续执行之前的程序
信号中断都可以进行屏蔽
不同点:
中断有优先级,而信号没有优先级
中断处理程序是在内核空间运行的,而信号处理程序是在用户空间运行的
中断响应一般都是及时的,而信号通常都会延迟
-
信号的分类
信号分为可靠信号与不可靠信号,可靠信号又称为实时信号,非可靠信号又称为非实时信号
不可靠信号主要有以下两个问题
1. 每次信号处理完之后,就会恢复成默认处理(这个只在早期的unix系统中存在,现在的Linux对信号机制进行了改进,因此,不可靠信号主要是指信号丢失)
2. 存在信号丢失问题
在linux系统中的64个信号中,前31个信号都是不可靠信号,后面的都是可靠信号
-
信号的处理方式
信号的处理方式有三种,分别是默认处理、忽略处理、自定义处理
1. 信号的默认处理:
信号 | 动作 | 发生条件 |
SIGABRT | 终止+core | 当程序调用abort函数时,会产生该信号,程序异常终止 |
SIGALRM | 终止 | 当由alarm或setitimer函数设置的定时器超时时,会产生该信号 |
SIGBUS | 终止+core | 经常因为内存错误产生该信号 |
SIGCHLD | 忽略 | 当进程terminate或stopped的时候,该信号会发送给父进程。如果父进程需要知道子进程什么时候终止,父进程必须捕获该信号。通常在该信号的捕获函数中调用wait或waitpid获取子进程的pid和终止状态 |
SIGCONT | 忽略/继续 | 作业控制命令进程继续执行时,该信号发送给进程。如果进程之前已被停止,则该信号的默认处理方式是继续进程的执行;否则,忽略该信号 |
SIGFPE | 终止+core | 当发生算术错误(如:除零,溢出等)时,产生该信号 |
SIGHUP | 终止 | 当终端界面检测到连接断开时,内核向与控制终端的session leader进程发送该信号(当且仅当终端的CLOCAL标识位没有被设置时,才会发送该信号)。接收信号的session leader可能是后台进程,这与普通终端产生的信号不同,普通终端信号接收者是前台进程组。另外,当控制终端的session leader终止时,SIGHUP信号会发送到前台进程组。因为守护进程没有控制终端,通常不应该接收该信号的,所以这个信号常常被用作守护进程重新读取配置文件的信号 |
SIGILL | 终止 | 当处理器执行了非法指令时,产生该信号 |
SIGINT | 终止 | 当向终端输入终端键(Control+C或DELETE)时,终端产生SIGINT信号。该信号被发送到前台进程组。通常用来终止已运行的进程 |
SIGIO | 终止/忽略 | 该信号用来提供异步IO模式。当有IO可用时,产生该信号通知进程 |
SIGKILL | 终止 | 该信号给超级用户提供了终止任何进程的能力,通常通过kill函数或命令。该信号不能够被忽略或捕获 |
SIGPIPE | 终止 | 当向已经关闭读者的管道写数据时,会产生该信号。同样向未连接的SOCK_STREAM类型的socket写数据时,也会产生该信号 |
SIGPOLL | 终止 | 当指定的事件发生在可选择的设备上时,产生该信号 |
SIGPROF | 终止 | 由setitimer设置的间隔定时器超时会产生该信号 |
SIGPWR | 终止 | 当系统有UPS(Uninterruptible Power Supply,即电池)时,断电后使用电池,当电池电量低时,会产生该信号通知进程在1530秒内关闭 |
SIGQUIT | 终止+core | 当输入退出键(Control+\)时,终端将会产生SIGQUIT信号,该信号被传送到前台进程组 |
SIGSEGV | 终止+core | 非法内存引用时,产生该信号 |
SIGSTOP | 停止进程 | 作业控制信号,用来停止进程。该信号不能被忽略或捕获 |
SIGSYS | 终止+core | 非法系统调用时,产生该信号 |
SIGTERM | 终止 | kill函数默认发送的信号,用来终止进程 |
SIGTRAP | 终止+core | 系统定义的硬件错误。通常,在遇到调试断点时,将控制权传递给debugger |
SIGTSTP | 停止进程 | 当输入挂起键(Control+Z)时,终端产生该(交互式停止)信号停止进程。该信号被发送给前台进程组 |
SIGTTIN | 停止进程 | 当后台进程组中的进程要求从控制终端读取数据时,会产生该信号。有两个例外情况:1、要求读数据的后台进程忽略或阻塞了该信号,2、进程所属进程组是“孤儿”。在这两种情况下,不会产生该信号,否则,read会错误返回,并将errno设置为EIO |
SIGTTOU | 停止进程 | 当后台进程组中的进程要求写数据到控制终端时,会产生该信号。后台进程可以被允许写数据到控制终端。当不允许后台进程写数据到控制终端时,write会错误返回,并将errno设置为EIO。到有两个例外情况:1、要求写数据的后台进程忽略或阻塞了该信号,2、进程所属进程组是“孤儿”。在这两种情况下,不会产生该信号 |
SIGURG | 忽略 | 当网络连接(Socket)接收到带外数据(out-of-band data)时,会产生该信号 |
SIGUSR1 | 终止 | 用户自定义的信号 |
SIGUSR2 | 终止 | 用户自定义的信号 |
SIGVTALRM | 终止 | 由setitimer设置的虚拟定时器超时时,产生该信号 |
SIGXCPU | 终止+core/忽略 | 进程超过了CPU的软限制时,产生该信号 |
SIGXFSZ | 终止+core/忽略 | 进程超过了文件大小的软限制时,产生该信号 |
---|
2. 忽略信号
信号的忽略主要使用signal()函数,如下面代码所示:
1 void ignoreSignal() 2 { 3 // SIG_IGN : ignore signal 4 signal(SIGINT, SIG_IGN); 5 for (;;) {} 6 }
将忽略的信号重新设置为缺省的处理方式:
1 void defaultSignal() 2 { 3 // SIG_IGN : ignore signal 4 signal(SIGINT, SIG_DFL); 5 for (;;) {} 6 }
3. 自定义信号处理函数
信号的自定义处理函数,同样使用signal函数进行注册,代码如下所示:
1 /** 2 * @brief handle : User-defined signal processing function 3 * @param sig 4 */ 5 void handle(int sig) 6 { 7 std::cout << " handle signal ******* " << sig << std::endl; 8 } 9 10 int main(int argc, char *argv[]) 11 { 12 if (SIG_ERR == signal(SIGINT, handle)) { 13 return 0; 14 } 15 for (;;) {} 16 }