七、中断和中断处理-创新互联

7.1 中断七、中断和中断处理

中断使得硬件得以发出通知给处理器。中断本质上是一种特殊的电信号,由硬件设备发向处理器。处理器接收到中断后,会马上向操作系统反应此信号的到来,然后就由操作系统负责处理这些新到来的数据。硬件设备生成中断的时候并不考虑与处理器时间的同步——中断可以随时产生。因此内核随时可能因为新到来的中断而被打断。

让客户满意是我们工作的目标,不断超越客户的期望值来自于我们对这个行业的热爱。我们立志把好的技术通过有效、简单的方式提供给客户,将通过不懈努力成为客户在信息化领域值得信任、有价值的长期合作伙伴,公司提供的服务项目有:域名申请、网页空间、营销软件、网站建设、枣阳网站维护、网站推广。

异常:与中断不同,它在产生时必须考虑处理器同步。异常也常常成为同步中断。在处理器执行到由于编程失误而导致的错误指令或执行期间出现特殊情况(如缺页),必须靠内核来处理的时候,处理器就会产生一个异常。在x86体系结构上如何通过软中断实现系统调用,那就是陷入内核,然后引起一种特殊的异常——系统调用处理程序异常。

7.2 中断处理程序

在响应一个特定中断的时候,内核会执行一个函数,该函数叫做中断处理程序或中断服务例程。产生中断的每个设备都有一个中断处理程序。

在linux中,中断处理程序就是普通的c函数。只不过这些函数必须按照特定的类型声明,以便内核能够以标准的方式传递处理程序的信息,在其他方面,他们与一般的函数一样。中断处理程序与其他内核函数的区别:中断处理程序时被内核调用来响应中断的,他们运行于中断上下文内,该上下文中的执行不能阻塞。

中断可能随时发生,中断处理程序随时可能执行。所以必须保证中断处理程序能够快速执行,这样才能保证尽可能快的恢复中断代码的执行。中断处理程序要负责通知硬件设备中断已经被接收,中断处理程序还要完成大量的其他工作,例如,网络设备的中断处理程序除了要对硬件应答,还要把来自硬件的网络数据包拷贝到内存,对其进行处理后再交给合适的协议栈或应用程序。

7.3 上半部与下半部的对比

一般把中断处理切为两个部分或两半。中断处理程序是上半部——接收到一个中断,它就立即开始执行,但只做有严格时限的工作,例如对接收的中断进行应答或复位硬件,这些工作都是在所有中断被禁止下完成的。能够被允许稍后完成的工作会推迟到下半部。此后,在合适的时机,下半部会被开中断执行。

例如:当网卡接收来自网络的数据包时,需要通知内核数据包到了。网卡需要立即完成这件事,从而优化网络的吞吐量和传递周期,以避免超时。因此网卡立即发出中断,内核通过执行网卡已经注册的中断处理程序来做出应答。中断开始执行,通知硬件,拷贝最新的网络数据包到内存,然后读取网卡更多的数据包。这些都是重要、紧迫而又与硬件相关的工作。内核通常需要快速的拷贝网络数据包到系统内存,因为网卡上接收网路数据包的缓存大小固定,而且相比系统内存也要小得多。所以拷贝动作延迟的话必然会造成网卡的缓存溢出——进入的网络包占满了网卡的缓存,后续的包只能被丢弃。当网络数据包被拷贝到系统内存后,中断的任务算是完成了,这是它将控制权交还给系统被中断前原先运行的程序。处理和操作数据包的其他工作在随后的下半部中进行。

7.4 注册中断处理程序

int request_irq(unsigned int irq,irq_handler_t handler,unsigned long flags,const char *name,void *dev)

irq表示要分配的中断号

handler 是一个指针,指向处理这个中断的实际中断处理程序

7.4.1 中断处理程序标志

flags 可以为0,也可能是下列一个或多个标志的位掩码:

IRQF_DISABLED——该标志被设置后,意味着内核在处理中断处理程序本身期间,要禁止所有其他中断。如果不设置,中断处理程序可以与除本身外的其他所有中断处理程序同时运行

IRQF_SAMPLE_RANDOM——表示这个设备产生的中断对内核熵池有贡献。内核熵池负责提供从各种随机事件导出真正的随机数。

IRQF_TIMER——特别为系统定时器中断准备

IRQF_SHARED——表明可以在多个中断处理程序之间共享中断线。在同一个给定线上注册的每个处理程序必须制定这个标志;否则,在每条线上只能有一个处理程序。

name是与中断相关的设备的ASCII文本表示。

dev用于共享中断线。当一个中断处理程序需要释放是,dev将提供唯一的标识信息,以便从共享中断线的多个中断处理程序中找到指定的中断处理程序。

request_irq()函数可能会睡眠,因此,不能在中断上下文或其他不允许阻塞的代码中调用该函数。

7.4.2 一个中断例子

if(request_irq(irqn,my_handler,IRQF_SHARED,"my_device",my_dev)){

printk(KERN_ERR"my_device:cannot register IRQ %d ",irqn);

return -EIO;

}

7.4.3 释放中断处理程序

void free_irq(unsigned int irq,void *dev);

如果指定的中断线是不共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断是共享的,则仅仅删除dev对应的中断处理程序,而这条中断线本身只有在删除了最后一个中断处理程序后才会被禁用。

7.5 编写中断处理程序

static irqreturn_t intr_handler(int irq,void *dev);

irq:中断号

dev:与中断处理程序注册是传递的dev一致

