如何理解softirq运行在中断上下文中?

发布于 2022-10-15 09:09:41 字数 3271 浏览 26 评论 0

本帖最后由 liujunwei1234 于 2011-04-19 16:09 编辑

  1. asmlinkage void do_softirq(void)
  2. {
  3.         unsigned long flags;
  4.         struct thread_info *curctx;
  5.         union irq_ctx *irqctx;
  6.         u32 *isp;
  7.         if (in_interrupt())  /*判断是否在中断上下文中??*/
  8.             return;
  9.         local_irq_save(flags);
  10.         if (local_softirq_pending()) {
  11.                 curctx = current_thread_info();
  12.                 irqctx = __get_cpu_var(softirq_ctx);
  13.                 irqctx->tinfo.task = curctx->task;
  14.                 irqctx->tinfo.previous_esp = current_stack_pointer;
  15.                 /* build the stack frame on the softirq stack */
  16.                 isp = (u32 *) ((char *)irqctx + sizeof(*irqctx));
  17.                 call_on_stack(__do_softirq, isp);
  18.                 /*
  19.                  * Shouldnt happen, we returned above if in_interrupt():
  20.                  */
  21.                 WARN_ON_ONCE(softirq_count());
  22.         }
  23.         local_irq_restore(flags);
  24. }

复制代码在很多Linux内核的教科书上都会解释到 :softirq运行在中断上下文中。今天在看代码的时候产生了疑问,希望大家指教!
1. 在上面do_softirq的代码中会首先判断是否在中断上下文中,in_interrupt()如果返回大于0的值,表明在中断上下文中,执行流程退出,岂不是说softirq不能运行在中断上下文中?
2. 在硬件中断退出的代码irq_exit中,又会判断如果当前执行路径不在中断上下文中才去执行do_softirq(),参见下面代码:

  1. void irq_exit(void)
  2. {
  3.         account_system_vtime(current);
  4.         trace_hardirq_exit();
  5.         sub_preempt_count(IRQ_EXIT_OFFSET);
  6.                 if (!in_interrupt() && local_softirq_pending()) /*判断是否在中断上下文中??*/
  7.                 invoke_softirq();
  8.         rcu_irq_exit();
  9. #ifdef CONFIG_NO_HZ
  10.         /* Make sure that timer wheel updates are propagated */
  11.         if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
  12.                 tick_nohz_stop_sched_tick(0);
  13. #endif
  14.         preempt_enable_no_resched();
  15. }

复制代码到底怎么才算在中断上下文中,是不是我没有理解in_interrupt的功能?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

赏烟花じ飞满天 2022-10-22 09:09:41

软中断做的是一些可延迟的费时间的事,当然不能在中断里执行了。

下面附有__do_softirq代码,可以看到在执行可延迟函数第一件事就是开中断。但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。
那么这样的后果就是软中断上下文里的会一直执行下去,直到到达了限定次数,然后唤醒守护进程。

因为软中断切换了栈,不再使用进程上下文,那么如果在软中断上下文直接或简洁调用了shedule,那么只有死翘翘了!!因为schedule调度回来的时候是依赖进程内核栈的thread_info。

如有不足之处,请不吝指点!

  1. asmlinkage void __do_softirq(void)
  2. {
  3.         struct softirq_action *h;
  4.         __u32 pending;
  5.         int max_restart = MAX_SOFTIRQ_RESTART;
  6.         int cpu;
  7.         pending = local_softirq_pending();
  8.         account_system_vtime(current);
  9.         __local_bh_disable((unsigned long)__builtin_return_address(0));
  10.         lockdep_softirq_enter();
  11.         cpu = smp_processor_id();
  12. restart:
  13.         /* Reset the pending bitmask before enabling irqs */
  14.         set_softirq_pending(0);
  15.         local_irq_enable();
  16.         h = softirq_vec;
  17.         do {
  18.                 if (pending & 1) {
  19.                         int prev_count = preempt_count();
  20.                         kstat_incr_softirqs_this_cpu(h - softirq_vec);
  21.                         trace_softirq_entry(h, softirq_vec);
  22.                         h->action(h);
  23.                         trace_softirq_exit(h, softirq_vec);
  24.                         if (unlikely(prev_count != preempt_count())) {
  25.                                 printk(KERN_ERR "huh, entered softirq %td %s %p"
  26.                                        "with preempt_count %08x,"
  27.                                        " exited with %08x?\n", h - softirq_vec,
  28.                                        softirq_to_name[h - softirq_vec],
  29.                                        h->action, prev_count, preempt_count());
  30.                                 preempt_count() = prev_count;
  31.                         }
  32.                         rcu_bh_qs(cpu);
  33.                 }
  34.                 h++;
  35.                 pending >>= 1;
  36.         } while (pending);
  37.         local_irq_disable();
  38.         pending = local_softirq_pending();
  39.         if (pending && --max_restart)
  40.                 goto restart;
  41.         if (pending)
  42.                 wakeup_softirqd();
  43.          lockdep_softirq_exit();
  44.         account_system_vtime(current);
  45.         _local_bh_enable();
  46. }

复制代码

愛放△進行李 2022-10-22 09:09:41

本帖最后由 liujunwei1234 于 2011-04-19 16:58 编辑

