您好、欢迎来到现金彩票网!
当前位置:双彩网 > 向量屏蔽 >

中断概述 - 浙林龙哥 - 博客园

发布时间:2019-06-16 19:53 来源:未知 编辑:admin

  的对象。为什么要指向这个对象呢?因为当发生中断的时候,内核需要对相应的中断进行一些处理,比如屏蔽这个中断等。这个时候需要对中断设备

  irq_desc还有一个字段action指向对象irqaction,后者是产生中断的设备的处理对象,其中的handler就是处理函数。由于一个中断可以由多个设备发出,Linux内核采用轮询的方式,将所有产生这个中断的设备的处理对象连成一个链表,一个一个执行。

  例如,硬盘1,硬盘2都产生中断IRQx,在do_IRQ中首先找到irq_desc[x],通过字段handler对产生中断IRQx的设备进行处理(对8259而言,就是屏蔽以后的中断IRQx),然后通过action先后运行硬盘1和硬盘2的处理函数。

  2.在多CPU的机器上,采用APIC子系统来处理芯片,APIC有3个部分组成,一个是I/O APIC模块,其作用可比做8259芯片,但是它发出的中断信号会通过APIC总线送到其中一个(或几个)CPU中的Local APIC模块,因此,它还起一个路由的作用;它可以接收16个中断。

  中断可以采取两种方式,电平触发和边沿触发,相应的,I/O APIC模块的hw_irq_controller就有两种:

  (这里指的是intel的APIC,还有其它公司研制的APIC,我没有研究过)

  3. Local APIC自己也能单独处理一些直接对CPU产生的中断,例如时钟中断(这和没有使用Local APIC模块的CPU不同,它们接收的时钟中断来自外围的时钟芯片),因此,它也有自己的hw_irq_controller:

  startup是启动中断芯片(模块),使得它开始接收中断,一般情况下,就是将所有被屏蔽的引脚取消屏蔽

  ack当CPU收到来自中断芯片的中断信号,给相应的引脚的处理,这个各种情况下(8259, APIC电平,边沿)的处理都不相同

  将一个硬件处理函数挂到相应的处理队列上去(当然首先要生成一个irqaction结构):

  第二个参数就是action的dev_id,这个参数非常灵活,可以派各种用处。而且要保证的是,这个dev_id在这个处理链中是唯一的,否则删除会遇到麻烦。

  4.看这个结构上是否已经挂了其它处理函数了,如果有,则必须确保它本身和这个队列上所有的处理函数都是可共享的(由于传递性,只需判断一个就可以了)

  6.如果这个irq_desc只有它一个irqaction,那么还要进行一些初始化工作

  首先在队列里找到这个处理函数(严格的说是irqaction),主要靠dev_id来匹配,这时dev_id的唯一性就比较重要了。

  IRQ_REPLAY是指如果被禁止的中断号上又产生了中断,这个中断是不会被处理的,当这个中断号被允许产生中断时,会将这个未被处理的中断转为IRQ_REPLAY。

  IRQ_WAITING探测用,探测时,会将所有没有挂处理函数的中断号上设置IRQ_WAITING,如果这个中断号上有中断产生,就把这个状态去掉,因此,我们就可以知道哪些中断引脚上产生过中断了。

  具体的说,当内核在运行某个中断号对应的处理程序(链)时,状态会设置成IRQ_INPROGRESS。如果在这期间,同一个中断号上又产生了中断,并且传给CPU,那么当内核打算再次运行这个中断号对应的处理程序(链)时,发现已经有一个实例在运行了,就将这下一个中断标注为IRQ_PENDING,然后返回。这个已在运行的实例结束的时候,会查看是否期间有同一中断发生了,是则再次执行一遍。

  这些状态的操作不是在什么情况下都必须的,事实上,一个CPU,用8259芯片,无论即使是开中断,也不会发生中断重入的情况,因为在这期间,内核把同一中断屏蔽掉了。

  多个CPU比较复杂,因为CPU由Local APIC,每个都有自己的中断,但是它们可能调用同一个函数,比如时钟中断,每个CPU都可能产生,它们都会调用时钟中断处理函数。

  从I/O APIC传过来的中断,如果是电平触发,也不会,因为在结束发出EOI前,这个引脚上是不接收中断信号。如果是边沿触发,要么是开中断,要么I/O APIC选择不同的CPU,在这两种情况下,会有重入的可能。

  goto out;/*要么该中断没有处理函数;要么被禁止运行(IRQ_DISABLE);要么有一个实例已经在运行了*/

  的机制的目的和老版本的底半部分的目的是一致的,都是将某个中断处理的一部分任务延迟到后面去执行。

  值得一提的是,无论有多少个CPU,内核一共只有32个公共的softirq,但是每个CPU可以执行不同的softirq,可以禁止/起用不同的softirq,可以激活不同的softirq,因此,可以说,所有CPU有相同的例程,但是

  对(1)的判断是通过考察irq_stat[ cpu ].mask相应的位得到的。这里面的cpu指的是当前指令所在的cpu.在一开始,softirq被定义时,所有的cpu的掩码mask都是一样的。但是在实际运行中,每个cpu上运行的程序可以根据自己的需要调整。

  虽然原则上可以任意定义每个softirq的函数,Linux内核为了进一步加强延迟中断功能,提出了tasklet的机制。tasklet实际上也就是一个函数。在第0个softirq的处理函数tasklet_hi_action中,我们可以看到,当执行这个函数的时候,会依次执行一个链表上所有的tasklet.

  内核依次对32个softirq轮询,如果遇到一个可以执行并且需要的softirq,就执行对应的函数,这些函数有可能又会执行一个函数队列。当执行完这个函数队列后,才会继续询问下一个softirq对应的函数。

  在这个32个softirq中,有的softirq的函数会依次执行一个队列中的tasklet,如第一帖中图所示。

  count用来禁用(count = 1 )或者启用( count = 0 )这个tasklet.因为一旦一个tasklet被挂到队列里,如果没有这个机制,它就一定会被执行。这个count算是一个事后补救措施,万一挂上了不想执行,就可以把它置1。

  然后调用schedule函数,注意,下面的函数仅仅是将这个tasklet挂到TASKLET_SOFTIRQ对应的软中断所执行的tasklet队列上去,事实上,还有其它的软中断,比如HI_SOFTIRQ,会执行其它的tasklet队列,如果要挂上,那么就要调用tasklet_hi_schedule().如果你自己写的softirq执行一个tasklet队列,那么你需要自己写类似下面的函数。

  首先值得注意的是,我们前面提到过,所有的cpu共用32个softirq,但是同一个softirq在不同的cpu上执行的数据是独立的,基于这个原则,tasklet_vec对每个cpu都有一个,每个cpu都运行自己的tasklet队列。

  当执行一个tasklet队列时,内核将这个队列摘下来,以list为队列头,然后从list的下一个开始依次执行。这样做达到什么效果呢?在执行这个队列时,这个队列的结构是静止的,如果在运行期间,有中断产生,并且往这个队列里添加tasklet的话,将填加到tasklet_vec[cpu].list中,注意这个时候,这个队列里的任何tasklet都不会被执行,被执行的是list接管的队列。

  见/*1*//*2/之间的代码。事实上,在一个队列上同时添加和运行也是可行的,没这个简洁。

  这也就说明了tasklet是不可重入的,以防止两个相同的tasket访问同样的变量而产生竞争条件(race condition)

  其实还有另一对状态,禁止或允许,tasklet_struct中用count表示,用下面的函数操作

  首先要测试它是否已经被别的cpu挂上了,如果已经在别的cpu挂上了,则不再将它挂上,否则设置状态为TASKLET_STATE_SCHED

  为什么要这样做?试想,如果一个tasklet已经挂在一队列上,内核将沿着这个队列一个个执行,现在如果又被挂到另一个队列上,那么这个tasklet的指针

  3清除TASKLET_STATE_SCHED,为什么现在清除,它不是还没有从队列上摘下来吗?事实上,它的指针已经不再需要的,它的下一个tasklet已经被list记录了(/*0*/)。这样,如果其它cpu把它挂到其它的队列上去一点影响都没有。

  1和4确保了在所有cpu上,不可能运行同一个tasklet,这样在一定程度上确保了tasklet对数据操作是安全的,但是不要忘了,多个tasklet可能指向同一个函数,所以仍然会发生竞争条件。

  可能会有疑问:假设cpu 1上已经有tasklet 1挂在队列上了,cpu2应该根本挂不上同一个tasklet 1,怎么会有tasklet 1和它发生重入的情况呢?

  如图,一般情况下,在硬件中断处理程序后都会试图调用do_softirq执行软中断,但是如果发现现在已经有中断在运行,或者已经有软中断在运行,则

  不再运行自己调用的中断。也就是说,软中断是不能进入硬件中断部分的,并且软中断在一个cpu上是不可重入的,或者说是串行化的(serialize)

  其目的是避免访问同样的变量导致竞争条件的出现。在开中断的中断处理程序中不允许调用软中断可能是希望这个中断处理程序尽快结束。

  当内核正在执行处理定时器的软中断时,这期间可能会发生多个时钟中断,这些时钟中断的处理程序都试图再次运行处理定时器的软中断,但是由于已经有个软中断在运行了,于是就放弃返回。

  当硬中断执行完后,迅速调用do_softirq来执行软中断(见下面的代码),这样,被硬中断标注的软中断能得以迅速执行。当然,不是每次调用都成功的,见前面关于重入的帖子。

  还有,不是每个被标注的软中断都能在这次陷入内核的部分中完成,可能会延迟到下次中断。

  (1)在系统调用或者异常处理中同样可以标注软中断,这样它们在返回前就能得以迅速执行

  (2)前面提到,有些软中断要延迟到下次陷入内核才能执行,系统调用和异常都陷入内核,所以可以尽早的把软中断处理掉

  (3)如果在异常或者系统调用中发生中断,那么前面提到,可能还会有一些软中断没有处理,在这两个地方做一个补救工作,尽量避免到下次陷入内核才处理这些软中断。

  2.2.x版本中的bottom half就相当于2.4.1中的softirq.它的问题在于只有32个,如果要扩充的话,需要task队列(这里task不是进程,而是函数),还有一个比较大的问题,就是虽然bottom half在一个CPU上是串行的(由local_bh_count[cpu]记数保证),但是在多CPU上是不安全的,例如,一个CPU上在运行关于定时器的bottom half,另一个CPU也可以运行同一个bottom half,出现了重入。

  另外,用一个全局锁来保证,当一个CPU上运行一个bottom half时,其它CPU上不能运行任何一个bottom half。这和以前的bottom half有所不同,不知道是否我看错了。

  /*1*/试图上锁,如果得不到锁,则重新将bottom half挂上,下次在运行。

  tasklet_kill确保这个tasklet被运行了,因而它的指针也没有用了。

http://bluecaleel.com/xiangliangpingbi/155.html
锟斤拷锟斤拷锟斤拷QQ微锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷锟斤拷微锟斤拷
关于我们|联系我们|版权声明|网站地图|
Copyright © 2002-2019 现金彩票 版权所有