信号中断与异步信号中断安全编程

一、什么是中断?

1.一、什么是中断

外围设备的速度远低于CPU的速度,因此为提升CPU计算效率,现代计算机变内核主动为硬件主动,只在硬件须要的时候才发送信号,通知内核来处理数据。这样外围设备与内核的协做方式即为中断机制。而设备发送的信号即为中断,其本质为一种特殊的电信号。html

硬中断处理流程:

一、各外围设备与中断管理器各输入引脚相连;linux

二、中断管理器与CPU之间只存在一条中断管线;编程

三、设备发送一个中断到中断管理器;数组

四、中断管理器发送对应电信号给CPU。安全

五、CPU中断当前工做,开始处理中断,并通知操做系统。网络

六、操做系统调用中断处理程序。并发

中断的特色:

一、不一样的设备对应不一样的中断,并被用数字标识;异步

二、对应的设备须要对应的中断处理程序;函数

三、中断值即中断请求线(IRQ),被关联到不一样的数值量,如IRQ 0,中断亦可动态分配。性能

四、设备中断信号可能在任意时刻到来,不与CPU时钟同步,即异步硬件中断。

糕富帅CPU来到女儿国,女儿国的妹子们(中断集合)精心打扮,总在认为打扮完美的时刻向糕富帅抛媚眼露大腿扮性感,引发糕富帅的注意。糕富帅玩弄妹子的手段高超,经验丰富,与最靓的妹纸牵手,喜欢为不一样的妹子编号并制定不一样的攻略策略,老是上半场激烈,下半场缠绵,中场偷腥不断,并在腻味以后回归原始的浪荡生活。妹纸们老是很傻很天真,屡败屡战,不停地打扮本身,完美本身,期待着与糕富帅的从新开始。

 

1.二、什么是异常

若是程序出现编码错误、CPU自己故障和内存缺页等反常状况时,CPU将异常反馈给内核,调用异常处理程序进行处理。异常的产生必须与cpu时钟同步,又叫同步中断。

异常分为:

一、故障,执行处理函数后,恢复现场继续执行;

二、陷阱,用于调试,陷阱命令执行完毕后,内核将控制权返回给用户程序;

三、异常停止,用于报告严重错误,如硬件故障和系统表数值异常等,其异常处理函数将当即停止程序的执行;

四、编程异常,用于执行系统调用或向调试程序通报特定事件,即软中断,被当作陷阱处理。

 

1.三、中断处理程序

内核在响应一个中断时,将调用对应的特定函数,这个函数就是中断处理程序 。这个函数是按照特定类型声明的C函数,运行与中断上下文中。中断随时可能到来,为保证尽快恢复被中断代码的执行,中断处理程序必须保证快速执行。但中断处理程序可能要完成大量非瞬时工做(好比将网络数据拷贝到内存,并将预处理的数据转交给特定的协议栈),因此中断处理任务被两部分,上半部--中断处理程序实时响应中断并执行,下半部--完成可被退出处理的工做,在合适的时机到来后,下半部将开中断执行。

中断程序是硬件驱动程序的重要组成部分。硬件的驱动程序在初始化加载时,必须为其中断线注册对应的中断处理程序。同一中断线可能被对个设备共享,此时必须使用设备结构来区分。共享中断线的各设备,注册中断程序时必须使用IRQF_SHARED标志位。中断程序能够注销,若某中断线上全部诊断处理程序均已注销,则此条中断线被禁用。

 

1.四、中断上下文

内核在执行中断处理程序时,即处于中断上下文中。中断上下文有严格的时间限制。中断上下文时执行的代码,必须迅速并简洁,不适用循环处理繁杂任务,且不能睡眠或调用可能睡眠的函数(不然没法从新调度),以迅速恢复中断现场,防止打断其余代码(甚至其余中断线的中断程序)的执行。因此中断程序的上半部必须力求简约。

2.6版本以前的中断程序共享被中断进程的内核栈(无进程调度时,将使用空任务进程idle),2.6版本的linux为每一个CPU均分配了一个中断栈。内核将中断处理程序的入口点预约义在内存中,中断到来,内核跳到中断入口点,保存该中断号和当前寄存器的值,接着执行do_IRQ()禁止该中断线并屏蔽该CPU对任意中断的响应(防止中断的嵌套和重入问题),接着开始调用对应的中断处理C代码。

 

