snifer

【原创】嵌入式系统中的中断及调用全解密

0
阅读(2990)

当一个设备发出中段请求时,CPU停止正在执行的指令,转而跳到包括中断处理代码或者包括指向中断处理代码的转移指令所在的内存区域。当中断处理完毕以后,CPU将恢复到以前的状态,继续执行中断处理前正在执行的指令。 


申请IRQ(Interrupt Requirement)的过程,分为3步:
1.将所有的中断线探测一遍,看看哪些中断还没有被占用。从这些还没有被占用的中断中选一个作为该设备的IRQ。
2.通过中断申请函数申请选定的IRQ,这是要指定申请的方式是独占还是共享。

3.根据中断申请函数的返回值决定是否重新申请或者放弃申请并返回错误。 


Linux系统的中断就是通常意义上的“中断处理程序”,它是直接处理由硬件发过来的中断信号。当Linux内核收到中断请求后,它首先判断中断源,然后调用相应的设备驱动程序, 驱动程序会去设备上查看其状态寄存器以了解发生了什么事情,并进行相应的操作。
Linux内核与中断相关的部分包括:硬件中断,下半部任务和内核线程
1、硬中断任务

硬中断是指那些由处理器以外的外设产生的中断,这些中断被处理器接收后交给内核中的中断处理程序处理。要注意的是:第一,硬中断是异步产生的,中断发生后立刻得到处理,也就是说中断操作可以抢占内核中正在运行的代码。这点非常重要。第二,中断操作是发生在中断上下文中的(所谓中断上下文指的是和任何进程无关的上下文环境)。中断上下文中不可以使用进程相关的资源,也不能够进行调度或睡眠。因为调度会引起睡眠,但睡眠必须针对进程而言(睡眠其实是标记进程状态,然后把当前进程推入睡眠列队),而异步发生的中断处理程序根本不知道当前进程的任何信息,也不关心当前哪个进程在运行,它完全是个过客。

2、下半部任务

下半部任务的由来完全出自上面提到的硬中断的影响。硬件中断任务(处理程序)是一个快速、异步、简单地对硬件做出迅速响应并在最短时间内完成必要操作的中断处理程序。硬中断处理程序可以抢占内核任务并且执行时还会屏蔽同级中断或其它中断,因此中断处理必须要快、不能阻塞。这样一来对于一些要求处理过程比较复杂的任务就不合适在中断任务中一次处理。例如在网卡接收数据的过程中,首先网卡发送中断信号通知CPU获取数据,然后系统从网卡中读取数据存入系统缓冲区中,再下来解析数据然后送入应用层。这些如果都让中断处理程序来处理显然过程太长,造成新来的中断丢失。因此Linux将这种任务分割为两个部分,中断处理程序,短平快地处理与硬件相关的操作(如从网卡读数据到系统缓存);而把对时间要求相对宽松的任务(如解析数据的工作)放在另一个部分执行,这个部分就是下半部任务。下半部任务是一种推后执行任务,它将某些不那么紧迫的任务推迟到系统更方便的时刻运行。内核中实现下半部的手段经过不断演化,目前已经从最原始的BH(bottom thalf) 衍生出tasklet,软中断softirq,BH,工作队列(work queues)。 

3、软中断操作
软中断softirq不象硬中断那样是由硬件中断信号触发执行的,所以也不同于硬件中断那样时随时都能够被执行。总的来讲,软中断会在内核处理任务完毕后返回用户级程序前得到处理机会。具体的讲,有三个时刻它将被执行do_softirq函数。硬件中断操作完成后,系统调用返回时,内核调度程序中。从中可以看出软中断会紧随硬中断处理,所以抢占内核任务至少在时钟中断后总有机会运行一次。还要记得软中断可以在不同处理器上并发执行。
使用时先open_softirq,需要出发此软中断时使用raise_softirq或cpu_raise_softirq。

软中断的执行也处于中断上下文中,所以中断上下文对它的限制是和硬中断一样的,一样不能进入阻塞状态。 

tasklet和bottom half都是建立在软中断之上的两种延迟机制,其具体不同之处在于软中断是静态分配的,而且同类软中断可以并发地在几个CPU上运行。tasklet可以动态分配,并且不同种类的Tasklets可以并发地在几个CPU上运行,但同类的Tasklets不可以。bottom half 只能静态分配。实际上,下半部分是一个不能与其它下半部分并发执行的高优先级tasklet,即使它们类型不同,而且在不同 CPU上运行。tasklet可以理解为软中断的派生,所以它的调度时机与软中断一致。
Tasklets与softirq的主要一个区别就是在同一时刻,只能用一个cpu来运行一个tasklet。而softirq就不然,可以在不同cpu上运行同一个softirq,但要注意做好相关的保护工作。而它与BH的区别是不同的Tasklets可以在同一时刻运行在不同的cpu上,而BH是不可以的。而在它的结构定义中,最重要的就是func成员,它所指的地址就是最终要执行的处理函数。对于内核中需要延迟执行的多数任务都可以利用tasklet来完成,由于同类tasklet本身已经进行了同步保护,所以使用 tasklet 相比软中断要简单得多,而且效率也不错。
bottom half 是Linux最早的内核延迟方法,它结构简单且容易控制,因为所有的BH处理程序都被严格地顺序执行,不允许任何两个BH处理程序同时并发执行,即使它们的类型不同也不可以,这样一来BH执行其间减少了许多同步保护。但是BH不得不被淘汰,因为它的"简便"牺牲了多处理器并发处理的高性能,等于一队人过独木桥那样速度受到牵制。

任务列队是BH的替代品,来自BH,所以它的属性也和BH相同。它的原意在于简化BH的操作接口,但它的随意性(数量随意、执行时机随意)却给系统带来了混乱,所以到今天已经被工作队列所取代。

这个就是嵌入式系统中全部的秘密。