LKD 笔记:底半部和推迟的工作

底半部

中断处理函数是异步执行的,在执行时,至少当前(产生中断信号的这条)中断线(在所有的处理器上)是关闭的。[另一种情况是当前的中断线在所有处理器上是关闭的再加上本地处理器上所有的中断线都是关闭的] 它们的执行时间越短越好。以下是几个提示,如何将工作划分为顶半部(中断处理函数中)和底半部:

  • 如果这个工作对执行时间要求很严格,将它放到中断处理函数中
  • 如果这个工作与硬件相关,将它放到中断处理函数中
  • 如果这个工作需要确保另一个中断(尤其是相同的中断)不会中断它,将它放到中断处理函数中
  • 其他的任何情形,将工作放到底半部

底半部在之后的某个时间点(而不是现在立即)运行,可能是任意一个系统不繁忙且中断是打开的时刻。通常,底半部在中断返回后就立即运行。关键是它们是在中断打开的状态下运行的。

底半部有几种类型,分别是软中断(Softirqs)、Tasklets、工作队列(Work Queues)。

软中断(Softirqs)

执行软中断

软中断在使用前需要调用 open_softirq()注册。注册后的软中断需要被标记(使用raise_softirq()函数)才能运行,这个行为称作抛出一个软中断。通常,在中断处理函数返回前会标记一个软中断,以便软中断在某个合适的时间点运行。待执行的软中断在以下几个地方检查:

  • 在硬件中断代码路径的返回中
  • 在 ksoftirqd 内核线程中
  • 在任何显示检查待执行的软中断的代码中,比如网络子系统

不管什么方法被调用,软中断的执行都发生在 __do_softirq()函数中,它被do_softirq()调用。

使用软中断

软中断是为系统中最重要的,对执行时间要求很严格的处理过程保留的。当前,只有网络子系统和和块设备驱动程序直接使用软中断。另外,内核定时器和 tasklets 是基于软中断实现的。

Tasklets

Tasklets 是基于软中断的一种底半部机制。它们和任务没有任何关系[看名字容易产生误解]。Tasklets 和软中断的行为很相似,但是有一个更简单的接口和加锁规则。

工作队列(Work Queues)

工作队列将延迟执行的工作放到一个内核线程中执行,因此它在进程上下文中运行。更重要的是,工作队列可以被调度,因此也就能够睡眠。

底半部机制中的锁

相同的 tasklet 不能同时运行,在多个处理器上也不行。因此相同的 tasklet 之间共享数据不需要加锁,不同的 tasklet 之间共享数据才需要合适的锁。

因为软中断不提供序列化(相同的软中断可以同时运行),所以所有在软中断中共享的数据需要合适的锁。

如果进程上下文中的代码和底半部共享数据,你需要关掉底半部的处理并获取一个锁。这能确保本地和多处理保护而且防止死锁。

如果中断上下文中的代码和底半部共享数据,你需要关掉中断并获取一个锁。这也能确保本地和多处理保护而且防止死锁。

在工作队列中共享的任何数据都需要锁。因为工作队列运行在进程上下文。

关掉底半部处理

local_bh_disable()函数关掉本地处理器上的软中断和tasklet的处理。

local_bh_enable()函数开启本地处理器上的软中断和tasklet的处理。

你可能感兴趣的:(LKD,笔记)