Linux内核:关于中断你需要知道的【转】

简介:

转自:http://blog.csdn.net/duqi_2009/article/details/38009717

1、中断处理程序与其他内核函数真正的区别在于,中断处理程序是被内核调用来相应中断的,而它们运行于中断上下文(原子上下文)中,在该上下文中执行的代码不可阻塞。中断就是由硬件打断操作系统。

2、异常与中断不同,它在产生时必须考虑与处理器时钟同步。异常被称为同步中断,例如:除0、缺页异常、陷入内核(trap)引起系统调用处理程序异常。

3、不同的设备对应的中断不同,而每个中断都通过一个唯一的数字(中断号)标识。

4、既想让中断处理程序运行得快,又想中断处理程序完成的工作量多,为了在这两个相悖的目标之间达到一种平衡,一般把中断处理分为两个部分。中断处理程序是上半部(top half):接收到一个中断,它就立刻开始执行,但只做有严格时限的工作,例如对接受的中断进行应答或者复位硬件,这些工作都是在中断被禁止的情况下完成的(上半部情况下,中断被禁止);另一部分是下半部(bottom half):能够被允许稍后完成的工作会推迟到下半部。

(1)为什么要用下半部?

中断处理程序执行的时候,当前中断号对应的中断在所有处理器上都会被屏蔽;更糟糕的是,如果处理器程序是IRQF_DISABLED类型,它执行的时候会把本地的所有中断都屏蔽。然而,缩短中断被屏蔽的时间对系统的响应能力和性能都至关重要,所以,我们应该尽力缩短中断处理程序的时间,办法就是把一些工作放到以后去做。关键是:在下半部运行的时候,允许响应所有中断

(2)划分中断上半部和下半部的借鉴原则:

 

  • 如果一个任务对时间非常敏感,将其放在中断处理程序中执行
  • 如果一个任务和硬件相关,将其放在中断处理程序中执行。
  • 如果一个任务要保证部被其他中断(特别是相同的中断)打断,将其放置在中断处理程序中执行。
  • 其他所有任务,考虑放置在下半部执行

 

5、例子,网卡驱动程序,当网卡接收到来自网路的数据包时,需要通知内核数据包到了。中断处理程序(top half)立即开始执行:通知硬件,拷贝最新的网络数据到内存,然后读取网卡更多的数据包,这些工作非常紧迫,因为网卡上接受数据包的缓存大小固定;下半部:执行处理和操作数据包的其他工作。

6、Linux提供的实现下半部的机制:(上半部的实现机制只有一种:中断处理程序)

在Linux内核2.6中,内核提供了三种不同形式的下半部实现机制:软中断、tasklet和工作队列。这三种机制从2.3开始引入。软中断用的比较少,tasklet是下半部更常用的一种形式,但是,tasklet是基于软中断实现的。

(1)如果你想加入一个新的软中断,首先应该问问自己为什么用tasklet实现不了,目前只有两个子系统(网络和SCSI)直接使用软中断。软中断只有在那些执行频率很高和连续性很高的情况下才需要使用。如果不需要扩展到多个处理器,那么就使用tasklet吧。tasklet本质上也是软中断,只不过同一个处理器程序的多个实例不能再多个处理器上同时运行。

下半部何时调用?内核在执行完中断处理器程序以后,do_softirq()函数,于是软中断开始执行中断处理程序留给它去完成的剩余任务。大部分tasklet和软中断都是在中断处理程序中被设置成待处理状态,所以最近一个中断返回的时候就是执行do_softirq()的最佳时机。

(2)tasklet_action()和tasklet_hi_action()是tasklet处理的核心。

(3)ksoftirqd辅助线程:每个处理器都有一组辅助处理软中断(和tasklet)的内核线程,当内核中出现大量软中断的时候,这些内核进程就会辅助处理它们。当一个软中断正在执行时,可能会再次触发它自己,内核目前采取的方案是:不会立即处理重新触发的软中断,在大量软中断出现的时候,内核会唤醒一组内核线程(nice值是19)来处理这些负载。

 

