Linux(内核剖析):27---中断下半部之(下半部机制的选择、在下半部之间加锁、禁止下半部(local_bh_disable、local_bh_enable))
发布日期:2021-06-29 22:33:08 浏览次数:3 分类:技术文章

本文共 2318 字,大约阅读时间需要 7 分钟。

一、下半部机制的选择

  • 在各种不同的下半部实现机制之间做出选择是很重要的。在当前的2.6版内核中,有三种可能的选择:软中断、tasklet和工作队列。tasklet基于软中断实现,所以两者很相近。工作队列机 制与它们完全不同,它靠内核线程实现
  • 从设计的角度考虑,软中断提供的执行序列化的保障最少。这就要求软中断处理函数必须格 外小心地采取一些步骤确保共享数据的安全,两个甚至更多相同类别的软中断有可能在不同的处 理器上同时执行。如果被考察的代码本身多线索化的工作就做得非常好,比如网络子系统,它完全使用单处理器变量,那么软中断就是非常好的选择。对于时间要求严格和执行频率很高的应用来说,它执行得也最快
  • 如果代码多线索化考虑得并不充分,那么选择tasklet意义更大。它的接口非常简单,而且,由于两个同种类型的tasklet不能同时执行,所以实现起来也会简单一些。tasklet是有效的软中断,但不能并发运行。驱动程序开发者应当尽可能选择tasklet而不是软中断,当然,如果准备利用每一处理器上的变量或者类似的情形,以确保软中断能安全地在多个处理器上并发地运行,那么还是选择软中断
  • 如果你需要把任务推后到进程上下文中完成,那么在这三者中就只能选择工作队列了。如果进程上下文并不是必须的条件(明确点说,就是如果并不需要睡眠),那么软中断和tasklet可能更合适。工作队列造成的开销最大,因为它要牵扯到内核线程甚至是上下文切换。这并不是说工作队列的效率低,如果每秒钟有几千次中断,就像网络子系统时常经历的那样,那么采用其他的机制可能更合适一些。尽管如此,针对大部分情况,工作队列都能提供足够的支持
  • 如果讲到易于使用,工作队列就当仁不让了。使用缺省的events队列简直不费吹灰之力。 接下来是tasklet,它的接口也很简单。最后才是软中断,它必须静态创建,并且需要慎重考虑其实现
  • 下标是对三种下半部接口的比较

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

二、在下半部之间加锁

  • 到现在为止,我们还没讨论过锁机制,这是一个非常有趣且广泛的话题,我将在后面的文章里仔细讨论它。不过,在这里还是应该对它的重要性有所了解:在使用下半部机制时,即使是在一个单处理器的系统上,避免共享数据被同时访问也是至关重要的。记住,一个下半部实 际上可能在任何时候执行
  • 使用tasklet的一个好处在于,它自己负责执行的序列化保障 :两个相同类型的tasklet不允许同时执行,即使在不同的处理器上也不行。这意味着你无须为intra-tasklet的同步问题操心了。tasklet之间的同步(就是当两个不同类型的tasklet共享同一数据时)需要正确使用锁机制
  • 如果进程上下文和一个下半部共享数据,在访问这些数据之前,你需要禁止下半部的处理并 得到锁的使用权。做这些是为了本地和SMP的保护并且防止死锁的出现
  • 如果中断上下文和一个下半部共享数据,在访问数据之前,你需要禁止中断并得到锁的使用 权。所做的这些也是为了本地和SM P的保护并且防止死锁的出现
  • 任何在工作队列中被共享的数据也需要使用锁机制。其中有关锁的要点和在一般内核代码中没什么区别,因为工作队列本来就是在进程上下文中执行的
  • 在后面会描述“内核同步”的文章中我每回介绍如何保护下半部使用的数据

三、禁止下半部(local_bh_disable、local_bh_enable)

  • 一般单纯禁止下半部的处理是不够的。为了保证共享数据的安全,更常见的做法是,先得到 一个锁然后再禁止下半部的处理。驱动程序中通常使用的都是这种方法(在后面“内核同步”文章介绍)。 然而,如果你编写的是内核的核心代码,你也可能仅需要禁止下半部就可以了
  • 如果需要禁止所有的下半部处理(明确点说,就是所有的软中断和所有的tasklet) , 可以调用local_bh_disable()函数。允许下半部进行处理,可以调用local_bh_enable()函数。没错,这些函数的命名也有问题;可是既然BH接口早就让位给软中断了,那么谁又会去改这些名称呢
  • 下标是这些函数的一份摘要

  • 这些函数有可能被嵌套使用— 最后被调用的local_bh_enable()最终激活下半部。比如,第 一次调用local_bh_disable(),则本地软中断处理被禁止;如果local_bh_disable()被调用三次,则本地处理仍然被禁止;只有当第四次调用local_bh_enable()时,软中断处理才被重新激活
  • 函数通过preempt_count(很有意思,还是这个计数器,内核抢占的时候用的也是它)为每个进程维护一个计数器 。当计数器变为0 时,下半部才能够被处理。因为下半部的处理已经被禁止,所以local_bh_enable()函还需要检査所有现存的待处理的下半部并执行它们
  • 这些函数与硬件体系结构相关,它们位于<asm/softirq.h>,通常由一些复杂的宏实现。下面是为那些好奇的人准备了 C 语言的近似描述:

  • 这些函数并不能禁止工作队列的执行。因为工作队列是在进程上下文中运行的,不会涉及 异步执行的问题,所以也就没有必要禁止它们执行。由于软中断和tasklet是异步发生的(就是说,在中断处理返回的时候),所以,内核代码必须禁止它们。另一方面,对于工作队列来说, 它保护共享数据所做的工作和其他任何进程上下文中所做的都差不多

转载地址:https://dongshao.blog.csdn.net/article/details/104024772 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:TCP/IP卷一:65---TCP连接管理之(TCP连接的建立与终止、TCP半关闭、同时打开/同时关闭、初始化序列号、连接与转换器(NAT))
下一篇:TCP/IP卷一:64---TCP基础之(传输控制协议(TCP)概述、TCP的可靠性、TCP报文格式与封装)

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月14日 12时18分17秒