Linux驱动开发基础

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

目录

1  Linux 对中断的扩展硬件中断、软件中断 

2  中断处理原则 1不能嵌套

3  中断处理原则 2越快越好

4 要处理的事情实在太多拆分为上半部、下半部 

5 下半部要做的事情耗时不是太长tasklet

6 下半部要做的事情太多并且很复杂工作队列 

7 新技术threaded irq 


Linux 中断系统的变化并不大。比较重要的就是引入了 threaded irq使用内核线程来处理中断。

Linux 系统中有硬件中断也有软件中断。对硬件中断的处理有 2 个原则不能嵌套越快越好。

1  Linux 对中断的扩展硬件中断、软件中断 

 Linux 系统把中断的意义扩展了对于按键中断等硬件产生的中断称之为“硬件中断”(hard irq)。每个硬件中断都有对应的处理函数比如按键中断、网卡中断的处理函数肯定不一样。 
为方便理解你可以先认为对硬件中断的处理是用数组来实现的数组里存放的是函数指针 

 注意上图是简化的Linux 中这个数组复杂多了。 
当发生 A 中断时对应的 irq_function_A 函数被调用。硬件导致该函数被调用。 
相对的还可以人为地制造中断软件中断(soft irq)如下图所示 

 注意上图是简化的Linux 中这个数组复杂多了。 
问题来了 
软件中断何时生产 
由软件决定对于 X 号软件中断只需要把它的 flag 设置为 1 就表示发生了该中断。 
软件中断何时处理 
软件中断嘛并不是那么十万火急有空再处理它好了。 什么时候有空不能让它一直等吧 
Linux 系统中各种硬件中断频繁发生至少定时器中断每 10ms 发生一次那取个巧 在处理完硬件中断后再去处理软件中断就这么办 
有哪些软件中断 
查内核源码 include/linux/interrupt.h 

 怎么触发软件中断最核心的函数是 raise_softirq简单地理解就是设置 softirq_veq[nr]的标记位

 怎么设置软件中断的处理函数 extern  void  open_softirq(int  nr,  void  (*action)  (struct 
soft_action*)); 

 后面讲到的中断下半部 tasklet 就是使用软件中断实现的。 

2  中断处理原则 1不能嵌套

官方资料中断处理不能嵌套 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e58aa3d2d0cc 
中断处理函数需要调用 C 函数这就需要用到栈。 
⚫  中断 A 正在处理的过程中假设又发生了中断 B那么在栈里要保存 A 的现
场然后处理 B。 
⚫  在处理 B 的过程中又发生了中断 C那么在栈里要保存 B 的现场然后处理
C。 
如果中断嵌套突然暴发那么栈将越来越大栈终将耗尽。 所以为了防止这种情况发生也是为了简单化中断的处理在 Linux 系统上中断无法嵌套即当前中断 A 没处理完之前不会响应另一个中断 B(即使它的优先级更高)。 

3  中断处理原则 2越快越好

妈妈在家中照顾小孩时门铃响起她开门取快递这就是中断的处理。她
取个快递敢花上半天吗不怕小孩出意外吗 
同理在 Linux 系统中中断的处理也是越快越好。 在单芯片系统中假设中断处理很慢那应用程序在这段时间内就无法执行系统显得很迟顿。

在 SMP 系统中假设中断处理很慢那么正在处理这个中断的 CPU 上的其他
线程也无法执行。 在中断的处理过程中该 CPU 是不能进行进程调度的所以中断的处理要越快越好尽早让其他中断能被处理──进程调度靠定时器中断来实现。 在 Linux 系统中使用中断是挺简单的为某个中断 irq 注册中断处理函数
handler可以使用 request_irq 函数

 在 handler 函数中代码尽可能高效。 但是处理某个中断要做的事情就是很多没办法加快。比如对于按键中断我们需要等待几十毫秒消除机械抖动。难道要在 handler 中等待吗对于计算
机来说这可是一个段很长的时间。 怎么办 

4 要处理的事情实在太多拆分为上半部、下半部 

当一个中断要耗费很多时间来处理时它的坏处是在这段时间内其他中断无法被处理。换句话说在这段时间内系统是关中断的。 
如果某个中断就是要做那么多事我们能不能把它拆分成两部分紧急的、不紧急的 
在 handler 函数里只做紧急的事然后就重新开中断让系统得以正常运行那些不紧急的事以后再处理处理时是开中断的。 

中断下半部的实现有很多种方法讲 2 种主要的tasklet(小任务)、work queue(工作队列)。  

5 下半部要做的事情耗时不是太长tasklet

假设我们把中断分为上半部、下半部。发生中断时上半部下半部的代码何时、如何被调用 
当下半部比较耗时但是能忍受并且它的处理比较简单时可以用 tasklet来处理下半部。tasklet 是使用软件中断来实现。 

 写字太多不如贴代码代码一目了然

 使用流程图简化一下

 假 设 硬 件 中 断 A 的 上 半 部 函 数 为 irq_top_half_A 下 半 部 为