[cpp]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. for(;;)  
  2. {  
  3.      if(!softirq_pending(cpu)) //如果没有软中断,则调用schedule()  
  4.          schedule();  
  5.        
  6.      set_current_state(TASK_RUNNING);  
  7.        
  8.      while(softirq_pending(cpu)){  
  9.            do_softirq();  
  10.            if(need_resched()) //如有必要重新调度,每次迭代之后都会schedule()以便让更重要的进程得到处理机会  
  11.                  schedule();  
  12.      }  
  13.   
  14.       set_current_state(TASK_INTERRUPTIBLE);  
  15. }  

 

(4)工作队列(work queue)是另一种将工作任务推后的方式,与软中断和tasklet都不相同。工作队列把工作交给一个内核线程去执行——这个下半部总是在进程上下文中执行,最重要的,工作队列允许重新调度甚至睡眠。所以,如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后的任务不需要睡眠,那么就选择软件中断或者tasklet。如果需要使用一个可以重新调度的实体来执行当前中断的下半部任务,就应该使用工作队列。工作队列是唯一能在进程上下文中运行的下半部实现机制,也只有它才可以睡眠(关键是看你的任务是否需要睡眠)。尽管工作队列的操作处理函数运行在进程上下文中,但是它不能访问用户空间,因为该内核线程在用户空间没有相关的内存映射。

注意:mmc驱动中用到了工作队列~

Notice:通常在发生系统调用时,内核会代表用户空间的进程运行,此时它才能访问用户空间,也只有在此时它才会映射用户空间的内存。

(5)下半部机制的选择

 

对下半部机制的比较
下半部 上下文 顺序执行保障
软中断 中断 没有
tasklet 中断 同类型不能同时执行
工作队列 进程 没有(和进程上下文一样被调度)

 

 

从易用性角度来看:工作队列 > tasklet > 软中断

总结:驱动程序的编写者,需要做两个选择。首先,你是不是需要一个可调度的实体来执行需要推后完成的工作——从根本上来说,你需要推后的工作任务有休眠的需要吗?要是有,那么工作队列就是唯一的选择;否则最好用tasklet。其次,如果必须专注于性能的提高,那么就考虑软中断吧~

(6)使用下半部机制时,即使是在一个单处理器的系统上,避免共享数据的访问也是至关重要的。禁止下半部的函数有local_bh_disable()和local_bh_enable(),这两个函数只能禁止和激活本地处理器的软中断和tasklet。因为工作队列是在进程上下文中运行的,不会涉及异步执行的问题,所以也就没必要禁止它们执行。

 

7、在驱动程序中,要申请中断(注册中断处理程序)

 

[cpp]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. request_threaded_irq(unsigned int irq, irq_handler_t handler,  
  2.              irq_handler_t thread_fn,  
  3.              unsigned long flags, const char *name, void *dev);  


  irq  需要申请的irq编号,对于ARM体系,irq编号通常在平台级的代码中事先定义好,有时候也可以动态申请。

 

 

  handler  中断服务回调函数,该回调运行在中断上下文中,并且cpu的本地中断处于关闭状态,所以该回调函数应该只是执行需要快速响应的操作,执行时间应该尽可能短小,耗时的工作最好留给下面的thread_fn回调处理。

  thread_fn  如果该参数不为NULL,内核会为该irq创建一个内核线程,当中断发生时,如果handler回调返回值是IRQ_WAKE_THREAD,内核将会激活中断线程,在中断线程中,该回调函数将被调用,所以,该回调函数运行在进程上下文中,允许进行阻塞操作

  flags  控制中断行为的位标志,IRQF_XXXX,例如:IRQF_TRIGGER_RISING,IRQF_TRIGGER_LOW,IRQF_SHARED等,在include/linux/interrupt.h中定义。

  name  申请本中断服务的设备名称,同时也作为中断线程的名称,该名称可以在/proc/interrupts文件中显示。

  dev  当多个设备的中断线共享同一个irq时,它会作为handler的参数,用于区分不同的设备。free_irq()函数调用的时候,dev的作用就体现出来了。

