⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 (ldd) ch09-中断处理(下)(转载).txt

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 3 页
字号:
(LDD) Ch09-中断处理(下)(转载)
       
       
      共享中断
             PC机一个众所周知的“特性”就是不能将不同的设备挂到同一个中断信号线上。
      但是,Linux打破了这一点。甚至我的ISA硬件手册―一本没提到Linux的书―也说“最多
      只有一个设备”可以挂到中断信号线上,除非硬件设备设计的不好,电信号上并无这样
      的限制。问题在于软件。
       
       
       
             Linux软件对共享的支持是为PCI设备做的,但也可用于ISA卡。不必说,非PC平
      台和总线也支持共享。
       
       
       

       
             为了开发能处理共享中断信号的驱动程序,必须考虑一些细节。下面会讨论到,
      使用共享中断的驱动程序不能使用本章描述的一些特性。但最好尽可能对共享中断提供
      支持,因为这样对最终用户来说比较方便。
       
      安装共享的处理程序
             和已经拥有的中断一样,要与它共享的中断也是通过request_irq函数来安装的
      ,但它们有两处不同:
       
       
       
      l        申请共享中断时,必须在flags参数中指定SA_SHIRQ位
       
       
       
      l        dev_id参数必须是唯一的。任何指向模块的地址空间的指针都可以,当然dev_
      id一定不能设为NULL。
       
       
       
      内核为每个中断维护了一张共享处理函数的列表,并且这些处理函数的dev_id各不相同
      ,就象是驱动程序的签名。如果两个驱动程序都将NULL注册为它们对同一个中断的签名
      ,那么在卸载时会混淆起来,当中断到达时内核就会出现oops消息。我第一次测试共享

      ,那么在卸载时会混淆起来,当中断到达时内核就会出现oops消息。我第一次测试共享
      中断时就发生过这种事情(当时我只是想着“一定要将SA_SHIRQ位加到这两个驱动程序上
      ”)。
       
       
       
      满足这些条件之后,如果中断信号线空闲或者下面两个条件同时得到满足,那么request
      _irq就会成功:
       
       
       
      l        前面注册的处理函数的flags参数指定了SA_SHIRQ位。
       
       
       
      l        新的和老的处理函数同为快速处理函数,或者同为慢速处理函数。
       
       
       
      需要满足这些要求的原因很明显:快速和慢速处理函数处于不同的环境,不能互相混淆
      。类似的,你也不能与已经安装为不共享的中断处理函数共享相同的中断。但关于快速
      和慢速处理函数的限制对最近的2.1版的内核来说是不必要的,因为两种处理函数已经合
      并了。

      并了。
       
       
       
      当两个或两个以上的驱动程序共享同一根中断信号线,而硬件又通过这根信号线中断了
      处理器时,内核激活这个中断注册的所有处理函数,并将自己的dev_id传递给它们。因
      此,共享处理函数必须能够识别出它对应于哪个中断。
       
       
       
      如果你在申请中断信号之前需要探测你的设备的话,内核无法提供帮助。没有共享中断
      的探测函数。仅当使用的中断信号线空闲时,标准的探测机制才能奏效;但如果被其它
      的具有共享特性的驱动程序占用的话,那么即使你的程序已经可以正常工作了,探测也
      会失败。
       
       
       
      那么,唯一的可以用来探测共享中断信号的技术就是DIY探测。驱动程序必须为所有可能
      的中断信号线申请共享处理函数,然后观察中断在何处报告。这里和前面介绍的DIY的探
      测之间的差别在于,此时探测处理函数必须检查是否真的发生了中断,因为为响应共享
      中断信号线上的其它设备的中断它可能已经被调用过了。
       
       

       
       
      释放处理函数同样是通过执行release_irq来实现的。这里dev_id参数用于从该中断的共
      享处理函数列表中正确地选出要释放的那个处理函数。这就是dev_id指针必须唯一的原
      因。
       
       
       
      使用共享处理程序的驱动程序时还要小心:不能使用enable_irq和disable_irq。如果它
      使用了这两个函数,共享中断信号线的其它设备就无法正常工作了。一般地,程序员必
      须牢记他的驱动程序并不独占这个中断,因此它的行为必须比独占中断信号线时更“社
      会化”些。
       
      运行处理函数
             如上所述,当内核接收到中断时,所有注册过的处理函数都会被激活。共享中断
      处理程序必须能将需要处理的中断和其它设备产生的中断区分开来。
       
       
       
             装载short时指定shared=1将安装下面的处理程序而不是缺省的处理程序:
       
       
       

       
      void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
       
      {
       
          int value;
       
          struct timeval tv;
       
       
       
          /* 如果不是short,立即返回 */
       
          value = inb(short_base);
       
          if (!(value & 0x80)) return;
       
       
       
          /* 清除中断位 */
       
          outb(value & 0x7F, short_base);
       

       
       
       
          /* 其余不变 */
       
       
       
          do_gettimeofday(&tv);
       
          short_head += sprintf((char *)short_head,"%08u.%06u\n",
       
                                (int)(tv.tv_sec % 100000000), 
(int)(tv.tv_usec));
       
          if (short_head == short_buffer + PAGE_SIZE)
       
              short_head = short_buffer; /* 绕回来 */
       
       
       
          wake_up_interruptible(&short_queue); /* 唤醒所有读进程 */
       
      }
       

       
       
       
             解释如下。因为并口没有 “待处理的中断”位可供检查,为此处理函数使用了A
      CK位。如果该位为高,报告的中断就是送给short的,并且处理函数将清除该位。
       
       
       
             处理函数是通过将并口的数据端口的高位清零来清除中断位的-short假定并口
      的9和10引脚是连在一起的。如果与short共享同一中断的设备产生了一个中断,short会
      知道它的信号线并未激活,因此什么也不会做。
       
       
       
             显然,真正的驱动程序做的工作会更多些;特别的,它要使用dev_id参数来得到
      自己的硬件结构。
       
       
       
             特性完全的驱动程序可能会将工作划分为上半部和下半部,但这很容易添加,对
      实现共享的代码并无太大影响。
       
      /proc接口

      /proc接口
             系统中安装的共享中断处理程序不会影响/proc/stat文件(该文件甚至并不知道
      处理程序的存在)。但是,/proc/interrupts文件会有些变化。
       
       
       
             为同一个中断号安装的处理程序会出现在/proc/interrupts文件的同一行上。下
      面的快照取自我的计算机,是在我将short和我的帧捕捉卡装载为共享中断处理程序之后
       
       
       
             0:      1153617   timer
       
             1:        13637   keyboard
       
             2:            0   cascade
       
             3:        14697 +  serial
       
             5:       190762   NE2000
       
             7:         2094 +  short, + cx100

             7:         2094 +  short, + cx100
       
         13:            0   math error
       
         14:        47995 +  ide0
       
         15:        12207 +  ide1
       
       
       
             这里共享中断信号是IRQ7号中断;激活的处理程序列在同一行,用逗号隔开。显
      然内核是无法区分short中断和捕捉卡(cx100)中断的。
       
      中断驱动的I/O
             如果和处理的硬件间的数据传输因为某些原因会被延迟的话,那么驱动程序的写
      函数必须实现缓冲。数据缓冲可以将数据的发送和接收与write及read系统调用分离开来
      ,提高系统的整体性能。
       
       
       
             一个好的缓冲机制是“中断驱动的I/O”,它在中断时间内填充一个输入缓冲区
      并由读设备的进程将其取空;或由写设备的进程来填充一个输入缓冲区并在中断时间内
      将其取空。

      将其取空。
       
       
       
             中断驱动的数据传输要正确进行,要求硬件必须安下面的语义产生中断:
       
       
       
      l        对输入而言,当新数据到达,系统处理器准备读取它时,设备就中断处理器。
      实际执行的动作取决于设备是否使用了I/O端口,内存映射或者DMA。
       
       
       
      l        对输出而言,当设备准备好接收新数据或对成功的数据传输进行确认时都会发
      出中断。内存映射和能进行DMA的设备通常是通过产生中断来通知系统它们的对缓冲区的
      处理已经结束。
       
       
       
      read或write调用时间和实际的数据到达时间之间的关系是在第5章“字符设备驱动程序
      的扩展操作”的“阻塞型和非阻塞型操作”一节中介绍的。中断驱动的I/O引入了共享数
      据项的并发进程间的同步问题,因此所有这些问题都与竞争条件有关。
       

       
      竞争条件
             当变量或其它数据项在中断时间内被修改时,由于竞争条件的存在,驱动程序的
      操作就有可能造成它们的不一致。当操作不是原子地执行时,竞争条件就会发生,但在
      执行时仍假定数据会保持一致性。因此“竞争”是在非原子性的操作和其它可能被同时
      执行的代码之间发生的。典型的,竞争条件会在三种情况下发生:在函数内隐式地调用s
      chedule,阻塞操作和由中断代码或系统调用访问共享数据。最后一种情况发生得最频繁
      ,因此我们在这一章处理竞争条件。
       
       
       
             处理竞争条件是编程时最麻烦的一部分,因为相关的臭虫满足的条件很苛刻,不
      容易再现,很难分辨出中断代码和驱动程序的方法间是否存在竞争条件。程序员必须极
      为小心地避免数据或元数据的冲突。
       
       
       
             一般用于避免竞争条件的技术是在驱动程序的方法中实现的,这些方法必须保证
      当数据项受到没有预料到的修改时得到正确的处理。但另一方面,中断处理函数并不需
      要特别的处理,因为相对设备的方法,它的操作是原子性的。
       
       
       

       
             可以使用不同的技术来防止数据冲突,我下面将介绍最常用的一些技术。我不给
      出完整的代码,因为各种情况下最好的实现代码取决于被驱动的设备的操作模式以及程
      序员的不同爱好。
       
       
       
             最常用的防止数据被并发地访问的方法有:
       
       
       
      l        使用循环缓冲区和避免使用共享变量。
       
       
       
      l        在访问共享变量的方法里暂时禁止中断。
       
       
       
      l        使用锁变量,它是原子地增加和减少的。
       
       
       

       
      当访问可能在中断时间内被修改了的变量时,不论你选用的是哪种方法,都必须决定如
      何进行处理。这样的变量可以声明为volatile的,来阻止编译器对该值的访问进行优化(
      例如,它阻止编译器在整个函数的运行期内将这个值放进一个寄存器中)。但是,使用vo
      latile变量后,编译器产生的代码会很糟糕,因此你可能会转向使用cli和sti。Linux实
      现这些函数时使用了gcc的制导来保证在中断标志位被修改之前处理器处于安全状态。
       
      使用循环缓冲区
             使用循环缓冲区是处理并发访问问题的一种有效方法:当然最好的处理方法还是
      不允许并发访问。
       
       
       
             循环缓冲区使用了一种被称为“生产者和消费者”的算法-一个进程将数据放进
      缓冲区中,另一个则将它取出来。如果只有一个生产者和一个消费者,那就避免了并发
      访问。在short模块中有两个生产者和消费者的例子。其中一个情形是,读进程等待消费
      在中断时间里生产的数据;而另一个情形是,下半部消费上半部生产的数据。
       
       
       
             共有两个指针用于对循环缓冲区进行寻址:head和tail。head是数据的写入位置
      ,由数据的生产者更新。数据从tail处读出,它是由消费者更新的。正如我上面提到的
      ,如果数据是在中断时间内写的,那么多次访问head多次时就必须小心。你必须将head

      ,如果数据是在中断时间内写的,那么多次访问head多次时就必须小心。你必须将head
      定义成volatile的或者在进入竞争条件前将中断禁止。
       
       
       
             循环缓冲区在填满前工作的很好。如果缓冲区满了,就可能出问题,但你可以有
      多种不同的解决方法可供选择。short中的实现就是简单地丢弃数据;并不检查溢出,如
      果head超过了tail,那么整个缓冲区中的数据都丢失了。其它的实现还有丢弃最后那个

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -