下半部的主要任务就是执行中断相关的,不在中断处理器中执行的工作。如何将中断任务分为上下两部分分别执行呢,如下提供一些参考:
如果工作对时间敏感,那么在中断处理器中执行。
如果工作与硬件相关,在中断处理器中执行。
如果工作需要确保另一个中断不能打断它,在中断处理器中执行。
对于其他的情况,一般考虑在下半部中执行。
通常就尽量使中断处理程序快速完成,将一些不需要迅速处理的工作推迟到下半部中去执行。推迟是指现在暂时不执行,也不是在将来的某个特定时刻执行,而是在系统不是很忙的时候再执行。总的来说,上半部代码执行时一些或所有中断被禁用,而下半部代码在执行的时候所有的中断是打开的。
另一种推迟工作的机制是内核计时器,与下半部机制不同,计时器将工作推迟到某个指定的时间去执行。历史上和现在正在使用的下半部机制如下表所示:
BottomHalf |
Status |
BH |
在2.5中被移除 |
Taskqueues Softirq Tasklet |
在2.5中被移除 2.3中开始出现 |
Workqueues |
2.5中开始出现 |
微线程与软中断不同的地方是:微线程在同一时刻只能在一个处理器上运行。另外,不同的微线程可同时运行于不同的处理器上。
下半部之间的同步
微线程相对自己来说是串行的,即相同的微线程不会同时运行,即便是在不同的处理器上。所以无需考虑微线程之间的同步。
软中断没有提供串行化,所以所有共享的数据需要适当的锁定。
在进程上下文中,访问下半部共享数据,需要禁用下半部处理并在访问数据之前获得一个锁。
在中断上下文中,访问下半部共享数据,需要禁用中断并在访问数据之前获得一个锁。
任何在一个工作队列中的共享数据也需要锁定。
禁用下半部
通常情况下,仅仅禁用下半部是不够的,需要获得一个锁,并禁用下半部,特别是在驱动程序中。对于内核核心代码,只需要禁用下半部就行了。
禁用下半部的一些函数如下:
Method |
Description |
void local_bh_disable() |
Disables softirq andtasklet processing on the local |
|
processor |
void local_bh_enable() |
Enables softirq and taskletprocessing on the local |
|
processor |
这些调用可以被嵌套,当然它们调用的次数应该相同。即local_bh_disable()与local_bh_enable()函数之间的调用次数应该相同。这些函数通过preempt_count(内核抢占与用户相同的计数器)来维护每个任务的计数器。这些函数对每个支持的平台来说是唯一的,下面是一些相同代码:
/* * disable local bottom halves by incrementing the preempt_count */ void local_bh_disable(void) { struct thread_info *t = current_thread_info(); t ->preempt_count += SOFTIRQ_OFFSET; } /* * decrement the preempt_count - this will ‘automatically’ enable * bottom halves if the count returns to zero * * optionally run any bottom halves that are pending */ void local_bh_enable(void) { struct thread_info *t = current_thread_info(); t->preempt_count -= SOFTIRQ_OFFSET; /* * is preempt_count zero and are any bottom halves pending? * if so, run them */ if (unlikely(!t->preempt_count && softirq_pending(smp_processor_id()))) do_softirq(); }
这些函数只对软中断和微线程有意义。