version=2.6.34
下半部
linux中凡是推后执行的任务都可以叫下半部
中断的下半部有三种实现方式
| 下半部机制 | 描述 | 状态 | 使用 |
|---|---|---|---|
| 软中断(softirq) | 静态注册的一种下半部机制,共有32种,当前系统共注册了9种。 一般软中断实现都是以单cpu上执行的,这样加锁粒度小,性能就高。 |
从2.3开始引入 | - 网络子系统和scsi系统使用软中断。 - 内核定时器和所有tasklet都是建立在软中断上。 |
| tasklet | 动态注册,基于软中断实现。 | 从2.3开始引入 | 除了特殊情况,一般都选择tasklet来实现。 |
| 工作队列(work queue) | 推后由进程去执行的机制。 | 从2.5开始引入 |
软中断
定义软中断
软中断是在编译期间静态分配的,分配就不会释放了,不像tasklet可以动态的注册和释放。
实现软中断
软中断由softirq_action结构表示,定义在
struct softirq_action {void (*action)(struct softirq_action *); // 方法名action,参数为结构本身。}
action函数原型由各种软中断自己实现,然后用open_softirq注册时以softirq_action[nr].action=action的形式写入到softirq_vec数组中。
内核运行一个软中断处理程序的时候,就会执行action函数。
一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是中断处理程序。不过其他软中断(包括同类型的)可以在其他处理器上同时执行。
1、软中断类型共有10种,定义在中:
enum{HI_SOFTIRQ=0, // 0 高优先级软中断队列TIMER_SOFTIRQ, // 1 定时器的下半部NET_TX_SOFTIRQ, // 2 发送网络数据包NET_RX_SOFTIRQ, // 3 接收网络数据包BLOCK_SOFTIRQ, // 4 块设备操作BLOCK_IOPOLL_SOFTIRQ, // 5 块设备iopoll轮询机制的软中断TASKLET_SOFTIRQ, // 6 tasklet,软中断的一种,驱动最常用的SCHED_SOFTIRQ, // 7 进程调度中使用的软中断,工作队列(下半部实现的一种)中使用的HRTIMER_SOFTIRQ, // 8 高精度定时器中使用RCU_SOFTIRQ, // 9 rcu 中锁定使用的软中断 rcu软中断是最后一个执行的NR_SOFTIRQS // 10 能在内核中使用的软中断数量};
使用enum结构定义软中断类型,方便从0开始自增计算类型值,而且如果同一条中断线上注册了多个软中断,可以按值从小到大的优先级依次执行;此外在hi和rcu之间插入任何新定义的类型;如果需要,可以调高或调低某种类型的执行优先级。
2、注册函数open_softirq,定义在中:
void open_softirq(int nr, void (*action)(struct softirq_action *)){softirq_vec[nr].action = action;}
nr为软中断类型或叫序号,softirq_action是该nr对应的处理函数或叫句柄
3、使用软中断,共有8处,包含9种中断类型:
1)、软中断初始化时,直接注册两个,在
// 注册tasklet和高优先级的软中断,并指定处理函数open_softirq(TASKLET_SOFTIRQ, tasklet_action);open_softirq(HI_SOFTIRQ, tasklet_hi_action);
tasklet_softirq注册的tasklet是下半部的另一种实现方式。
2)、定时器初始化时,注册一个 TIMER_SOFTIRQ,在
void __init init_timers(void){// ...open_softirq(TIMER_SOFTIRQ, run_timer_softirq);}
3)、进程调度初始化时,注册一个SCHED_SOFTIRQ,在
void __init sched_init(void){// ...open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);// ...}
4)、block块设备的初始化时,注册一个BLOCK_SOFTIRQ,在
static __init int blk_softirq_init(void){// ...open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);// ...}
5)、创建block-iopoll块设备iopoll轮询时,注册一个BLOCK_IOPOLL_SOFTIRQ,在
static __init int blk_iopoll_setup(void){// ...open_softirq(BLOCK_IOPOLL_SOFTIRQ, blk_iopoll_softirq);// ...}
6)、初始化高精度定时器时创建一个HRTIMER_SOFTIRQ,在
void __init hrtimers_init(void){// ...#ifdef CONFIG_HIGH_RES_TIMERSopen_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);#endif// ...}
7)、rcutiny(read-copy update,数据同步的一种,主要面向链表,rcutiny主要针对单cpu,尤其是嵌入式系统)初始化时,注册一个RCU_SOFTIRQ,在
void __init rcu_init(void){open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);}
或
rcutree (组织多核cpu为一个树结构)初始化时,注册一个RCU_SOFTIRQ,在
void __init rcu_init(void){// ...open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);// ...}
8)、网络设备初始化时,注册两个,一个是发送数据时NET_TX_SOFTIRQ,一个是接收数据时NET_RX_SOFTIRQ,在
static int __init net_dev_init(void){// ...open_softirq(NET_TX_SOFTIRQ, net_tx_action);open_softirq(NET_RX_SOFTIRQ, net_rx_action);// ...}
4、执行软中断
触发软中断后才能执行,触发点有3个:
- 从一个硬中断代码处返回时,由汇编调用。
- 在ksoftirqd内核线程中调用
- 在那些显式和执行待处理的软中断的代码中,如网络子系统中和scsi中。