1.六、中断下半部与软中断

中断下半部设计方案:

一、BH静态全局链表:即bottom half,在全局范围内同步,共32个bottom halves组成,用一个32位数标识那个BH可执行。简单方便、死板、性能不够;

二、任务队列:即task queue,一组待处理函数组成的链表队列,驱动程序将下半部注册到队列中,内核依照队列循序处理。性能有提高,但不能彻底替换BH,没法处理高性能子系统;

三、软中断、tasklet和工做队列:2.3开发版本开始。软中断是静态定义的32个下半部接口(结构体数组),可在全部处理器上同时运行;tasklet基于软中断实现,可动态建立和加载,不一样类型的tasklet可同时在不一样cpu上运行,但同类型tasklet只能同时运行一个,因为其灵活性,tasklet是最多见的下半部实现;工做队列将下半部任务排队,在进程的上下文中执行,替代任务队列(2.5)。2.5版本后,BH被完全废弃。

四、内核定时器:将工做延期到某个肯定时间段去执行,适用于有时间要求的下半部。

 

二、信号是对中断的模拟

信号是进程处理异步事件的方法,信号是进程与内核、进程与进程通讯的一种方式,是进程接受外部控制的一种方式,好比:接受内核执行除零计算返回的SIGSEGV信号,父进程接受子进程的SIGCHLD,接受键盘驱动产生的SIGINIT信号等。进程对信号的默认处理方式:终止进程、终止进程+生成core文件、忽略和暂停进程等。软件编程人员能够在程序中设置信号处理函数,以响应外部信号。

信号即软件中断,它与软中断不一样,信号是软件程序的中断,软中断是内核的中断,但它们有许多的共同点,都将中断程序的运行并执行指定的函数,尤为是在使用中必须注意的同步和互斥问题,必须谨慎的使用临界区代码。为防止临界区代码对共享资源的并发访问,必须保证临界区代码的原子性。信号中断实际的中断点是在某个系统调用后,而不是用户函数。

当进程收到信号时,内核信号预处理函数do_singal()会将信号的处理句柄(对应信号处理函数)插入到用户进程的程序堆栈中,在当前系统调用结束并返回后,信号处理函数才会被pop并执行,最终返回执行用户程序。

对于慢速系统调用,慢速系统调用将进程运行状态置为TASK_INTERRUPTIBLE,可能在其为彻底执行完毕前,信号的到来将致使其提早返回(内核进程调度函数将进程运行状态更新为TASK_RUNNING,唤醒进程,直接将进程入可调度队列并执行)。4.2BSD为部分慢速系统调用设计了自动重启动功能,详细内容可参照APUE。

各种信号设置函数,支持的系统请参看APUE2.p304

一、signal

二、sigset

三、sigvec

四、sigaction(推荐)

旧signal提供的是不可靠的信号机制,sigaction提供的是可靠的信号机制,但当前大多数系统的signal均以sigaction实现,因此signal也是可靠的信号处理了。但依然建议只使用sigaction,除非系统不支持。

这些安装信号处理程序的函数所有支持:保持已安装的信号处理程序,并提供阻塞信号的功能。

 

2.一、不可靠信号和可靠信号

早期UNIX版本的信号,可能会丢失,对信号的控制能力也很是弱,不能阻塞信号。好比:早期接收信号并处理时,该信号的处理复位为默认,而从捕获信号到执行信号处理函数之间会有一个时间窗,此时若是信号再次到来时,进程可能被终止。有时咱们只想暂时阻塞信号,可是不要忽略信号,在进程准备好是再去获取并处理,可是早期的UNIX并不具有该功能。此时,信号处理是不可靠的。

在后期的4.2BSD和SVR3均提供了改进的信号处理机制,POSIX.1最终选择了BSD的可靠信号方案做为标准化的基础。