8、irq_handler_t的类型定义如下:

 

[cpp]  view plain  copy
 
 print?在CODE上查看代码片派生到我的代码片
  1. typedef irqreturn_t  (*irq_handler_t)(int,void*);  

 

用typedef 定义了一个函数指针类型irq_handler_t,指向的函数原型返回类型为 irqreturn_t  ;它接收的参数类型就是int 和void* 两个参数

9、request_threaded_irq()函数是可以睡眠的,因为request_threaded_irq()-->proc_mkdir()-->proc_create()-->kmalloc(),而kmalloc()是可以睡眠的。所以,不能在中断上下文中调用该函数。

10、先初始化硬件,然后再注册中断处理程序,以防止中断处理程序在设备初始化完成之前就开始执行。

11、free_irq():如果在给定的中断线上没有中断处理程序,则注销响应的处理程序,并禁用其中断线。

12、中断处理程序即使什么工作也不做,至少需要知道产生中断的设备,告诉它已经收到中断了;对于复杂的设备,可能还需要在中断处理器程序中发送和接收数据,以及执行一些扩充的工作。这些扩充的工作尽可能被推迟到下半部(bottom half)去完成。

13、中断线和中断号是两个相同的概念,irq

14、Linux中的中断处理程序是无需重入的,当一个给定的中断处理程序正在执行时,相应的中断号在所有处理器上都是被屏蔽掉;所以,同一个中断处理程序绝对不会被同时调用以处理嵌套中断。

15、进程上下文:一种内核所处的操作模式,此时内核代表进程执行——例如,执行系统调用或者运行内核线程。在进程上下文中,可以通过current宏关联当前进程。又因为进程是以进程上下文的形式连接到内核的,因此,进程上下文可以睡眠,也可以调用调度程序

16、中断上下文:与进程没什么关系,与current宏也没有关系,所以中断上下文不可以睡眠,在中断上下文中不可以调用任何可能睡眠的函数。

17、中断处理程序打断了其他的代码的执行,所以中断上下文中的代码应该简洁、迅速,尽可能把工作从中断处理程序中分离出来,放在下半部执行。

18、中断处理程序栈的设置是一个内核配置项,如果有的话,是1页大小,即4KB

19、控制中断系统的原因归根结底是需要提供同步。禁止中断提供保护机制,防止来自其他中断程序的并发访问,也能够禁止内核抢占;提供保护机制,防止来自其他处理器的并发访问(SMP系统需要考虑)















本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sky-heaven/p/5531416.html,如需转载请自行联系原作者

