下半部的历史:
下半部机制 | 状态 |
BH | 在2.5中移除 |
任务队列 task queues | 在2.5中移除 |
软中断 softirq | 从2.3开始引入 |
tasklet | 从2.3开始引入 |
工作队列 work queue | 从2.5开始引入 |
软中断是在编译期间静态分配的。
软中断由下述结构体表示,定义在<linux/interrupt.h>中:
1 2 3 | struct softirq_action { void (*action)( struct softirq_action *); }; |
kernel/softirq.c 中定义了一个32个软中断的数组:
1 | static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp; |
一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是硬中断。不过,其它中断(甚至是相同类型的中断)可以在其它处理器上同时执行。
软中断必须被标记才会被执行,这被称作触发软中断(raising the softirq)。在下列地方,待处理的软中断会被检查和执行:
从一个硬件中断代码处返回时
在ksoftirqd内核线程中
在那些显式检查和执行处理的软中断的代码中,如网络子系统。
目前使用了9种软中断,如下:
ID | 优先级 | 描述 |
HI_SOFTIRQ | 0 | 优先级高的tasklet |
TIMER_SOFTIRQ | 1 | 定时器的下半部 |
NET_TX_SOFTIRQ | 2 | 发送网络数据包 |
NET_RX_SOFTIRQ | 3 | j接收网络数据包 |
BLOCK_SOFTIRQ | 4 | BLOCK装置 |
TASKLET_SOFTIRQ | 5 | 正常优先级的tasklet |
SCHED_SOFTIRQ | 6 | 调度程序 |
HRTIMER_SOFTIRQ | 7 | 高分辨率定时器 |
RCU_SOFTIRQ | 8 | RCU锁定 |
注册处理程序:
1 | open_softirq(NET_TX_SOFTIRQ, net_tx_action); |
触发软中断
1 | raise_softirq(NET_TX_SOFTIRQ); |
上述接口会在禁止中断的情况下修改当前CPU的软中断raise标志。如果不是在中断中raise,将唤醒ksoftirqd线程处理软中断。
ksoftirqd
在中断返回的时候,内核会检查并处理所有的软中断,但是那些不在中断中raise的softirq,由每CPU内核ksoftirqd线程负责执行。
在下半部之间加锁
记住:下半部可能在任何时候执行。
禁止下半部
一般禁止下半部是不安全的,通常的做法是先获得一个锁,然后在禁止下半部。如果是编写内核核心代码,可能仅需要禁止下半部。
1 2 | void local_bh_disable() void local_bh_enable() |
它们实际也是修改preempt_count,不过softirq使用preempt_count的0xff00掩码表示的位段。因此上述两个函数可以嵌套执行。
在local_bh_enable的时候,会检查是否在中断上上文,是否有pending的软中断,并执行软中断。