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

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

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 4 页
字号:
      */

      */
       
      } while(short_irq<=0 && count++<5);
       
       
       
      /* 循环结束,卸载处理程序 */
       
      for (i=0; trials[i]; i++)
       
             if (tried[i]==0)
       
                    free_irq(trials[i],NULL);
       
       
       
      if (short_irq<0)
       
             printk("short: probe failed %i times, giving up\n",count);
       
       
       
      你可能事先不知道“可能的”中断号。此时,你需要探测所有空闲的中断,而不仅是一

      你可能事先不知道“可能的”中断号。此时,你需要探测所有空闲的中断,而不仅是一
      些trials[]。为了探测所有的中断,你不得不从0号中断探测到NR_IRQS-1号中断,NR_IR
      QS是在头文件<asm/irq.h>中定义的与平台无关的常数。
       
       
       
      现在缺的就是探测处理程序自己了。该处理程序的功能就是根据实际接收到的中断号来
      更新short_irq变量。short_irq值为0意味着“什么也没有”,而负值意味着存在“二义
      性”。我选取这些值是为了和probe_irq_off保持一致,并可以在short.c中使用同样的
      代码来调用任何一种探测方法。
       
       
       
      void short_probing(int irq, void *dev_id, struct pt_regs *regs)
       
      {
       
          if (short_irq == 0) short_irq = irq;    /* 找到 */
       
          if (short_irq != irq) short_irq = -irq; /* 有二义性 */
       
      }
       

       
       
       
      处理程序的参数稍后会介绍。知道参数irq是要处理的中断号就足以理解上面的函数了。
       
       
      快速和慢速中断处理
             你已经看到,我为short的中断处理程序设置了SA_INTERRUPT标志位,因此是请
      求安装一个快速中断处理程序。现在到解释什么是“快速”和“慢速”的时候了。实际
      上,不是所有的体系结构都支持快速和慢速中断处理程序两种实现的。例如,Alpha和Sp
      arc的移植版本,快速和慢速处理程序是一样处理的。2.1.37版和其后的Intel移植版本
      也消除了两者的差别,因为现代处理器的可以获得的处理能力使得我们不必再区分出快
      速和慢速两种中断。
       
       
       
             这两种中断处理程序的主要差别就在于,快速中断处理程序保证中断的原子处理
      ,而慢速中断处理程序则不保证(这种差别在最新的中断处理的实现也保留了)。也就是
      说,“开启中断”处理器标志位(IF)在运行快速中断处理程序时是关闭的,因此在服务
      该中断时不允许被中断。而调用慢速中断处理时,内核启动微处理器的中断报告,因此
      在运行慢速中断处理程序时其它中断仍可以得到服务。
       
       

       
       
             在调用实际的中断处理程序之前,不管是快速还是慢速中断处理程序,内核都要
      执行一项任务,关闭刚才发出报告的那个中断信号线。这对程序员是个好消息-中断服
      务例程不必是可重入的。但另一方面,即使是慢速中断处理程序也要实现得运行的尽可
      能快,以免丢失后面到达的中断。
       
       
       
             当处理程序还在处理上一个中断时,如果设备又发出新的中断,新的中断会永远
      丢失。中断控制器并不缓存被屏蔽的中断,但是处理器会进行缓存-一旦发出sti指令,
      待处理的中断就会得到服务。sti函数是“置中断标志位”处理器指令(是在第2章“编写
      和运行模块”的“ISA内存”一节引入的)。
       
       
       
             总结快速和慢速两种执行环境如下:
       
       
       
      l      快速中断处理程序运行时微处理器关闭了中断报告,中断控制器禁止了被服务这
      个中断。但处理程序可以通过调用sti来启动处理器的中断报告。
       

       
       
       
      l      慢速处理程序运行时启动了处理器的中断报告,但中断控制器也禁止了被服务这
      个中断。
       
       
       
      但快速和慢速中断处理程序还有另一处不同:内核带来的额外开销。慢速中断处理程序
      之所以慢是因为内核带来的一些管理开销造成的。这意味着较频繁的中断最好由快速中
      断处理程序为之提供服务。至于short,当把大文件拷贝到/dev/short0时每秒会产生上
      千次中断。因此我选择使用了一个快速中断处理程序来控制添加给系统的开销。这种分
      别在更新的2.1版的内核中已经得到统一;这个开销现在加到了所有的中断处理程序上。
       
       
       
       
      帧捕捉卡是使用慢速中断处理程序的一个好的候选者。它每秒只中断处理器50到60次,
      选择使用慢速处理程序将帧数据从接口卡拷贝到物理内存就不会阻塞住其它的系统中断
      ,例如那些由串口或定时器服务产生的中断。
       
      x86平台上中断处理的内幕
             下面的描述是根据2.0.x版本的内核中的两个文件arch/i386/kernel/irq.c和inc

             下面的描述是根据2.0.x版本的内核中的两个文件arch/i386/kernel/irq.c和inc
      lude/asm-i386/irq.h推断的;虽然基本概念是相同的,但是具体的硬件细节与平台有关
      ,并且在2.1开发版本中有些修改。
       
       
       
             最底层的中断处理是在头文件irq.h中的声明为宏的一些汇编代码,这些宏在文
      件irq.h中被扩展。为每个中断声明了三种处理函数:慢速,快速和伪(bad)处理函数。
       
       
       
             “伪”处理程序,它最小,是当没有为中断安装C语言的处理程序时的汇编入口
      点。它将中断转交给适当的PIC(Programmable Interrupt Controller,可编程的中断控
      制器)设备*的同时禁止它,以避免由于伪中断而进一步浪费处理器时间。在驱动程序处
      理完中断信号后调用free_irq时又会重新安装伪处理程序。伪处理程序不会将/proc/sta
      t中的计数器加1。
       
       
       
             值得注意的是,在x86和Alpha上的自动探测都是依赖于伪处理程序的这种行为。
      probe_irq_on启动所有的伪中断,而不安装处理程序;probe_irq_off只是简单地检查自
      调用probe_irq_on以来那些中断被禁止了。如果你想验证这一点,可以在装载short时指
      定probe=1(内核帮助下的检测),此时可观察到中断计数器没有加1,而如果装载时指定p

      定probe=1(内核帮助下的检测),此时可观察到中断计数器没有加1,而如果装载时指定p
      robe=2(DIY检测)则会将它们加1。
       
       
       
             慢速中断的汇编入口点会将所有寄存器保存到堆栈中,并将数据段(DS和ES处理
      器寄存器)指向核心地址空间(处理器已经设置了CS寄存器)。然后代码将将中断转交给PI
      C,禁止在相同的中断信号线上触发新的中断,并发出一条sti指令(set interrupt
      flag,置中断标志位)。注意处理器在对中断进行服务时会自动清除该标志位。接着慢速
      中断处理程序就将中断号和指向处理器寄存器的一个指针传递给do_IRQ,这是一个C函数
      ,由它来调用相应的C语言处理程序。驱动程序传递给中断处理程序参数struct
      pt_regs *是一个指向存放着各个寄存器的堆栈的指针。
       
       
       
             do_IRQ结束后,会发出cli指令,打开PIC中指定的中断,并调用ret_from_sys_c
      all。最后这个入口点(arch/i386/kernel/entry.S)从堆栈中恢复所有的寄存器,处理所
      有待处理的下半部处理程序(参见本章的“下半部”一节),并且,如果需要的话,重新
      调度处理器。
       
       
       
             快入口点不同的是,在跳转到C代码之前并不调用sti指令,并且在调用do_fast_

             快入口点不同的是,在跳转到C代码之前并不调用sti指令,并且在调用do_fast_
      IRQ前并不保存所有的机器寄存器。当驱动程序中的处理程序被调用时,regs参数是NULL
      (空指针,因为寄存器没有保存到堆栈中)并且中断仍被屏蔽。
       
       
       
             最后,快速中断处理程序会重新打开8259芯片上的所有中断,恢复先前保存的所
      有寄存器,并且不经过ret_from_sys_call就返回了。待处理的下半部处理程序也不运行
       
       
       
             2.1.34前的所有内核版本中,这两种处理程序在将控制转移给C代码前都会将int
      r_count变量加1(参见第6章“时间流”的“任务队列的特性”一节)。
       
      实现中断处理程序
             至此,我们学习了如何注册一个中断处理程序,但还并没有真正编写这样的一个
      处理程序。实际上,处理程序并没有什么特别的-就是普通的C代码。
       
       
       
             唯一特别的地方就是处理程序是在中断时间内运行的,因此它的行为要受些限制
      。这些限制和我们在任务队列中看到的差不多。处理程序不能向用户空间发送或接受数

      。这些限制和我们在任务队列中看到的差不多。处理程序不能向用户空间发送或接受数
      据,因为它不在任何进程的上下文中执行。快速中断处理程序,可以认为是原子地执行
      的,当访问共享的数据项时并不需要避免竞争条件。而慢速处理程序不是原子的,因为
      在运行慢速处理程序时也能为其它处理程序提供服务。
       
       
       
             中断处理程序的功能就是将有关中断接收的信息反馈给设备,并根据要服务的中
      断的不同含义相应地对数据进行读写。第一步通常要先清除接口卡上的一个位;大部分
      硬件设备在它们的“中断待处理”位被清除前是不会产生任何中断的。一些设备就不需
      要这一步,因为它们没有“中断待处理”位;这样的设备比较少,但并口却是其中之一
      。因此,short不需要清除这样的位。
       
       
       
             中断处理程序的典型任务是唤醒在设备上睡眠的那些进程——如果中断向这些进
      程发出了信号,指示它们等待的事件已经发生,比如,新数据到达了。
       
       
       
             还举老的帧捕获卡的例子,进程可以通过连续地对设备读来获取一系列的图像;
      每读一帧后read调用都被阻塞,而新的帧一到达中断处理程序都会唤醒该进程。这假定
      了捕获卡会中断处理器来发出信号通知每一帧的成功到达。

      了捕获卡会中断处理器来发出信号通知每一帧的成功到达。
       
       
       
             不论是快速还是慢速中断处理程序,程序员都要注意处理例程的执行时间必须尽
      可能短。如果要进行长时间的计算,最好的方法是使用任务队列,将计算调度到安全时
      间内进行(参见第6章的“任务队列”一节)。这也是需要下半部处理的一个原因(参见本
      章稍后的“下半部”)。
       
       
       
             short中的范例代码使用中断来调用do_gettimeofday并把当前时间打印到大小为
      一页的循环缓冲区。然后它唤醒所有的读进程(实际上由于short使用快速中断处理程序
      ,这些读进程只会在下一个慢速中断处理程序结束时或下一个时钟滴答时醒来)。
       
       
       
      void short_interrupt(int irq, void *dev_id, struct pt_regs *regs)
       
      {
       
          struct timeval tv;
       

       
          do_gettimeofday(&tv);
       
       
       
          /* 写一个16个字节的记录。假设 PAGE_SIZE是16的倍数 */
       
          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); /* 唤醒所有的读进程 */
       
      }
       
       
       

       
             这段代码,尽管简单,却给出了一个中断处理程序的典型工作流程。
       
       
       
             用来读取在中断时间里填满的缓冲区的节点是/dev/shortint。这是唯一的没有
      在第8章中介绍的short设备节点。/dev/shortint内部的实现为中断产生和报告作了特别
      的处理。每向设备写入一个字节都会产生一个中断;而读设备时则给出每次中断报告的
      时间。
       
       
       
             如果你将并口插座的第9和第10引脚相连,那么拉高并行数据字节的最高位就可
      以产生中断。这可以通过向/dev/short0写二进制数据或者向/dv/shortint*写入任意数
      据来实现。
       
       
       

⌨️ 快捷键说明

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