【Linux内核】Linux软中断处理机制-ksoftirqd
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.henum{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.cvoid 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.cvoid irq_exit(void){…preempt_count_sub(HARDIRQ_OFFSET);if (! in_interrupt() && local_softirq_pending())invoke_softirq();…}
如果正在处理的硬中断没有抢占正在执行的软中断,没有禁止软中断,并且当前处理器的待处理软中断位图不是空的,那么调用函数invoke_softirq()来处理软中断。
kernel/softirq.c1 static inline void invoke_softirq(void)2 {3 if (ksoftirqd_running())4 return;56 if (! force_irqthreads) {7 __do_softirq();8 } else {9 wakeup_softirqd();10 }11 }
第3行代码,如果软中断线程处于就绪状态或运行状态,那么让软中断线程执行软中断。第6行和第7行代码,如果没有强制中断线程化,那么调用函数__do_softirq()执行软中断。第8行和第9行代码,如果强制中断线程化,那么唤醒软中断线程执行软中断。函数__do_softirq是执行软中断的核心函数,其主要代码如下:
kernel/softirq.c1 #define MAX_SOFTIRQ_TIME msecs_to_jiffies(2)2 #define MAX_SOFTIRQ_RESTART 103 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;1213 …14 pending = local_softirq_pending();15 …16 __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);17 …1819 restart:20 set_softirq_pending(0);2122 local_irq_enable();2324 h = softirq_vec;2526 while ((softirq_bit = ffs(pending))) {27 …28 h += softirq_bit - 1;29 …30 h->action(h);31 …32 h++;33 pending >>= softirq_bit;34 }3536 …37 local_irq_disable();3839 pending = local_softirq_pending();40 if (pending) {41 if (time_before(jiffies, end) && ! need_resched() &&42 --max_restart)43 goto restart;4445 wakeup_softirqd();46 }4748 …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.cstatic void run_ksoftirqd(unsigned int cpu){local_irq_disable();if (local_softirq_pending()) {__do_softirq();local_irq_enable();…return;}local_irq_enable();}
5.参与讨论
==================================
新的文章内容和分享已更新在:
|工|·-·|重|·-·|号|:协议森林
==================================