Linux内核中的软中断、tasklet和工作队列详解( 三 )

  1. 首先调用local_softirq_pending函数取得目前有哪些位存在软件中断 。
  2. 调用__local_bh_disable关闭软中断 , 其实就是设置正在处理软件中断标记 , 在同一个CPU上使得不能重入__do_softirq函数 。
  3. 重新设置软中断标记为0 , set_softirq_pending重新设置软中断标记为0 , 这样在之后重新开启中断之后硬件中断中又可以设置软件中断位 。
  4. 调用local_irq_enable , 开启硬件中断 。
  5. 之后在一个循环中 , 遍历pending标志的每一位 , 如果这一位设置就会调用软件中断的处理函数 。 在这个过程中硬件中断是开启的 , 随时可以打断软件中断 。 这样保证硬件中断不会丢失 。
  6. 之后关闭硬件中断(local_irq_disable) , 查看是否又有软件中断处于pending状态 , 如果是 , 并且在本次调用__do_softirq函数过程中没有累计重复进入软件中断处理的次数超过max_restart=10次 , 就可以重新调用软件中断处理 。 如果超过了10次 , 就调用wakeup_softirqd()唤醒内核的一个进程来处理软件中断 。 设立10次的限制 , 也是为了避免影响系统响应时间 。
  7. 调用_local_bh_enable开启软中断 。
软中断内核线程之前我们分析的触发软件中断的位置其实是中断上下文中 , 而在软中断的内核线程中实际已经是进程的上下文 。这里说的软中断上下文指的就是系统为每个CPU建立的ksoftirqd进程 。软中断的内核进程中主要有两个大循环 , 外层的循环处理有软件中断就处理 , 没有软件中断就休眠 。 内层的循环处理软件中断 , 每循环一次都试探一次是否过长时间占据了CPU , 需要调度就释放CPU给其它进程 。 具体的操作在注释中做了解释 。
set_current_state(TASK_INTERRUPTIBLE);//外层大循环 。while (!kthread_should_stop()) {preempt_disable();//禁止内核抢占 , 自己掌握cpuif (!local_softirq_pending()) {preempt_enable_no_resched();//如果没有软中断在pending中就让出cpuschedule();//调度之后重新掌握cpupreempt_disable();}__set_current_state(TASK_RUNNING);while (local_softirq_pending()) {/* Preempt disable stops cpu going offline.If already offline, we'll be on wrong CPU:don't process */if (cpu_is_offline((long)__bind_cpu))goto wait_to_die;//有软中断则开始软中断调度do_softirq();//查看是否需要调度 , 避免一直占用cpupreempt_enable_no_resched();cond_resched();preempt_disable();rcu_sched_qs((long)__bind_cpu);}preempt_enable();set_current_state(TASK_INTERRUPTIBLE);}__set_current_state(TASK_RUNNING);return 0;wait_to_die:preempt_enable();/* Wait for kthread_stop */set_current_state(TASK_INTERRUPTIBLE);while (!kthread_should_stop()) {schedule();set_current_state(TASK_INTERRUPTIBLE);}__set_current_state(TASK_RUNNING);return 0;tasklet由于软中断必须使用可重入函数 , 这就导致设计上的复杂度变高 , 作为设备驱动程序的开发者来说 , 增加了负担 。 而如果某种应用并不需要在多个CPU上并行执行 , 那么软中断其实是没有必要的 。 因此诞生了弥补以上两个要求的tasklet 。 它具有以下特性: a)一种特定类型的tasklet只能运行在一个CPU上 , 不能并行 , 只能串行执行 。b)多个不同类型的tasklet可以并行在多个CPU上 。c)软中断是静态分配的 , 在内核编译好之后 , 就不能改变 。 但tasklet就灵活许多 , 可以在运行时改变(比如添加模块时) 。tasklet是在两种软中断类型的基础上实现的 , 因此如果不需要软中断的并行特性 , tasklet就是最好的选择 。 也就是说tasklet是软中断的一种特殊用法 , 即延迟情况下的串行执行 。
相关数据结构