回复 2# amarant

    谢谢你的回答!我感觉还是中断上下文的理解不同。你说当然不能在中断里执行,是不是强调的是"上半部分"。
   很多资料都会提到softirq和tasklet实在中断上下文中执行,work queue在进程上下文中执行,估计这个说法应该没有错。就是看代码的时候提出了上面的几点疑惑,代码里哪里说明了还是在中断上下文中?
   另外,第一件事是开中断和不在中断上下文中执行也没有什么必然的联系。
   
  
  还有你提到的“但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。”,我不是很理解是什么意思,能具体讲一下吗?
   希望多交流啊!呵呵!

陌伤浅笑 2022-10-22 09:09:41

本帖最后由 amarant 于 2011-04-20 14:43 编辑

回复 3# liujunwei1234

       很多资料都会提到softirq和tasklet实在中断上下文中执行,work queue在进程上下文中执行,估计这个说法应该没有错。就是看代码的时候提出了上面的几点疑惑,代码里哪里说明了还是在中断上下文中?
       我认为上下文主要是以栈来识别的。如果中断不换栈,直接使用进程的栈,那么进程上下文和中断上下文没有本质区别(可能仅有的区别就是进程上下文会保存更多寄存器的值吧,这点待考证,我等下去看看代码)。

   另外,第一件事是开中断和不在中断上下文中执行也没有什么必然的联系。
      处于中断上下文中的代码基本都是关中断的吧,Linux的中断嵌套我不是很了解。。   
  
  还有你提到的“但在开始之前,禁用了下半部中断(__local_bh_disable)。这样就算被中断了,返回内核时也不会被抢占,还是执行这里的代码。也不会被调度。”,我不是很理解是什么意思,能具体讲一下吗?
      内核抢占点之一就是中断返回的时候检查是否可以抢占,检查的内容之一就是preempt_count是否等于0,因为禁用了下半部中断,那么肯定就不会等于0的,所以不会被抢占。也就是说返回的时候不会发生调度。

葬シ愛 2022-10-22 09:09:41

这个地方貌似不能把in_interrupt()单纯的理解为“是否在中断上下文中”。

  1. #define in_interrupt()                (irq_count())
  2. #define irq_count()        (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
  3. #define preempt_count()        (current_thread_info()->preempt_count)

复制代码thread_info->preempt_count实际上是被划分成四个区段:preempt计数、hardirq计数、softirq计数、nmi计数。
in_interrupt()实际上是检查后三个计数是否至少有一个非0。
很多情况下,in_interrupt()可以表示是否在中断上下文。但是本质上,从它的实现来看,并不是这么回事。

在进入中断的时候,irq_enter会增加hardirq计数:

  1. #define __irq_enter()                                        \
  2.         do {                                                \
  3.                 account_system_vtime(current);                \
  4.                 add_preempt_count(HARDIRQ_OFFSET);        \
  5.                 trace_hardirq_enter();                        \
  6.         } while (0)

复制代码退出中断的时候,irq_exit会相应的减hardirq计数:

  1. void irq_exit(void)
  2. {
  3.         account_system_vtime(current);
  4.         trace_hardirq_exit();
  5.         sub_preempt_count(IRQ_EXIT_OFFSET);
  6.         if (!in_interrupt() && local_softirq_pending())
  7.                 invoke_softirq();
  8.         rcu_irq_exit();
  9.         preempt_enable_no_resched();
  10. }
  11. # define IRQ_EXIT_OFFSET HARDIRQ_OFFSET

复制代码在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()的逻辑也是同样的道理。

暖风昔人 2022-10-22 09:09:41

简单的说,下半部不能抢占下半部及中断。

分分钟 2022-10-22 09:09:41

个人理解 中断上下文 最大的特征 禁掉了某种中断(硬中断和软中断),所以导致 不能阻塞。

softirq 有可能在两种方式下被调用,一是中断处理程序退出时,开放硬件中断之后,会去调用do_softirq()。
do_softirq()会禁掉后半部抢占,并且现在执行流使用的是被中断的进程的栈,所以无法阻塞。

softirq的另一种调用方式是ksoftirq内核线程,同样do_softirq()被调用,后半部中断被禁掉,同样禁止阻塞。

工作队列,可以被任何中断或者软中断中断,运行在进程上下文,有自己的栈,可以阻塞。

旧人哭 2022-10-22 09:09:41

中断上下文 最大的特征 禁掉了某种中断(硬中断和软中断),所以导致 不能阻塞。

wangjl_sdu 发表于 2011-04-20 00:18

    我認為如果沒有切換棧的話(中斷可以配置成擁有自己的棧,好像是一個硬中斷棧,一個軟中斷棧),就是說用進程上下文的棧,應該是可以睡眠的。雖然會照成無法估計的後果。但是還是可以調度回來繼續完成中斷的(因為中斷上下文用的是進程的棧,還是算一個調度實體吧)。
     不過看網上的文章都是說,中斷上下文絕對不可以調度,否者就panic。我不知道自己哪裡理解錯了,請各位過路的大俠指教

烟火散人牵绊 2022-10-22 09:09:41

本帖最后由 omycle 于 2011-04-20 10:39 编辑

kouu同学对in_interrupt的解释非常到位,赞!

貪欢 2022-10-22 09:09:41

回复 5# kouu

    谢谢你的回答,对in_interrupt的分析是对的,但是是否会出现这种情况呢?
禁止中断下半部分的函数会增加软中断的计数,即当有软中断的do_softirq在进行处理时,如果此时被硬件中断打断,而且在硬件中断中又激活了优先级更高的软中断,当硬件中断退出时,那么当再去执行do_softirq时,此时in_interrupt > 0,岂不是死锁了!!!

这里是我对软中断的一点分析,与大家交流!
http://blog.chinaunix.net/space.php?uid=20940095&do=blog&id=252237

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文