> 文章列表 > 【Linux内核】Linux软中断处理机制-ksoftirqd

【Linux内核】Linux软中断处理机制-ksoftirqd

【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;121314   pending = local_softirq_pending();1516   __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);171819  restart:20   set_softirq_pending(0);2122   local_irq_enable();2324   h = softirq_vec;2526   while ((softirq_bit = ffs(pending))) {2728        h += softirq_bit - 1;2930        h->action(h);3132        h++;33        pending >>= softirq_bit;34   }353637   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   }474849   __local_bh_enable(SOFTIRQ_OFFSET);5051  }

第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.参与讨论

==================================

新的文章内容和分享已更新在:

|工|·-·|重|·-·|号|:协议森林

==================================