链接:http://bbs.chinaunix.net/thread-4125504-1-1.html
原问:
看博客有写到:一个软中断不会去抢占另一个软中断。
个人理解如下:《Linux内核设计与实现》中提到,如果软中断存在共享数据,那么需要进行加锁保护,因为同类型的软中断可以同时在不同的CPU下运行。所以,一个软中断不会去抢占另一个软中断,这句话的是在一个CPU上吧?
以下内容为个人猜想,不对的地方还请指正。假设硬件中断频繁发生,那么就会挤压了多个下半部(tasklet)在等待处理,这些tasklet应该是以队列的形式存放起来的。空闲的CPU从队列中取出一个tasklet进行处理,所以所有CPU都在处理软中断。 假设在一个CPU上,正在运行软中断A,此时来了一个中断,该中断处理也分为硬中断B和软中断B,硬中断B肯定可以抢占CPU运行。 那么接下来,执行软中断A还是软中断B呢? 是软中断A运行完以后,立即运行B,还是会放入队列当中啊?
6楼回复:
如果你这里的软中断时指softirq的话,那么:
查看了一下代码,流程应该是这样:1. 当某CPU正在run softirq A,这时来了一个硬件中断,硬件中断打断软件中断;
2. 硬件中断在上半部raise了软中断B的pending标志;
3. 硬件中断运行完毕,call irq_exit :
- void irq_exit(void)
- {
- account_system_vtime(current);
- trace_hardirq_exit();
- sub_preempt_count(IRQ_EXIT_OFFSET);
- if (!in_interrupt() && local_softirq_pending())
- invoke_softirq();
- rcu_irq_exit();
- #ifdef CONFIG_NO_HZ
- /* Make sure that timer wheel updates are propagated */
- if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
- tick_nohz_stop_sched_tick(0);
- #endif
- preempt_enable_no_resched();
- }
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
是说如果现在的环境本身就是在中断中(软中断也是在中断环境中),那么!in_interrupt()就会返回false,那么就不会运行invoke_softirq()
5. 硬件中断继续返回到被打断的地方(即先前的softirq A的处理中),kernel继续运行softirq A。
请注意在step 2中kernel已经raise了softirq B的pending位,所以在函数__do_softirq中,会重新check到softirq B的bit,从而运行softirq B的action
- restart:
- /* Reset the pending bitmask before enabling irqs */
- set_softirq_pending(0);
- local_irq_enable();
- h = softirq_vec;
- do {
- if (pending & 1) {
- int prev_count = preempt_count();
- kstat_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 (pending && --max_restart)
- goto restart;