Linux内核设计与实现 阅读笔记:7、中断和中断处理
 2016.04.08    |      linux内核设计与实现(原书第3版)    |     AilsonJack    |     暂无评论    |     1257 views
By: Ailson Jack
Date: 2016-04-08
个人博客: http://www.only2fire.com/
<p class="artical_littlestyle1">1、中断概述</p><p style="text-indent: 2em;">如何让处理器和外部设备协同工作,并且不会降低机器的整体性能呢?这里有两种方法:<br/></p><p style="text-indent: 2em;">轮询是其中之一的解决办法,但是这种方法可能会让内核做不少的无用功。<br/></p><p style="text-indent: 2em;">中断是另一种解决办法,即硬件在需要的时候向内核发出信号,此时内核会暂停当前正在处理的任务,去处理硬件发出的请求,待处理完成之后,接着执行之前的任务。<br/></p><p style="text-indent: 2em;">中断分为同步中断(异常)和异步中断(一般由硬件产生)。<br/></p><p style="text-indent: 2em;">硬件设备生成中断的时候并不考虑和处理器的时钟同步—换句话说就是中断随时可以产生。因此内核随时可能因为新到来的中断而被打断。不同的设备对应的中断不同,而每个中断都通过唯一的数字标志。这些中断值通常被称为中断请求(IRQ)线。每个IRQ线都会关联一个数值量。例如,在经典的PC机上,IRQ0是时钟中断,而IRQ1是键盘中断。</p><p class="artical_littlestyle2">2、中断处理程序</p><p style="text-indent: 2em;">在响应一个特定中断的时候,内核会执行一个函数,这个函数就叫做中断处理程序(interrupt handler)或者中断服务例程(interrupt service routine,ISR)。<br/></p><p style="text-indent: 2em;">一个设备的中断处理程序是它设备驱动程序的一部分—设备驱动程序是用于对设备进行管理的内核代码。<br/></p><p style="text-indent: 2em;">中断随时可能发生,因此中断处理程序也就随时可能执行。所以中断处理程序必须尽可能快的被执行完,同时中断打断了系统其它任务的执行,如果中断处理程序运行的时间过长,那么系统的整体性能也就会下降。<br/></p><p style="text-indent: 2em;">下面来看一个例子,假设某个中断需要处理的工作量比较大,比如网卡的中断处理程序,它在接收到中断之后,除了对硬件做出应答,还需要把硬件的网络数据包拷贝到内存,然后对其进行处理再交给合适的协议栈或应用程序。显然,这种工作量不会太小。<br/></p><p style="text-indent: 2em;">为了解决中断处理程序既想运行得快,又想完成的工作量多的需求,我们一般中断处理分成两个部分或两半。中断处理程序是上半部(top half)—接收到一个中断,它就立即开始执行,但只做有严格时限的工作,例如对接收的中断进行应答或者复位硬件,这些工作都是在所有中断被禁止的情况下完成的。能够被允许稍后完成的工作会被推迟到下半部(bottom half)去,此后,在合适的时机,下半部会被开中断执行。这里只关心中断的上半部也就是中断的处理程序,而对于中断的下半部将在第8章中进行说明。<br/></p><p class="artical_littlestyle3">3、中断处理相关函数</p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">1)、</span>注册中断处理程序<br/></p><p style="text-indent: 2em;">中断处理程序是管理硬件的驱动程序的一部分。驱动程序可以通过<span style="color: rgb(0, 112, 192);">request_irq()</span>函数注册一个中断处理程序(它被声明在文件&lt;linux/interrupt.h&gt;),并且激活给定的中断线,以处理中断,函数的定义如下:<br/><span style="color: rgb(0, 112, 192);">/*<br/>&nbsp;* irq&nbsp;&nbsp; – 表示要分配的中断号<br/>&nbsp;* handler – 实际的中断处理程序<br/>&nbsp;* flags&nbsp; – 标志位,表示此中断的具有特性<br/>&nbsp;* name &nbsp; – 中断设备名称的ASCII 表示,这些会被/proc/irq和/proc/interrupts文件使用<br/>&nbsp;* dev&nbsp;&nbsp; – 用于共享中断线,多个中断程序共享一个中断线时(共用一个中断号),依靠dev来区别各个中断程序<br/>&nbsp;* 返回值:<br/>&nbsp;* 执行成功:0<br/>&nbsp;* 执行失败:非0<br/>*/<br/>int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char* name, void *dev)</span><br/>其中flags常用的标志:<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">IRQF_DISABLED</span>:该标志被设置后,意味着内核在处理中断处理程序本身期间,要禁止所有的其它中断。如果不设置,中断处理程序可以与除本身外的其它任何中断同时运行。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">IRQF_TIMER</span>:该标志是特别为系统定时器的中断处理而准备的。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">IRQF_SHARED</span>:此标志表明可以在多个中断处理程序之间共享中断线。在同一个给定线上注册的每个处理程序必须指定这个标志;否则,在每条线上只能有一个处理程序。<br/></p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">注意:</span><span style="color: rgb(0, 112, 192);">request_irq()</span>函数可能会睡眠,因此,不能在中断上下文或者其他不允许阻塞的代码中调用该函数。还有一点很重要,初始化硬件和注册中断处理程序的顺序必须正确,以防止中断处理程序在设备初始化完成之前就开始执行。</p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">2)、</span>释放中断处理程序<br/></p><p style="text-indent: 2em;">卸载驱动程序时,需要注销相应的中断处理程序,并释放中断线,此时需要调用函数:<br/><span style="color: rgb(0, 112, 192);">void free_irq(unsigned int irq, void *dev)</span><br/></p><p style="text-indent: 2em;">如果指定的中断线不是共享的,那么,该函数删除处理程序的同时将禁用这条中断线。如果中断线是共享的,那么仅删除dev所对应的处理程序,而这条中断线本身只有在删除了最后一个处理程序时才会被禁用。<span style="color: rgb(255, 0, 0);">必须在进程上下文中调用free_irq()。</span></p><p style="text-indent: 0em;"><span style="background-color: rgb(118, 146, 60);">3)、</span>中断处理程序的声明<br/><span style="color: rgb(0, 112, 192);">/*<br/>&nbsp;* 中断处理程序的声明<br/>&nbsp;* @irq&nbsp;&nbsp;&nbsp; &nbsp; – 中断处理程序(即request_irq()中handler)关联的中断号<br/>&nbsp;* @dev&nbsp;&nbsp;&nbsp; &nbsp; – 与 request_irq()中的dev一样,表示一个设备的结构体<br/>&nbsp;* 返回值:<br/>&nbsp;* irqreturn_t&nbsp; –&nbsp; 执行成功:IRQ_HANDLED&nbsp; 执行失败:IRQ_NONE<br/>*/<br/>static irqreturn_t intr_handler(int, irq, void *dev)</span></p><p class="artical_littlestyle4">4、中断处理机制</p><p style="text-indent: 2em;">中断处理的过程主要涉及3个函数:<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">do_IRQ()</span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; — 与体系结构有关,对所接收的中断进行应答<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">handle_IRQ_event()</span> — 调用中断线上所有中断处理<br/></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">ret_from_intr()</span> &nbsp; — 恢复寄存器,将内核恢复到中断前的状态<br/></p><p style="text-indent: 2em;">中断从硬件到内核的路由如下图:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2018.08.26/1535215350205410.png" onclick="preview_image(&#39;/uploads/AilsonJack/2018.08.26/1535215350205410.png&#39;)"/></p><p class="artical_littlestyle1">5、中断控制</p><p style="text-indent: 2em;">Linux内核提供了一组接口用于操作机器上的中断状态。这些接口为我们提供了能够禁止当前处理器的中断系统,或屏蔽掉整个机器的一条中断线的能力,这些例程都是与体系结构相关的,可以在&lt;asm/system.h&gt;和&lt;asm/irq.h&gt;文件中找到。<br/></p><p style="text-indent: 2em;">常见的控制方法见下表:<br/></p><table><tbody><tr class="firstRow"><td style="word-break: break-all;" width="534" valign="top">函数<br/></td><td style="word-break: break-all;" width="534" valign="top">说明<br/></td></tr><tr><td style="word-break: break-all;" width="534" valign="top">local_irq_disable()<br/></td><td style="word-break: break-all;" width="534" valign="top">禁止本地中断传递<br/></td></tr><tr><td style="word-break: break-all;" width="534" valign="top">local_irq_enable()</td><td style="word-break: break-all;" width="534" valign="top">激活本地中断传递<br/></td></tr><tr><td style="word-break: break-all;" width="534" valign="top">local_irq_save()</td><td style="word-break: break-all;" width="534" valign="top">保存本地中断传递的当前状态,然后禁止本地中断传递</td></tr><tr><td style="word-break: break-all;" width="534" valign="top">local_irq_restore()</td><td style="word-break: break-all;" width="534" valign="top">恢复本地中断传递到给定的状态</td></tr><tr><td style="word-break: break-all;" width="534" valign="top">disable_irq()<br/></td><td style="word-break: break-all;" width="534" valign="top">禁止给定中断线,并确保该函数返回之前在该中断线上没有处理程序在运行<br/></td></tr><tr><td style="word-break: break-all;" width="534" valign="top">disable_irq_nosync()<br/></td><td style="word-break: break-all;" width="534" valign="top">禁止给定中断线<br/></td></tr><tr><td style="word-break: break-all;" width="534" valign="top">enable_irq()</td><td style="word-break: break-all;" width="534" valign="top">激活给定中断线</td></tr><tr><td style="word-break: break-all;" width="534" valign="top">irqs_disabled()<br/></td><td style="word-break: break-all;" width="534" valign="top">如果本地中断传递被禁止,则返回非0;否则返回0<br/></td></tr><tr><td style="word-break: break-all;" width="534" valign="top">in_interrupt()<br/></td><td style="word-break: break-all;" width="534" valign="top">如果在中断上下文中,则返回非0;如果在进程上下文中,则返回0<br/></td></tr><tr><td colspan="1" rowspan="1" style="word-break: break-all;" valign="top">in_irq()<br/></td><td colspan="1" rowspan="1" style="word-break: break-all;" valign="top">如果当前正在执行中断处理程序,则返回非0;否则返回0<br/></td></tr></tbody></table><p class="artical_littlestyle2">6、总结</p><p style="text-indent: 2em;">中断处理程序对处理时间的要求很高,如果一个中断要花费较长时间,那么中断处理一般分为2部分:<br/></p><p style="text-indent: 2em;">上半部只做一些必要的工作后,立即通知硬件继续自己的工作。<br/></p><p style="text-indent: 2em;">中断处理中耗时的部分,也就是下半部的工作,CPU会在适当的时候去完成。</p>
欢迎关注博主的公众号呀,精彩内容随时掌握:
热情邀请仔细浏览下博客中的广告,万一有对自己有用或感兴趣的呢。◕ᴗ◕。。
如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^,当然了能够赞赏博主,那就非常感谢啦!
注: 转载请注明出处,谢谢!^_^
暂无评论,要不要来个沙发
发表评论

 
Copyright © 2015~2023  说好一起走   保留所有权利   |  百度统计  蜀ICP备15004292号