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

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

📁 献给ARM初学者
💻 TXT
📖 第 1 页 / 共 3 页
字号:
      任务(Linus非常喜欢用这种方法)。但是,如果你愿意的话你可以跳过下面的讨论,因为
      下面的讨论的确有点太细了。
       
       
       
             该方法的基本想法是,进程可以把自己排进等待队列,声明自己的状态为睡眠状
      态,然后执行它的测试代码。
       
       
       
      典型的实现如下:
       
       

       
       
      struct wait_queue wait = {current, NULL};
       
       
       
      add_wait(&short_queue, &wait);
       
      current->state=TASK_INTERRUPTIBLE;
       
      while (short_head==short_tail){
       
             schedule();
       
             /* ... 信号解码 ... */
       
      }
       
      remove_wait_queue(&short_queue, &wait);
       
       
       
             这段代码看起来有点象将sleep_on的内部实现展开了。显式地声明了wait变量,

             这段代码看起来有点象将sleep_on的内部实现展开了。显式地声明了wait变量,
      因为需要用它来使进程进入睡眠;这一切是在第5章的“等待队列”一节中解释的,但这
      个例子中引入了一些新的符号。
       
       
       
      current->state
       
      这个字段是给调度器用的提示。调度器被激活后,它将通过观察所有进程的state字段来
      决定接着作些什么。所有进程都可以任意修改自己的state字段,但在调度器运行之前这
      种改变还不会生效。
       
       
       
      #include <linux/sched.h>
       
      TASK_RUNNING
       
      TASK_INTERRUPTIBLE
       
      TASK_UNINTERRUPTIBLE
       
      这些符号名代表了current->state最经常取的一些值。TASK_RUNNING表示进程正在运行

      这些符号名代表了current->state最经常取的一些值。TASK_RUNNING表示进程正在运行
      ,其它两个表示进程正在睡眠。
       
       
       
      void add_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      void remove_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      void __add_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      void __ remove_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      这些函数用于从等待队列中插入和删除进程。wait参数必须指向进程堆栈所在的页(临时
      变量)。以下划线开头的函数运行的更快些,但它们在禁止中断后才能被调用(例如,在
      快速中断处理程序内)。
       
       
       
             有了这些背景知识,下面让我们看看当中断到达时会发生什么。此时处理程序将
      调用wake_up_interruptible(&short_queue);对Linux而言,这意味着“将state置为TA
      SK_RUNNING”。因此,如果在while条件和schedule调用间有中断报告的话,该任务的st
      ate字段将会又被标记为TASK_RUNNING的,因此不会丢失数据。

      ate字段将会又被标记为TASK_RUNNING的,因此不会丢失数据。
       
       
       
             而如果进程仍是“可中断的”(TASK_INTERRUPTIBLE),schedule将保持它的睡眠
      状态。
       
             值得注意的是,wake_up系统调用并不会将进程从等待队列中删去。是由sleep_o
      n来对等待队列进行进程的添加和删除的。因此程序代码必须显式地调用add_wait_queue
      和remove_wait_queue,因为这种情况下不再使用sleep_on了。
       
      中断处理的版本相关性
             不是所有本章引入的代码都能向后兼容地移植到Linux 1.2上的。在此我将列出
      主要的差异并对如何处理这些差异提出建议。实际上,short在2.0.x和1.2.13版的内核
      上都编译和运行得很好。
       
      request_irq函数的不同原型
             我在这一整章中使用的给request_irq函数传递参数的方式都是到1.3.70版的内
      核才引入的,因为是到这个版本才出现了共享中断处理程序的。
       
       
       
             更早的内核版本并不需要dev_id参数,原型也相对简单些:

             更早的内核版本并不需要dev_id参数,原型也相对简单些:
       
       
       
      int request_irq(unsigned int irq,
       
      void (*handler)(int, struct pt_regs *),
       
      unsigned long flags, const char *device);
       
       
       
             只要使用下面的宏定义(注意早期的版本中free_irq也没有dev_id参数),新的语
      义可以很容易地强加在旧原型上:
       
       
       
      #if LINUX_VERSION_CODE < VERSION_CODE(1,3,70)
       
         /* 预处理器必须能处理递归的定义 */
       
      #  define request_irq(irq,fun,fla,nam,dev) request_irq(irq,fun,fla,nam)
       

       
      #  define free_irq(irq,dev)                free_irq(irq)
       
      #endif
       
       
       
             这些宏只是简单地丢弃额外的dev参数。
       
       
       
             处理函数原型上的差异通过显式的#if/#else/#endif语句得到很好的处理。如果
      你使用了dev_id指针,旧内核的条件分支可以将它申明为NULL变量,这样处理函数体就
      可以对NULL设备指针进行处理了。
       
       
       
             short模块中的一个例子可以作为这种想法的范例:
       
       
       
      #if LINUX_VERSION_CODE < VERSION_CODE(1,3,70)
       

       
      void short_sh_interrupt(int irq, struct pt_regs *regs)
       
      {
       
       void *dev_id = NULL;
       
      #else
       
      void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs)
       
      {
       
      #endif
       
      探测中断信号线
             内核到1.3.30版开始开放探测函数。如果你希望你的驱动程序能移植到旧的内核
      上,你将不得不实现DIY检测。实际上早在1.2版的内核,这些函数就已经存在了,只是
      模块化的驱动程序无法使用罢了。
       
       
       
             这样,移植中断处理函数就没有什么其它的问题了。

             这样,移植中断处理函数就没有什么其它的问题了。
       
      快速参考
             本章引入了下面这些与中断管理有关的符号:
       
       
       
      #include <linux/sched.h>
       
      int request_irq(unsigned int irq, void (*handler)());
       
      unsigned long flags, const char *device, void *dev_id);
       
      void free_irq(unsigned int irq, void *dev_id);
       
             这些系统调用用于注册和注销中断处理程序。低于2.0版的内核不提供dev_id参
      数。
       
       
       
      SA_INTERRUPT
       
      SA_SHIRQ

      SA_SHIRQ
       
      SA_SAMPLE_RANDOM
       
      这些是request_irq函数的各种选项。SA_INTERRUPT请求安装快速中断处理程序(相对于
      慢速处理函数)。SA_SHIRQ安装共享中断处理函数,而第三种选项表明产生的中断的时间
      戳对系统熵池(entropy pool)有贡献。
       
       
       
      /proc/interrupts
       
      /proc/stat
       
             这些文件系统节点用于报告关于硬件中断和安装的处理函数的信息。
       
       
       
      unsigned long probe_irq_on(void);
       
      int probe_irq_off(unsigned long);
       
      当驱动程序需要探测设备使用哪根中断信号线时,可以使用这些函数。在中断产生之后

      当驱动程序需要探测设备使用哪根中断信号线时,可以使用这些函数。在中断产生之后
      ,probe_irq_on的返回值必须传回给probe_irq_off。probe_irq_off的返回值就是检测
      到的中断号。
       
       
       
      void disable_irq(int irq);
       
      void enable_irq(int irq);
       
      驱动程序可以启动和禁止中断报告。禁止中断后,硬件产生的中断都将丢失。在上半部
      处理程序中调用这些函数则没有任何效果。而使用共享中断处理程序的驱动程序决不能
      使用这些函数。
       
       
       
      #include <linux/interrupt.h>
       
      void mark_bh(int nr);
       
             这些函数用于标记要执行的下半部。
       
       

       
       
      #include <asm/bitops.h>
       
      set_bit(nr, void *addr);
       
      clear_bit(nr, void *addr);
       
      change_bit(nr, void *addr);
       
      test_bit(nr, void *addr);
       
      这些函数用于原子性地访问位的值;它们可作用于标志位和锁变量。使用这些函数避免
      了所有与对位的并发访问有关的竞争条件。
       
       
       
      #include <asm/atomic.h>
       
      typedef int atomic_t;
       
      void atomic_add(atomic_t i, atomic_t *v);
       

       
      void atomic_sub(atomic_t i, atomic_t *v);
       
      void atomic_inc(atomic_t *v);
       
      void atomic_dec(atomic_t *v);
       
      int atomic_dec_and_test(atomic_t *v);
       
      这些函数用于原子地访问整数变量。如果想让编译时不出现警告信息,必须只使用这些
      函数来访问atomic_t类型的变量。
       
       
       
      #include <linux/sched.h>
       
      TASK_RUNNING
       
      TASK_INTERRUPTIBLE
       
      TASK_UNINTERRUPTIBLE
       
             这些是current->state最经常取的一些值。它们是给schedule用的提示。

             这些是current->state最经常取的一些值。它们是给schedule用的提示。
       
       
       
      void add_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      void remove_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      void __add_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      void __ remove_wait_queue(struct wait_queue ** p, struct wait_queue *wait)
       
      这些是使用等待队列的最底层的函数。打头的下划线标志该函数是底层的函数,使用后
      两个函数时处理器必须已经禁止了中断报告。
       
       
       
      -----------------------------------------------------------------------------
       
      * PC机通常就已经有了两个中断控制芯片,叫做8259芯片(主从片)。而可编程的中断控
      制器设备已经不存在了,但现代的芯片组中也实现了相同的功能。
       
      * shortint设备是通过交替地向并口写入0x00和0xff来实现的。

       
      * PC机通常就已经有了两个中断控制芯片,叫做8259芯片(主从片)。而可编程的中断控
      制器设备已经不存在了,但现代的芯片组中也实现了相同的功能。
       
      * shortint设备是通过交替地向并口写入0x00和0xff来实现的。
       
      * 随后就会介绍,使用自己的下半部的驱动程序可以调用disable_bh函数。
       
      --
         

⌨️ 快捷键说明

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