注:不要有这样的误解:由sigqueue()发送、sigaction安装的信号就是可靠的。事实上,可靠信号(实时信号)是指后来添加的新信号(信号值位于SIGRTMIN及SIGRTMAX之间);不可靠信号(非实时信号)是信号值小于SIGRTMIN的信号。信号的可靠与不可靠只与信号值有关,与信号的发送及安装函数无关。signal()及sigaction()都不能把SIGRTMIN之前的信号变成可靠信号(都不支持排队,仍有可能丢失,仍然是不可靠信号),并且对SIGRTMIN之后的信号都支持排队。这两个函数的最大区别在于,通过sigaction安装的信号都能传递信息给信号处理函数(对全部信号这一点都成立),而通过signal安装的信号却不能向信号处理函数传递信息。对于信号发送函数来讲也是同样的。

2.二、简单的信号处理程序

信号机制具备如下三方面的功能:
(1 )发送信号。发送信号的主要函数有:kill()、raise()、 sigqueue()、alarm()、setitimer()以及abort();
(2 )预置对信号的处理方式。接收信号的程序用 signal( ) 来实现对处理方式的预置;
(3 )收受信号的进程按事先的规定完成对相应事件的处理。

以下是一简单的信号处理程序,sigaction设置信号SIGINT的处理方式,用户经过按Crtl+C生成SIGINT信号给当前进程,程序收到信号后foo函数打印信息。

#include<stdio.h>
#include<unistd.h>
#include<signal.h>

void foo(int signum)
{
    printf("Hello signal(%d), i catch you!\n", signum);
}

int main()
{
    struct sigaction act, oact;

    act.sa_handler = foo;
    sigemptyset(&act.sa_mask);
    act.sa_flags = 0;

    if(sigaction(SIGINT, &act, &oact) < 0)
    {
        return (-1);
    }
    while(1)
    {
        sleep(1);
    }

    return 0;
}

 

2.三、信号发送函数

一、int kill(pid_t pid, int signo)

二、int raise(int signo)

三、int sigqueque(pid_t pid, int signo, const union sigval val)

四、unsigned int alarm(unsigned int seconds)

五、int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue))

六、void abort(void)

2.四、信号处理预置函数

 

2.五、信号处理函数编程

 

2.六、信号与中断的异同

信号与中断的类似点:
(1 )采用了相同的异步通讯方式;
(2 )当检测出有信号或中断请求时,都暂停正在执行的程序而转去执行相应的处理程序;
(3 )都在处理完毕后返回到原来的断点;
(4 )对信号或中断均可进行屏蔽。

信号与中断的区别:
(1 )中断有优先级,而信号没有优先级,全部的信号都是平等的;
(2 )信号处理程序是在用户态下运行的,而中断处理程序是在核心态下运行;
(3 )中断响应是及时的,而信号响应一般都有较大的时间延迟。

三、异步信号安全高级编程

 

3.一、异步信号安全、可重入与线程安全

 

3.二、化异步为同步

 

3.三、信号安全的系统调用

 

四、用信号安全的系统调用实现非安全usleep功能

五、定时器

六、信号安全编程总结

七、信号、线程、进程、操做系统

八、参考资料

参考网上资源:

关于软中断:http://www.ibm.com/developerworks/cn/linux/kernel/interrupt/index.html

信号入门与信号编程:http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index1.html        对信号相关的基础知识介绍的很全面

                                      http://www.ibm.com/developerworks/cn/linux/l-ipc/part2/index2.html        对信号编程作了较详细说明

                                      http://www.man7.org/linux/man-pages/man7/signal.7.html                             signal的man信息

                                      http://linux.chinaunix.net/techdoc/system/2006/06/04/933746.shtml            对信号的内核处理以及信号编程作了比较详细的描述

可重入、异步信号安全和线程安全:http://www.ibm.com/developerworks/cn/linux/l-cn-signalsec/  提供了异步信号转化为同步信号的方法

                                                            http://www.ibm.com/developerworks/cn/linux/l-reent.html       使用可重入函数进行更安全的信号处理

                                                            http://blog.sina.com.cn/s/blog_8fa7dd4101015hi5.html               可重入、线程安全、异步信号安全小结

参考书目:

W. Richard Stevens, Advanced Programming in the UNIX Environment(2 Edition)   对信号编程有介绍

赵炯,Linux内核彻底剖析——基于0.12内核                   详细分析了0.12内核下的信号和中断处理源码

Robert Love, Linux Kernel Development(3 Edition)  有对Linux的中断机制的阐述