irqreturn_t:当中断处理程序检测到一个中断,但该终端对应的设备并不是在注册是指定的产生源时,返回IRQ_NONE;当中断处理程序被正确调用,且确实是它所对应的设备产生了中断时,返回IRQ_HNADLED。中断处理程序通常会标记为static,因为它从来不会被别的文件中的代码直接调用。

重入和中断处理程序

linux中的中断处理程序时无须重入的。当一个给定的中断处理程序正在执行时,相应的中断线在所有处理器上都会被屏蔽,以防止在同一中断线上接收另一个新的中断。通常情况下,所有其他的中断都是打开的,所以这些不同中断线上的其他中断都能被处理,但当前中断线总是被禁止的。由此可以看出,同一个中断处理程序绝对不会被同时调用以处理嵌套的中断。

7.5.1 共享的中断处理程序

共享与非共享的差异:request_irq()参数flags必须为IRQF_SHARED;dev必须唯一;中断处理程序能够区分它的设备是否真的产生了中断。

指定IRQF_SHARED标志以调用request_irq()时,只能在:中断线当前未被注册、该中断线上的所有注册处理程序标志都为IRQF_SHARED;

内核接收一个中断后,他将依次调用在该中断线上注册的每一个处理程序。因此,一个处理程序必须知道它是否应该为这个中断负责。如果不是相关的设备产生了中断,则该中断处理程序应该马上退出。这需要硬件设备提供状态寄存器,以便中断处理程序进行检查。

7.5.2 中断处理程序实例

7.6 中断上下文

中断上下文具有较为严格的时间限制,因为它打断了其他代码。中断上下文中的代码应当迅速。

中断处理程序栈的设置是一个配置选项。曾经,中断处理程序不具有自己的栈。相反,他们共享所中断进程的内核栈。在2.6版早期的内核中,增加了一个选项,把栈的大小从两页减到一页。为了应对栈大小的减少,中断处理程序拥有自己的栈,每个处理器一个,大小为1页。这个栈就成为中断栈,尽管中断栈的大小是原先共享栈一半,但平均可用栈空间大的多,因为中断处理程序把这一页占为己有。

7.7 中断处理机制的实现

中断的旅程开始于预定义的入口点,类似于系统调用通过预定义的异常句柄进入内核。对于每条中断线,处理器都会跳到对应的一个唯一的位置。这样,内核就知道所接受的中断号。初始入口点只是在栈中保存这个号,并存放当前寄存器的值(这些值属于被中断的任务);然后,内核调用函数do_IRQ()。从这里开始,大多数中断处理代码用C编写的——但他们依然与体系结构相关。

unsigned int do_IRQ(struct pt_regs regs);

因为c的调用惯例是要把函数参数放在栈的顶部,因此pt_regs结构包含原始寄存器的值,这些值是以前在汇编入口例程中保存在栈中的。中断的值也会得以保存,do_IRQ()会将它提取出来。

计算出中断号后,do_IRQ()对所接受的中断进行应答,禁止这条线上的中断传递。接下来do_IRQ()需要确保在这条中断线上有一个有一个有效的处理程序,而且这个程序已经启动,但是当前并没有执行。然后do_IRQ()就调用handle_IRQ_event()来运行相应的中断处理程序。

ret_from_intr()检查重新调度是否正在挂起。如果重新调度正在挂起,内核正在返回用户空间(中断了用户进程),那么schedule()调用;如果内核正在返回内核空间(中断了内核),只有在preempt_count 为0时,schedule()才被调用,否则抢占内核是不安全的。在schedule()返回后,或者如果没有挂起的工作,那么,原来的寄存器被恢复,内核恢复到曾经中断的点。

7.8 /proc/interrupts

procfs是一个虚拟文件系统,它只存在与内核内存,一般安装于/proc目录。/proc/interrupts文件存放的是系统中与中断相关的统计信息。

7.9 中断控制

linux内核提供了一组接口用于操作机器上的中断状态。这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽掉整个机器一条中断线的能力。

锁提供保护机制,防止来自其他处理器的并发访问,而禁止中断提供保护机制,则是防止来自其他中断处理程序的并发访问。

7.9.1 禁止和激活中断

local_irq_disable();当前处理器

local_irq_enable();

不再使用cli(),cli()是能够禁止系统所有处理器上的中断。取消全局cli(),强制驱动程序编写者实现真正的枷锁,要知道特定目的细粒度锁比全局锁要快很多,而且也完全吻合cli()的使用初衷。其次,这也使得很多代码更具流线型,避免了代码的成簇布局。

7.9.2 禁止指定中断线

void disable_irq(unsigned int irq);保证已经开始的中断处理程序执行完毕

void disable_irq_nosync(unsigned int irq);不需保证执行完毕,直接禁止

void enable_irq(unsigned int irq);

void synchronize_irq(unsigned int irq);等待一个特定的中断处理程序退出。如果该处理程序正在执行,那么该函数必须退出后才能返回。

例如,如果disable_irq()被调用了两次,那么直到第二次调用enable_irq()后,才能真正地激活中断线。

7.9.3 中断系统的状态

irqs_interrupt() 如果本地中断被禁止,则返回非0

in_interrupt() 如果内核处于任何类型的中断处理中,它返回非0,说明内核此刻正在执行中断处理程序,或者正在执行下半部处理程序。

in_irq() 只有在内核确实正在执行中断处理程序时才返回0;


文章标题:七、中断和中断处理-创新互联
网页网址:http://pwwzsj.com/article/ddepoj.html