Skip to the content.

qs

rcu 什么可以导致进入 quiescent state

可以的:

不可以的:

结论:基本正确,但有三个地方需要精确化。逐条对照代码和文档(kernel/rcu/tree.c、kernel/rcu/tree_plugin.h、Documentation/RCU/Design/Requirements/Requirements.rst):

✅ 「可以」的三条基本对

  1. 用户态执行 — 对。调度时钟中断从 usermode 打断时(rcu_sched_clock_irq(user=1)),直接记为 QS(tree.c:2711 的 if (user || rcu_is_cpu_rrupt_from_idle()))。 NO_HZ_FULL 下纯用户态运行由 dyntick 检测跟踪。

  2. idle 循环 — 对。dyntick-idle 是 QS。有个重要的例外/细节:从 idle 被第一层(非嵌套)中断打断也算 QS —— 这正是 rcu_is_cpu_rrupt_from_idle() 存在的意义:
      /* Non nested idle interrupt (interrupting section where RCU wasn't watching) */
      if (nmi_nesting == 1)
          return true;
    

    因为不能让 tick 打断 idle 后反而丢掉 QS。但 NMI 嵌套(nmi_nesting > 1)不算。

  3. 从 RCU 读临界区退出 — 只对可抢占 RCU(CONFIG_PREEMPTION=y)成立。退出最外层 rcu_read_unlock() 时若 need_qs/blocked 被置位, 经 rcu_read_unlock_special() → rcu_preempt_deferred_qs() 报告 QS。对非抢占 RCU,rcu_read_lock/unlock 只是 preempt_disable/enable,退出本身不报告 QS。

❌ 「不可以」的两条基本对,但注意例外

  1. 中断/软中断 — 对,理由很明确(tree_plugin.h 的 rcu_flavor_sched_clock_irq):
      if (rcu_preempt_depth() > 0 ||
          (preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK))) {
          /* No QS, force context switch if deferred. */
    

    中断/软中断上下文 preempt_count 非零,且中断会延续被打断的 RCU 读临界区。

    例外:中断处理程序本身不产生 QS,但 tick 在中断里运行时看被打断的上下文——打断的是 usermode 或 idle 就仍记 QS(见第 1、2 条)。

  2. rcu_read_lock()/rcu_read_unlock() 之间 — 对,QS 的定义就是”不在读临界区中”。

📌 你没提到的「可以」

一句话总结:你的列表是对的,补充两点

  1. 从 idle 被非嵌套中断打断仍算 QS;
  2. “退出读临界区”这条只适用于可抢占 RCU,且别漏掉调度器/上下文切换这个最常见的 QS 来源。

还有一个细节:上下文切换这条路径是”被招募”的——jiffies_till_sched_qs 控制,GP 拖太久还没结束时,调度器才会通过 rcu_urgent_qs/rcu_momentary_eqs() 更积极地提供 QS 帮助;正常短 GP 主要靠 tick 推断和 idle。

所以准确说法是:普通 GP 靠 “tick 推断 user/idle/无锁现场 + dynticks EQS”,调度 点(含 cond_resched)和长 softirq 里的 rcu_softirq_qs() 兜底,而不是只靠”特殊位置”。

本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。