如何理解softirq运行在中断上下文中?
本帖最后由 liujunwei1234 于 2011-04-19 16:09 编辑
- asmlinkage void do_softirq(void)
- {
- unsigned long flags;
- struct thread_info *curctx;
- union irq_ctx *irqctx;
- u32 *isp;
- if (in_interrupt()) /*判断是否在中断上下文中??*/
- return;
- local_irq_save(flags);
- if (local_softirq_pending()) {
- curctx = current_thread_info();
- irqctx = __get_cpu_var(softirq_ctx);
- irqctx->tinfo.task = curctx->task;
- irqctx->tinfo.previous_esp = current_stack_pointer;
- /* build the stack frame on the softirq stack */
- isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
- call_on_stack(__do_softirq, isp);
- /*
- * Shouldnt happen, we returned above if in_interrupt():
- */
- WARN_ON_ONCE(softirq_count());
- }
- local_irq_restore(flags);
- }
复制代码在很多Linux内核的教科书上都会解释到 :softirq运行在中断上下文中。今天在看代码的时候产生了疑问,希望大家指教!
1. 在上面do_softirq的代码中会首先判断是否在中断上下文中,in_interrupt()如果返回大于0的值,表明在中断上下文中,执行流程退出,岂不是说softirq不能运行在中断上下文中?
2. 在硬件中断退出的代码irq_exit中,又会判断如果当前执行路径不在中断上下文中才去执行do_softirq(),参见下面代码:
- 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();
- }
复制代码到底怎么才算在中断上下文中,是不是我没有理解in_interrupt的功能?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(9)
软中断做的是一些可延迟的费时间的事,当然不能在中断里执行了。
下面附有__do_softirq代码,可以看到在执行可延迟函数第一件事就是开中断。但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。
那么这样的后果就是软中断上下文里的会一直执行下去,直到到达了限定次数,然后唤醒守护进程。
因为软中断切换了栈,不再使用进程上下文,那么如果在软中断上下文直接或简洁调用了shedule,那么只有死翘翘了!!因为schedule调度回来的时候是依赖进程内核栈的thread_info。
如有不足之处,请不吝指点!
复制代码
本帖最后由 liujunwei1234 于 2011-04-19 16:58 编辑
回复 2# amarant
谢谢你的回答!我感觉还是中断上下文的理解不同。你说当然不能在中断里执行,是不是强调的是"上半部分"。
很多资料都会提到softirq和tasklet实在中断上下文中执行,work queue在进程上下文中执行,估计这个说法应该没有错。就是看代码的时候提出了上面的几点疑惑,代码里哪里说明了还是在中断上下文中?
另外,第一件事是开中断和不在中断上下文中执行也没有什么必然的联系。
还有你提到的“但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。”,我不是很理解是什么意思,能具体讲一下吗?
希望多交流啊!呵呵!
本帖最后由 amarant 于 2011-04-20 14:43 编辑
回复 3# liujunwei1234
很多资料都会提到softirq和tasklet实在中断上下文中执行,work queue在进程上下文中执行,估计这个说法应该没有错。就是看代码的时候提出了上面的几点疑惑,代码里哪里说明了还是在中断上下文中?
我认为上下文主要是以栈来识别的。如果中断不换栈,直接使用进程的栈,那么进程上下文和中断上下文没有本质区别(可能仅有的区别就是进程上下文会保存更多寄存器的值吧,这点待考证,我等下去看看代码)。
另外,第一件事是开中断和不在中断上下文中执行也没有什么必然的联系。
处于中断上下文中的代码基本都是关中断的吧,Linux的中断嵌套我不是很了解。。
还有你提到的“但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。”,我不是很理解是什么意思,能具体讲一下吗?
内核抢占点之一就是中断返回的时候检查是否可以抢占,检查的内容之一就是preempt_count是否等于0,因为禁用了下半部中断,那么肯定就不会等于0的,所以不会被抢占。也就是说返回的时候不会发生调度。
这个地方貌似不能把in_interrupt()单纯的理解为“是否在中断上下文中”。
复制代码thread_info->preempt_count实际上是被划分成四个区段:preempt计数、hardirq计数、softirq计数、nmi计数。
in_interrupt()实际上是检查后三个计数是否至少有一个非0。
很多情况下,in_interrupt()可以表示是否在中断上下文。但是本质上,从它的实现来看,并不是这么回事。
在进入中断的时候,irq_enter会增加hardirq计数:
复制代码退出中断的时候,irq_exit会相应的减hardirq计数:
复制代码在sub_preempt_count减掉hardirq计数之后,上下文显然是还没变的,但是in_interrupt()的返回值可能就变了。
所以,这里的in_interrupt()并不是检查是否在中断上下文,而是检查hardirq计数、softirq计数、nmi计数是否全为0。hardirq计数或nmi计数不为0,表示有中断嵌套;而softirq计数不为0则表示bh_disabled。
只有在没有中断嵌套且不有禁用下半部的情况下,才处理softirq。如果有中断嵌套,那么应该由最外层的中断来触发softirq(到了最外层,hardirq计数和nmi计数就都是0了)。
在do_softirq里面判断in_interrupt()的逻辑也是同样的道理。
简单的说,下半部不能抢占下半部及中断。
个人理解 中断上下文 最大的特征 禁掉了某种中断(硬中断和软中断),所以导致 不能阻塞。
softirq 有可能在两种方式下被调用,一是中断处理程序退出时,开放硬件中断之后,会去调用do_softirq()。
do_softirq()会禁掉后半部抢占,并且现在执行流使用的是被中断的进程的栈,所以无法阻塞。
softirq的另一种调用方式是ksoftirq内核线程,同样do_softirq()被调用,后半部中断被禁掉,同样禁止阻塞。
工作队列,可以被任何中断或者软中断中断,运行在进程上下文,有自己的栈,可以阻塞。
我認為如果沒有切換棧的話(中斷可以配置成擁有自己的棧,好像是一個硬中斷棧,一個軟中斷棧),就是說用進程上下文的棧,應該是可以睡眠的。雖然會照成無法估計的後果。但是還是可以調度回來繼續完成中斷的(因為中斷上下文用的是進程的棧,還是算一個調度實體吧)。
不過看網上的文章都是說,中斷上下文絕對不可以調度,否者就panic。我不知道自己哪裡理解錯了,請各位過路的大俠指教
本帖最后由 omycle 于 2011-04-20 10:39 编辑
kouu同学对in_interrupt的解释非常到位,赞!
回复 5# kouu
谢谢你的回答,对in_interrupt的分析是对的,但是是否会出现这种情况呢?
禁止中断下半部分的函数会增加软中断的计数,即当有软中断的do_softirq在进行处理时,如果此时被硬件中断打断,而且在硬件中断中又激活了优先级更高的软中断,当硬件中断退出时,那么当再去执行do_softirq时,此时in_interrupt > 0,岂不是死锁了!!!
这里是我对软中断的一点分析,与大家交流!
http://blog.chinaunix.net/space.php?uid=20940095&do=blog&id=252237