version=2.6.34

下半部

linux中凡是推后执行的任务都可以叫下半部
中断的下半部有三种实现方式

下半部机制 描述 状态 使用
软中断(softirq) 静态注册的一种下半部机制,共有32种,当前系统共注册了9种。
一般软中断实现都是以单cpu上执行的,这样加锁粒度小,性能就高。
从2.3开始引入
- 网络子系统和scsi系统使用软中断。
- 内核定时器和所有tasklet都是建立在软中断上。
tasklet 动态注册,基于软中断实现。 从2.3开始引入 除了特殊情况,一般都选择tasklet来实现。
工作队列(work queue) 推后由进程去执行的机制。 从2.5开始引入

软中断

定义软中断

软中断是在编译期间静态分配的,分配就不会释放了,不像tasklet可以动态的注册和释放。

实现软中断

软中断由softirq_action结构表示,定义在中:

  1. struct softirq_action {
  2. void (*action)(struct softirq_action *); // 方法名action,参数为结构本身。
  3. }

action函数原型由各种软中断自己实现,然后用open_softirq注册时以softirq_action[nr].action=action的形式写入到softirq_vec数组中。
内核运行一个软中断处理程序的时候,就会执行action函数。

一个软中断不会抢占另一个软中断,唯一可以抢占软中断的是中断处理程序。不过其他软中断(包括同类型的)可以在其他处理器上同时执行。

1、软中断类型共有10种,定义在中:

  1. enum
  2. {
  3. HI_SOFTIRQ=0, // 0 高优先级软中断队列
  4. TIMER_SOFTIRQ, // 1 定时器的下半部
  5. NET_TX_SOFTIRQ, // 2 发送网络数据包
  6. NET_RX_SOFTIRQ, // 3 接收网络数据包
  7. BLOCK_SOFTIRQ, // 4 块设备操作
  8. BLOCK_IOPOLL_SOFTIRQ, // 5 块设备iopoll轮询机制的软中断
  9. TASKLET_SOFTIRQ, // 6 tasklet,软中断的一种,驱动最常用的
  10. SCHED_SOFTIRQ, // 7 进程调度中使用的软中断,工作队列(下半部实现的一种)中使用的
  11. HRTIMER_SOFTIRQ, // 8 高精度定时器中使用
  12. RCU_SOFTIRQ, // 9 rcu 中锁定使用的软中断 rcu软中断是最后一个执行的
  13. NR_SOFTIRQS // 10 能在内核中使用的软中断数量
  14. };

使用enum结构定义软中断类型,方便从0开始自增计算类型值,而且如果同一条中断线上注册了多个软中断,可以按值从小到大的优先级依次执行;此外在hi和rcu之间插入任何新定义的类型;如果需要,可以调高或调低某种类型的执行优先级。

2、注册函数open_softirq,定义在中:

  1. void open_softirq(int nr, void (*action)(struct softirq_action *))
  2. {
  3. softirq_vec[nr].action = action;
  4. }

nr为软中断类型或叫序号,softirq_action是该nr对应的处理函数或叫句柄

3、使用软中断,共有8处,包含9种中断类型:

1)、软中断初始化时,直接注册两个,在中:

  1. // 注册tasklet和高优先级的软中断,并指定处理函数
  2. open_softirq(TASKLET_SOFTIRQ, tasklet_action);
  3. open_softirq(HI_SOFTIRQ, tasklet_hi_action);

tasklet_softirq注册的tasklet是下半部的另一种实现方式。


2)、定时器初始化时,注册一个 TIMER_SOFTIRQ,在中:**

  1. void __init init_timers(void)
  2. {
  3. // ...
  4. open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
  5. }

3)、进程调度初始化时,注册一个SCHED_SOFTIRQ,在中:

  1. void __init sched_init(void)
  2. {
  3. // ...
  4. open_softirq(SCHED_SOFTIRQ, run_rebalance_domains);
  5. // ...
  6. }

4)、block块设备的初始化时,注册一个BLOCK_SOFTIRQ,在中:

  1. static __init int blk_softirq_init(void)
  2. {
  3. // ...
  4. open_softirq(BLOCK_SOFTIRQ, blk_done_softirq);
  5. // ...
  6. }

5)、创建block-iopoll块设备iopoll轮询时,注册一个BLOCK_IOPOLL_SOFTIRQ,在中:

  1. static __init int blk_iopoll_setup(void)
  2. {
  3. // ...
  4. open_softirq(BLOCK_IOPOLL_SOFTIRQ, blk_iopoll_softirq);
  5. // ...
  6. }

6)、初始化高精度定时器时创建一个HRTIMER_SOFTIRQ,在中:

  1. void __init hrtimers_init(void)
  2. {
  3. // ...
  4. #ifdef CONFIG_HIGH_RES_TIMERS
  5. open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
  6. #endif
  7. // ...
  8. }

7)、rcutiny(read-copy update,数据同步的一种,主要面向链表,rcutiny主要针对单cpu,尤其是嵌入式系统)初始化时,注册一个RCU_SOFTIRQ,在中:

  1. void __init rcu_init(void)
  2. {
  3. open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
  4. }




rcutree (组织多核cpu为一个树结构)初始化时,注册一个RCU_SOFTIRQ,在中:

  1. void __init rcu_init(void)
  2. {
  3. // ...
  4. open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
  5. // ...
  6. }

8)、网络设备初始化时,注册两个,一个是发送数据时NET_TX_SOFTIRQ,一个是接收数据时NET_RX_SOFTIRQ,在中:

  1. static int __init net_dev_init(void)
  2. {
  3. // ...
  4. open_softirq(NET_TX_SOFTIRQ, net_tx_action);
  5. open_softirq(NET_RX_SOFTIRQ, net_rx_action);
  6. // ...
  7. }

4、执行软中断

触发软中断后才能执行,触发点有3个:

  • 从一个硬中断代码处返回时,由汇编调用。
  • 在ksoftirqd内核线程中调用
  • 在那些显式和执行待处理的软中断的代码中,如网络子系统中和scsi中。