相关文章
|
11天前
|
算法 Linux 调度
深入理解Linux内核调度器:从基础到优化####
本文旨在通过剖析Linux操作系统的心脏——内核调度器,为读者揭开其高效管理CPU资源的神秘面纱。不同于传统的摘要概述,本文将直接以一段精简代码片段作为引子,展示一个简化版的任务调度逻辑,随后逐步深入,详细探讨Linux内核调度器的工作原理、关键数据结构、调度算法演变以及性能调优策略,旨在为开发者与系统管理员提供一份实用的技术指南。 ####
47 4
|
15天前
|
缓存 算法 Linux
深入理解Linux内核调度器:公平性与性能的平衡####
真知灼见 本文将带你深入了解Linux操作系统的核心组件之一——完全公平调度器(CFS),通过剖析其设计原理、工作机制以及在实际系统中的应用效果,揭示它是如何在众多进程间实现资源分配的公平性与高效性的。不同于传统的摘要概述,本文旨在通过直观且富有洞察力的视角,让读者仿佛亲身体验到CFS在复杂系统环境中游刃有余地进行任务调度的过程。 ####
37 6
|
6天前
|
算法 Linux 开发者
Linux内核中的锁机制:保障并发控制的艺术####
本文深入探讨了Linux操作系统内核中实现的多种锁机制,包括自旋锁、互斥锁、读写锁等,旨在揭示这些同步原语如何高效地解决资源竞争问题,保证系统的稳定性和性能。通过分析不同锁机制的工作原理及应用场景,本文为开发者提供了在高并发环境下进行有效并发控制的实用指南。 ####
|
14天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
42 9
|
13天前
|
缓存 负载均衡 Linux
深入理解Linux内核调度器
本文探讨了Linux操作系统核心组件之一——内核调度器的工作原理和设计哲学。不同于常规的技术文章,本摘要旨在提供一种全新的视角来审视Linux内核的调度机制,通过分析其对系统性能的影响以及在多核处理器环境下的表现,揭示调度器如何平衡公平性和效率。文章进一步讨论了完全公平调度器(CFS)的设计细节,包括它如何处理不同优先级的任务、如何进行负载均衡以及它是如何适应现代多核架构的挑战。此外,本文还简要概述了Linux调度器的未来发展方向,包括对实时任务支持的改进和对异构计算环境的适应性。
36 6
|
14天前
|
缓存 Linux 开发者
Linux内核中的并发控制机制:深入理解与应用####
【10月更文挑战第21天】 本文旨在为读者提供一个全面的指南,探讨Linux操作系统中用于实现多线程和进程间同步的关键技术——并发控制机制。通过剖析互斥锁、自旋锁、读写锁等核心概念及其在实际场景中的应用,本文将帮助开发者更好地理解和运用这些工具来构建高效且稳定的应用程序。 ####
35 5
|
14天前
|
算法 Unix Linux
深入理解Linux内核调度器:原理与优化
本文探讨了Linux操作系统的心脏——内核调度器(Scheduler)的工作原理,以及如何通过参数调整和代码优化来提高系统性能。不同于常规摘要仅概述内容,本摘要旨在激发读者对Linux内核调度机制深层次运作的兴趣,并简要介绍文章将覆盖的关键话题,如调度算法、实时性增强及节能策略等。
|
15天前
|
存储 监控 安全
Linux内核调优的艺术:从基础到高级###
本文深入探讨了Linux操作系统的心脏——内核的调优方法。文章首先概述了Linux内核的基本结构与工作原理,随后详细阐述了内核调优的重要性及基本原则。通过具体的参数调整示例(如sysctl、/proc/sys目录中的设置),文章展示了如何根据实际应用场景优化系统性能,包括提升CPU利用率、内存管理效率以及I/O性能等关键方面。最后,介绍了一些高级工具和技术,如perf、eBPF和SystemTap,用于更深层次的性能分析和问题定位。本文旨在为系统管理员和高级用户提供实用的内核调优策略,以最大化Linux系统的效率和稳定性。 ###
|
14天前
|
Java Linux Android开发
深入探索Android系统架构:从Linux内核到应用层
本文将带领读者深入了解Android操作系统的复杂架构,从其基于Linux的内核到丰富多彩的应用层。我们将探讨Android的各个关键组件,包括硬件抽象层(HAL)、运行时环境、以及核心库等,揭示它们如何协同工作以支持广泛的设备和应用。通过本文,您将对Android系统的工作原理有一个全面的认识,理解其如何平衡开放性与安全性,以及如何在多样化的设备上提供一致的用户体验。
|
16天前
|
Linux 数据库
Linux内核中的锁机制:保障并发操作的数据一致性####
【10月更文挑战第29天】 在多线程编程中,确保数据一致性和防止竞争条件是至关重要的。本文将深入探讨Linux操作系统中实现的几种关键锁机制,包括自旋锁、互斥锁和读写锁等。通过分析这些锁的设计原理和使用场景,帮助读者理解如何在实际应用中选择合适的锁机制以优化系统性能和稳定性。 ####
34 6
下一篇
无影云桌面