【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之七姑八姨

本文主要是介绍【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之七姑八姨,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

中断服务下半部之七姑八姨

 

Sailor_forever  sailing_9806@163.com 转载请注明

http://blog.csdn.net/sailor_8318/archive/2008/07/13/2645131.aspx

 

【摘要】本文分析了中断服务下半部存在的必要性,接着介绍了上下半部的分配原则,最后分析了各种下半部机制的历史渊源,简单介绍了各种机制的特点。

【关键字】下半部,bottom halfBHtaskletsoftirq工作队列内核定时器

 

 

1       下半部,我思故我在

中断处理程序是内核中很有用的—实际上也是必不可少的—部分。但是,由于本身存在一些局限,所以它只能完成整个中断处理流程的上半部分。这些局限包括:

²      中断处理程序以异步方式执行并且它有可能会打断其他重要代码(甚至包括其他中断处理程序)的执行。因此,为了避免被打断的代码停止时间过长,中断处理程序应该执行得越快越好。

²      如果当前有一个中断处理程序正在执行,在最好的情况下与该中断同级的其他中断会被屏蔽,在最坏的情况下,当前处理器上所有其他中断都会被屏蔽。因此,仍应该让它们执行得越快越好。

²      由于中断处理程序往往需要对硬件进行操作,所以它们通常有很高的时限要求。

²      中断处理程序不在进程上下文中运行,所以它们不能阻塞。这限制了它们所做的事情。

   

现在,为什么中断处理程序只能作为整个硬件中断处理流程一部分的原因就很明显了。我们必须有一个快速、异步、简单的处理程序负责对硬件做出迅速响应并完成那些时间要求很严格的操作。中断处理程序很适合于实现这些功能,可是,对于那些其他的、对时间要求相对宽松的任务,就应该推后到中断被激活以后再去运行。

 

这样,整个中断处理流程就被分为了两个部分,或叫两半。第一个部分是中断处理程序(上半部),内核通过对它的异步执行完成对硬件中断的即时响应。下半部(bottom half)负责其他响应。

 

2       上下半部分家产的原则

下半部的任务就是执行与中断处理密切相关但中断处理程序本身不执行的工作。在理想的情况下,最好是中断处理程序将所有工作都交给下半部分执行,因为我们希望在中断处理程序中完成的工作越少越好(也就是越快越好)。我们期望中断处理程序能够尽可能快地返回。

 

但是,中断处理程序注定要完成一部分工作。例如,中断处理程序几乎都需要通过操作硬件对中断的到达进行确认。有时它还会从硬件拷贝数据。因为这些工作对时间非常敏感,所以只能靠中断处理程序自己去完成。

 

剩下的几乎所有其他工作都是下半部执行的目标。例如,如果你在上半部中把数据从硬件拷贝到了内存,那么当然应该在下半部中处理它们。遗憾的是,并不存在严格明确的规定来说明到底什么任务应该在哪个部分中完成—如何做决定完全取决于驱动程序开发者自己的判断。记住,中断处理程序会异步执行,并且即使在最好的情况下它也会锁定当前的中断线。因此将中断处理程序持续执行的时间缩短到最小非常重要。上半部和下半部之间划分应大致遵循以下规则:

²      如果一个任务对时间非常敏感,将其放在中断处理程序中执行;

²      如果一个任务和硬件相关,将其放在中断处理程序中执行;

²      如果一个任务要保证不被其他中断(特别是相同的中断)打断,将其放在中断处理程序中执行;

²      其他所有任务,考虑放置在下半部执行。

 

在决定怎样把你的中断处理流程中的工作划分到上半部和下半部中去的时候,问问自己什么必须放进上半部而什么可以放进下半部。通常,中断处理程序要执行得越快越好。

 

理解为什么要让工作推后执行以及在什么时候推后执行非常关键。我们希望尽量减少中断处理程序中需要完成的工作量,因为在它运行的时候当前的中断线在所有处理器上都会被屏蔽。更糟糕的是如果一个处理程序是SA_ INTERRUPT类型,它执行的时候会禁止所有本地中断。而缩短中断被屏蔽的时间对系统的响应能力和性能都至关重要。解决的方法就是把一些工作放到以后去做。

 

但具体放到以后的什么时候去做呢?在这里,以后仅仅用来强调不是马上而已,理解这一点相当重要。下半部并不需要指明一个确切时间,只要把这些任务推迟一点,让它们在系统不太繁忙并且中断恢复后执行就可以了。通常下半部在中断处理程序一返回就会马上运行。下半部执行的关键在于当它们运行的时候,允许响应所有的中断。

 

不仅仅是Linux,许多操作系统也把处理硬件中断的过程分为两个部分。上半部分简单快速,执行的时候禁止一些或者全部中断。下半部分稍后执行,而且执行期间可以响应所有的中断。这种设计可使系统处干中断屏蔽状态的时间尽可能的短,以此来提高系统的响应能力。

 

