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

1、什么是中断?

1.1、什么是中断


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

硬中断处理流程:


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

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

3、设备发送一个中断到中断管理器;

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

5、CPU中断当前工作,开始处理中断,并通知操作系统。

6、操作系统调用中断处理程序。

中断的特点:

1、不同的设备对应不同的中断,并被用数字标识;

2、对应的设备需要对应的中断处理程序;

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

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

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

1.2、什么是异常


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

异常分为:


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

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

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

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

1.3、中断处理程序


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

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

1.4、中断上下文


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

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

1.6、中断下半部与软中断


中断下半部设计方案:


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

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

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

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

2、信号是对中断的模拟


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

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

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

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

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

1、signal

2、sigset

3、sigvec

4、sigaction(推荐)

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

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

2.1、不可靠信号和可靠信号


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

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

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

2.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.3、信号发送函数


1、int kill(pid_t pid, int signo)

2、int raise(int signo)

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

4、unsigned int alarm(unsigned int seconds)

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

6、void abort(void)

2.4、信号处理预置函数

2.5、信号处理函数编程

2.6、信号与中断的异同


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


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


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

3.1、异步信号安全、可重入与线程安全

3.2、化异步为同步

3.3、信号安全的系统调用

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


5、定时器


6、信号安全编程总结


7、信号、线程、进程、操作系统


8、参考资料


参考网上资源:


关于软中断: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的中断机制的阐述

时间: 2024-10-07 11:21:43

信号中断与异步信号中断安全编程的相关文章

Linux环境编程之信号(二):不可靠信号、中断的系统调用、可重入函数

(一)不可靠信号 对前面说的信号,是不可靠的,不可靠指的是信号可能会丢失:一个信号发生了,但进程却可能一直不知道这一点.另外,进程对信号的控制能力有限,只能捕捉信号或忽略它.有时用户希望通知内核阻塞一个信号:不要忽略它,在其发生时记住它,然后在进程做好准备时再通知它.这种阻塞信号的能力并不具备. 之前的版本中村咋一个问题:在进程每次接到信号对其进行处理时,随即将该信号动作复位为默认值.另一个问题是,在进程不希望某种信号发生时,它不能关闭该信号.进程能做的一切就是忽略该信号. (二)中断的系统调用

[学习笔记]信号基本概念(中断和信号)/名称及常用信号/信号处理/signal函数实践

1基本概念 中断 q  中断是系统对于异步事件的响应 q  中断信号 q  中断源 q  现场信息 q  中断处理程序 q  中断向量表 异步事件的响应:进程执行代码的过程中可以随时被打断,然后去执行异常处理程序 生活中的中断和计算机系统中的中断 1)  无中断生活场景 张三看书,厨房烧水 2)有中断的生活场景 张三看书,设置闹钟,厨房烧水. 闹钟发出中断信号,张三把书合好(第20页),去厨房把开水事情处理好,张三重新打开20页进行阅读. 3)计算机系统的中断场景 中断源发出中断信号,CPU判断

关于内核态用户态和信号的思考(其中中断上下段没有看懂)

终于搞懂用户态内核态以及中断.信号的上下文切换关系了,处于内核态的时候用户态的上下文保存在内核栈中,此时如果发生中断或者切换,是不会区分进程处于用户态还是内核态的,直接切之,软中断导致的是内核态和用户态的转化,也即是用户上下文到内核上下文的转化,而中断导致的是用户态或者内核态上下文到中断上下文的转化,进程切换导致的是用户态/内核态上下文到用户态/内核态上下文的转化.关键是每个进程都对应一个用户栈和内核栈. 扩展阅读: http://19880512.blog.51cto.com/936364/2

linux 异步信号的同步处理方式

关于代码的可重入性,设计开发人员一般只考虑到线程安全,异步信号处理函数的安全却往往被忽略.本文首先介绍如何编写安全的异步信号处理函数:然后举例说明在多线程应用中如何构建模型让异步信号在指定的线程中以同步的方式处理. 应用中编写安全的信号处理函数 在开发多线程应用时,开发人员一般都会考虑线程安全,会使用 pthread_mutex 去保护全局变量.如果应用中使用了信号,而且信号的产生不是因为程序运行出错,而是程序逻辑需要,譬如 SIGUSR1.SIGRTMIN 等,信号在被处理后应用程序还将正常运

Linux信号实践(1) --Linux信号编程概述

中断 中断是系统对于异步事件的响应, 进程执行代码的过程中可以随时被打断,然后去执行异常处理程序; 计算机系统的中断场景:中断源发出中断信号 -> CPU判断中断是否屏蔽屏蔽以及保护现场 -> CPU(查询中断向量表, 找到中断服务程序的入口地址)执行中断处理程序 ->(处理完中断之后) ->恢复现场,继续执行原来的任务 中断分类 硬件中断(外部中断) 外部中断是指由外部设备通过硬件请求的方式产生的中断,也称为硬件中断 软件中断(内部中断) 内部中断是由CPU运行程序错误或执行内部

linux可重入、异步信号安全和线程安全

一 可重入函数 当一个被捕获的信号被一个进程处理时,进程执行的普通的指令序列会被一个信号处理器暂时地中断.它首先执行该信号处理程序中的指令.如果从信号处理程序返回(例如没有调用exit或longjmp),则继续执行在捕获到信号时进程正在执行的正常指令序列(这和当一个硬件中断发生是所发生的事情相似.)但是在信号处理器里,我们并不知道当信号被捕获时进程正在执行哪里的代码. 如果进程正使用malloc在它的堆上分配额外的内存,而此时由于捕捉到信号而插入执行该信号处理程序,其中又调用了malloc,这会

函数的可重入性、线程安全函数、异步信号安全函数

重入即表示重复进入,首先它意味着这个函数可以被中断,其次意味着它除了使用自己栈上的变量以外不依赖于任何环境(包括static),这样的函数就是purecode(纯代码)可重入,可以允许有该函数的多个副本在运行,由于它们使用的是分离的栈,所以不会互相干扰.,常见的情况是,程序执行到某个函数foo()时,收到信号,于是暂停目前正在执行的函数,转到信号处理函数,而这个信号处理函数的执行过程中,又恰恰也会进入到刚刚执行的函数foo(),这样便发生了所谓的重入.此时如果foo()能够正确的运行,而且处理完

字符驱动程序之——异步信号通知

为了使设备支持异步通知机制,驱动程序中涉及以下 3 项工作:1. 支持 F_SETOWN 命令,能在这个控制命令处理中设置 filp->f_owner 为对应进程 ID.不过此项工作已由内核完成,设备驱动无须处理.2. 支持 F_SETFL 命令的处理,每当 FASYNC 标志改变时,驱动程序中的 fasync()函数将得以执行.驱动中应该实现 fasync()函数.3. 在设备资源可获得时,调用 kill_fasync()函数激发相应的信号 应用程序:fcntl(fd, F_SETOWN, g

同步中断和异步中断区别

中断有两种,一种是CPU本身在执行程序的过程中产生的,一种是由CPU外部产生的.外部中断,就是通常所讲的“中断”(interrupt).对于执行程序来说,这种“中断”的发生完全是异步的,因为不知道什么时候会发生.CPU对其的响应也完全是被动的,可以通过“关中断”指令关闭对其的响应.由软件产生的中断一般是由专设的指令,如X86中的“INT n”在程序中有意产生的,是主动的,同步的.只要CPU执行一条INT指令,在开始执行下一条指令之前一定会进入中断服务程序.这种主动的中断称为“陷阱”(trap).