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

实际上即以软中断类型nr作为偏移量置位每cpu变量irq_stat[cpu_id]的成员变量__softirq_pending , 这也是同一类型软中断可以在多个cpu上并行运行的根本原因 。

  • 软中断执行函数
do_softirq-->__do_softirq执行软中断处理函数__do_softirq前首先要满足两个条件: (1)不在中断中(硬中断、软中断和NMI)。 1 (2)有软中断处于pending状态 。系统这么设计是为了避免软件中断在中断嵌套中被调用 , 并且达到在单个CPU上软件中断不能被重入的目的 。 对于ARM架构的CPU不存在中断嵌套中调用软件中断的问题 , 因为ARM架构的CPU在处理硬件中断的过程中是关闭掉中断的 。 只有在进入了软中断处理过程中之后才会开启硬件中断 , 如果在软件中断处理过程中有硬件中断嵌套 , 也不会再次调用软中断 , because硬件中断是软件中断处理过程中再次进入的 , 此时preempt_count已经记录了软件中断!对于其它架构的CPU , 有可能在触发调用软件中断前 , 也就是还在处理硬件中断的时候 , 就已经开启了硬件中断 , 可能会发生中断嵌套 , 在中断嵌套中是不允许调用软件中断处理的 。 Why?我的理解是 , 在发生中断嵌套的时候 , 表明这个时候是系统突发繁忙的时候 , 内核第一要务就是赶紧把中断中的事情处理完成 , 退出中断嵌套 。 避免多次嵌套 , 哪里有时间处理软件中断 , 所以把软件中断推迟到了所有中断处理完成的时候才能触发软件中断 。
需要C/C++ Linux服务器架构师学习资料私信“资料”(资料包括C/C++ , Linux , golang技术 , Nginx , ZeroMQ , MySQL , Redis , fastdfs , MongoDB , ZK , 流媒体 , CDN , P2P , K8S , Docker , TCP/IP , 协程 , DPDK , ffmpeg等) , 免费分享
Linux内核中的软中断、tasklet和工作队列详解文章插图
实现原理和实例软中断的调度时机:
  1. do_irq完成I/O中断时调用irq_exit 。
  2. 系统使用I/O APIC,在处理完本地时钟中断时 。
  3. local_bh_enable , 即开启本地软中断时 。
  4. SMP系统中 , cpu处理完被CALL_FUNCTION_VECTOR处理器间中断所触发的函数时 。
  5. ksoftirqd/n线程被唤醒时 。下面以从中断处理返回函数irq_exit中调用软中断为例详细说明 。触发和初始化的的流程如图所示:

Linux内核中的软中断、tasklet和工作队列详解文章插图
软中断处理流程asmlinkage void __do_softirq(void){struct softirq_action *h;__u32 pending;int max_restart = MAX_SOFTIRQ_RESTART;int cpu;pending = local_softirq_pending();account_system_vtime(current);__local_bh_disable((unsigned long)__builtin_return_address(0));lockdep_softirq_enter();cpu = smp_processor_id();restart:/* Reset the pending bitmask before enabling irqs */set_softirq_pending(0);local_irq_enable();h = softirq_vec;do {if (pendingkstat_incr_softirqs_this_cpu(h - softirq_vec);trace_softirq_entry(h, softirq_vec);h->action(h);trace_softirq_exit(h, softirq_vec);if (unlikely(prev_count != preempt_count())) {printk(KERN_ERR "huh, entered softirq %td %s %p""with preempt_count %08x,"" exited with %08x?\n", h - softirq_vec,softirq_to_name[h - softirq_vec],h->action, prev_count, preempt_count());preempt_count() = prev_count;}rcu_bh_qs(cpu);}h++;pending >>= 1;} while (pending);local_irq_disable();pending = local_softirq_pending();if (pendingif (pending)wakeup_softirqd();lockdep_softirq_exit();account_system_vtime(current);_local_bh_enable();}