3       下半部之七姑八姨

和上半部分只能通过中断处理程序实现不同,下半部可以通过多种机制实现。这些用来实现下半部的机制分别由不同的接口和子系统组成。实际上,在Linux发展的过程中曾经出现过多种下半部机制。让人倍受困扰的是,其中不少机制名字起得很相像,甚至还有一些机制名字起得辞不达意。

 

最早的Linux只提供“bottom half”这种机制用于实现下半部。这个名字在那个时候毫无异义,因为当时它是将工作推后的惟一方法。这种机制也被称为“BH",我们现在也这么叫它,以避免和“下半部”这个通用词汇混淆。

 

BH接口也非常简单。它提供了一个静态创建、由32bottom half组成的数组。上半部通过一个32位整数中的一位来标识出哪个bottom half可以执行。每个BH都在全局范围内进行同步。对于本地CPU,严格的串行执行,当被中断重入后,若发现中断前已经在执行BH则退出。即使分属于不同的处理器,也不允许任何两个bottom half同时执行。若发现另一CPU正在执行,则退出。这种机制使用方便却不够灵活,简单却有性能瓶颈。

 

不久,内核开发者们就引入了任务队列(task queue)机制来实现工作的推后执行,并用它来代替BH机制。内核为此定义了一组队列其中每个队列都包含一个由等待调用的函数组成链表,这样就相当于实现了二级链表,扩展了BH32个的限制。根据其所处队列的位置,这些函数会在某个时刻被执行。驱动程序可以把它们自己的下半部注册到合适的队列上去。这种机制表现得还不错,但仍不够灵活,没法代替整个BH接口。对于一些性能要求较高的子系统,像网络部分,它也不能胜任。

 

2.3这个开发版本中,内核开发者引入了tasklet和软中断softirq。如果无须考虑和过去开发的驱动程序兼容的话,软中断和tasklet可以完全代替BH接口。

 

软中断是一组静态定义的下半部接口,有32个,可以在所有处理器上同时执行—即使两个类型相同也可以。

 

tasklet是一种基于软中断实现的灵活性强、动态创建的下半部实现机制。两个不同类型的tasklet可以在不同的处理器上同时执行,但类型相同的tasklet不能同时执行。tasklet其实是一种在性能和易用性之间寻求平衡的产物。对于大部分下半部处理来说,用tasklet就足够了。像网络这样对性能要求非常高的情况才需要使用软中断。可是,使用软中断需要特别小心,因为两个相同的软中断在SMP上有可能同时被执行。此外,软中断由数组组织,还必须在编译期间就进行静态注册,即与某个软中断号关联。与此相反,tasklet为某个固定的软中断号,经过二级扩展,维护了一个链表,因此可以动态注册删除。

 

在开发2.5版本的内核时BH接口最终被弃置了,所有的BH使用者必须转而使用其他下半部接口。此外,任务队列接口也被工作队列接口取代了。工作队列是一种简单但很有用的方法,它们先对要推后执行的工作排队,稍后在进程上下文中执行它们。

 

另外一个可以用于将工作推后执行的机制是内核定时器。不像其他下半部机制,内核定时器把操作推迟到某个确定的时间段之后执行。也就是说,尽管本章讨论的其他机制可以把操作推后到除了现在以外的任何时间进行,但是当你必须保证在一个确定的时间段过去以后再运行时,你应该使用内核定时器。但是执行定时器注册的函数时,仍然需要使用软中断机制,即定时器引入了一个固定延时和一个软中断的可变延时。

 

BH转换为软中断或者tasklet并不是轻而易举的事,因为BH是全局同步的,因此,在其执行期间假定没有其他BH在执行。但是,这种转换最终还是在内核2.5中实现了。

 

 “下半部(bottom half)”是一个操作系统通用词汇,用于指代中断处理流程中推后执行的那一部分,之所以这样命名是因为它表示中断处理方案一半的第二部分或者下半部。所有用于实现将工作推后执行的内核机制都被称为“下半部机制”。

 

综上所述,在2.6这个当前版本中,内核提供了三种不同形式的下半部实现机制:软中断、tasklet和工作队列。tasklet通过软中断实现,而工作队列与它们完全不同。下半部机制的演化历程如下:

   

 

这篇关于【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】中断服务下半部之七姑八姨的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/284596

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信

Java服务实现开启Debug远程调试

《Java服务实现开启Debug远程调试》文章介绍如何通过JVM参数开启Java服务远程调试,便于在线上排查问题,在IDEA中配置客户端连接,实现无需频繁部署的调试,提升效率... 目录一、背景二、相关图示说明三、具体操作步骤1、服务端配置2、客户端配置总结一、背景日常项目中,通常我们的代码都是部署到远程