1.前言
软中断(softirq)是中断处理程序在开启中断的情况下执行的部分,可以被硬中断抢占。把延迟函数叫做软中断并不是因为它是一个真正的中断,而是因为延迟函数主要在中断上下文环境中运行。Linux中最多可以支持32种软中断操作,但目前Linux只实现了其中的几种。
中断号 | 名称 | 作用 |
---|---|---|
0 | HI_SOFTIRQ | 高优先级tasklet |
1 | TIMER_SOFTIRQ | 定时器软中断 |
2 | NET_TX_SOFTIRQ | 网络发送软中断 |
3 | NET_RX_SOFTIRQ | 网络接收软中断 |
4 | SCSI_SOFTIRQ | SCSI设备驱动专用软中断 |
5 | TASKLET_SOFTIRQ | 常规tasklet |
include/linux/interrupt.h
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
IRQ_POLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ, /* 没有使用,但是保留,因为有些工具依赖这个编号 */
RCU_SOFTIRQ, /* RCU软中断应该总是最后一个软中断 */
NR_SOFTIRQS
};
2.关键函数
- 函数open_softirq()用来注册软中断的处理函数,在软中断向量表中为指定的软中断编号设置处理函数。
kernel/softirq.c
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;
}
- 函数raise_softirq用来触发软中断,参数是软中断编号。
void raise_softirq(unsigned int nr);
3.执行软中断
内核执行软中断的地方如下。
- (1)在中断处理程序的后半部分执行软中断,对执行时间有限制:不能超过2毫秒,并且最多执行10次。
- (2)每个处理器有一个软中断线程,调度策略是SCHED_NORMAL,优先级是120。
- (3)开启软中断的函数local_bh_enable()。
在中断处理程序的后半部分,调用函数irq_exit()以退出中断上下文,处理软中断,其代码如下:
kernel/softirq.c
void irq_exit(void)
{
…
preempt_count_sub(HARDIRQ_OFFSET);
if (! in_interrupt() && local_softirq_pending())
invoke_softirq();
…
}
如果正在处理的硬中断没有抢占正在执行的软中断,没有禁止软中断,并且当前处理器的待处理软中断位图不是空的,那么调用函数invoke_softirq()来处理软中断。
kernel/softirq.c
1 static inline void invoke_softirq(void)
2 {
3 if (ksoftirqd_running())
4 return;
5
6 if (! force_irqthreads) {
7 __do_softirq();
8 } else {
9 wakeup_softirqd();
10 }
11 }
第3行代码,如果软中断线程处于就绪状态或运行状态,那么让软中断线程执行软中断。第6行和第7行代码,如果没有强制中断线程化,那么调用函数__do_softirq()执行软中断。第8行和第9行代码,如果强制中断线程化,那么唤醒软中断线程执行软中断。函数__do_softirq是执行软中断的核心函数,其主要代码如下:
kernel/softirq.c
1 #define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)
2 #define MAX_SOFTIRQ_RESTART 10
3 asmlinkage __visible void __softirq_entry __do_softirq(void)
4 {
5 unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
6 unsigned long old_flags = current->flags;
7 int max_restart = MAX_SOFTIRQ_RESTART;
8 struct softirq_action *h;
9 bool in_hardirq;
10 __u32 pending;
11 int softirq_bit;
12
13 …
14 pending = local_softirq_pending();
15 …
16 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
17 …
18
19 restart:
20 set_softirq_pending(0);
21
22 local_irq_enable();
23
24 h = softirq_vec;
25
26 while ((softirq_bit = ffs(pending))) {
27 …
28 h += softirq_bit - 1;
29 …
30 h->action(h);
31 …
32 h++;
33 pending >>= softirq_bit;
34 }
35
36 …
37 local_irq_disable();
38
39 pending = local_softirq_pending();
40 if (pending) {
41 if (time_before(jiffies, end) && ! need_resched() &&
42 --max_restart)
43 goto restart;
44
45 wakeup_softirqd();
46 }
47
48 …
49 __local_bh_enable(SOFTIRQ_OFFSET);
50 …
51 }
第14行代码,把局部变量pending设置为当前处理器的待处理软中断位图。第16行代码,把抢占计数器的软中断计数加1。第20行代码,把当前处理器的待处理软中断位图重新设置为0。第22行代码,开启硬中断。第26~34行代码,从低位向高位扫描待处理软中断位图,针对每个设置了对应位的软中断编号,执行软中断的处理函数。第37行代码,禁止硬中断。第40行代码,如果软中断的处理函数又触发软中断,处理如下。❑ 第41~43行代码,如果软中断的执行时间小于2毫秒,不需要重新调度进程,并且软中断的执行次数没超过10,那么跳转到第19行代码继续执行软中断。第45行代码,唤醒软中断线程执行软中断。第49行代码,把抢占计数器的软中断计数减1。
4.ksoftirqd
软中断线程每个处理器有一个软中断线程,名称是“ksoftirqd/”后面跟着处理器编号,调度策略是SCHED_NORMAL,优先级是120。软中断线程的核心函数是run_ksoftirqd(),其代码如下:
kernel/softirq.c
static void run_ksoftirqd(unsigned int cpu)
{
local_irq_disable();
if (local_softirq_pending()) {
__do_softirq();
local_irq_enable();
…
return;
}
local_irq_enable();
}
5.参与讨论
==================================
新的文章内容和分享已更新在:
|工|·-·|重|·-·|号|:协议森林
==================================