irq_bottom_half_A。 
使用情景化的分析才能理解上述代码的精华。 
⚫  硬件中断 A 处理过程中没有其他中断发生 一开始preempt_count = 0上述流程图①~⑨依次执行上半部、下半部的代码各执行一次。  
⚫  硬件中断 A 处理过程中又再次发生了中断 A 
一开始preempt_count = 0 
执行到第⑥时一开中断后中断 A 又再次使得 CPU 跳到中断向量表。 
注意这时 preempt_count 等于 1并且中断下半部的代码并未执行。 
CPU 又从①开始再次执行中断 A 的上半部代码 
在第①步 preempt_count 等于 2 
在第③步 preempt_count 等于 1 
在第④步发现 preempt_count 等于 1所以直接结束当前第 2 次中断的处
理 
注意重点来了第 2 次中断发生后打断了第一次中断的第⑦步处理。当第 2
次中断处理完毕CPU 会继续去执行第⑦步。 
可以看到发生 2 次硬件中断 A 时它的上半部代码执行了 2 次但是下半
部代码只执行了一次。 
所以同一个中断的上半部、下半部在执行时是多对一的关系。 
⚫  硬件中断 A 处理过程中又再次发生了中断 B 
一开始preempt_count = 0 
执行到第⑥时一开中断后中断 B 又再次使得 CPU 跳到中断向量表。 
注意这时 preempt_count 等于 1并且中断 A 下半部的代码并未执行。 
CPU 又从①开始再次执行中断 B 的上半部代码 
在第①步 preempt_count 等于 2 
在第③步 preempt_count 等于 1 
在第④步发现 preempt_count 等于 1所以直接结束当前第 2 次中断的处
理 
注意重点来了第 2 次中断发生后打断了第一次中断 A 的第⑦步处理。当第
2 次中断 B 处理完毕CPU 会继续去执行第⑦步。 
在第⑦步里它会去执行中断 A 的下半部也会去执行中断 B 的下半部。 
所以多个中断的下半部是汇集在一起处理的。 
总结 

  •   中断的处理可以分为上半部下半部 
  •   中断上半部用来处理紧急的事它是在关中断的状态下执行的 
  •   中断下半部用来处理耗时的、不那么紧急的事它是在开中断的状态下执行的 
  •   中断下半部执行时有可能会被多次打断有可能会再次发生同一个中断 
  •   中断上半部执行完后触发中断下半部的处理 
  •  中断上半部、下半部的执行过程中不能休眠中断休眠的话以后谁来调度进程啊 

6 下半部要做的事情太多并且很复杂工作队列 

在中断下半部的执行过程中虽然是开中断的期间可以处理各类中断。但是毕竟整个中断的处理还没走完这期间 APP 是无法执行的。 
假设下半部要执行 1、2 分钟在这 1、2 分钟里 APP 都是无法响应的。 
这谁受得了所以如果中断要做的事情实在太耗时那就不能用软件中断来做而应该用内核线程来做在中断上半部唤醒内核线程。内核线程和 APP 都一样竞争执行APP 有机会执行系统不会卡顿。 
这个内核线程是系统帮我们创建的一般是 kworker 线程内核中有很多这
样的线程 

 kworker 线程要去“工作队列”(work queue)上取出一个一个“工作”(work)来执行它里面的函数。 
那我们怎么使用 work、work queue 呢

创建 work 
你得先写出一个函数然后用这个函数填充一个 work 结构体。比如 

 要执行这个函数时把 work 提交给 work queue 就可以了


上述函数会把 work 提供给系统默认的 work  queuesystem_wq它是一个队列。 谁来执行 work 中的函数 不用我们管schedule_work 函数不仅仅是把 work 放入队列还会把kworker 线程唤醒。此线程抢到时间运行时它就会从队列中取出 work执行里面的函数。 谁把 work 提交给 work queue 在中断场景中可以在中断上半部调用 schedule_work 函数。

 总结 

  • 很耗时的中断处理应该放到线程里去
  • 可以使用 work、work queue 
  • 在中断上半部调用 schedule_work 函数触发 work 的处理 
  • 既然是在线程中运行那对应的函数可以休眠。

  7 新技术threaded irq 

使用线程来处理中断并不是什么新鲜事。使用 work 就可以实现但是需要定义 work、调用 schedule_work好麻烦啊。于是内核提供了这样一个函数。

 你可以只提供 thread_fn系统会为这个函数创建一个内核线程。发生中断时内核线程就会执行这个函数。 
以前用 work 来线程化地处理中断一个 worker 线程只能由一个 CPU 执行多个中断的 work 都由同一个 worker 线程来处理在单 CPU 系统中也只能忍着了。但是在 SMP 系统中明明有那么多 CPU 空着你偏偏让多个中断挤在这个CPU 上 
新技术 threaded irq为每一个中断都创建一个内核线程多个中断的内核线程可以分配到多个 CPU 上执行这提高了效率。 

阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6
标签: linux