当前位置: 首页 > news >正文

Linux 内核的 Context Tracking上下文跟踪

Linux 内核的Context Tracking(上下文跟踪,也叫 CT / EQoS — Extended Quiescent State)​ 是内核用来精确感知 CPU 当前运行在哪个"执行域"(用户态 / 内核态 / 空闲 / Guest VM),并通知 RCU 和 虚拟时间记账子系统做相应处理的基础设施。它是NO_HZ_FULL(全动态无滴答 tickless)能正常工作的前提。

下面从数据结构、核心原理、关键流程、与 RCU 的关系四个层面详细说明。


一、Context Tracking 解决什么问题

传统 RCU 依赖周期性的 timer tick(调度时钟中断)来判断某 CPU 是否经过 quiescent state(经历用户态或 idle)。但在NO_HZ_FULL模式下,tick 被停止了——RCU 无法靠 tick 感知 CPU 进入/离开用户空间。

Context Tracking 的作用就是:

在体系结构入口/出口(syscall/异常/中断返回用户态、idle 进入/退出)显式通知内核​ "本 CPU 现在进入/退出用户态(或 idle/GUEST)",让 RCU 可以把用户态当作 Extended Quiescent State(EQS),从而安全停止对该 CPU 的 grace-period 追踪,也允许停掉周期性 tick。

此外它还驱动:

  • vtime(Virtual Time Accounting):区分进程在用户态 / 内核态 / guest 的耗时

  • TASKS_RCU / TRACE_RCU:标记任务不在内核临界区


二、核心数据结构

每 CPU 一个struct context_tracking(新内核重命名为struct ct_rcu,原理相同):

// include/linux/context_tracking.h + context_tracking_state.h struct context_tracking { bool active; // 该 CPU 是否启用 context tracking (在 NO_HZ_FULL mask 中) atomic_t state; // 当前上下文状态 + RCU watching 位 /* 旧版用 enum { IN_KERNEL=0, IN_USER } state; */ }; DEFINE_PER_CPU(struct context_tracking, context_tracking);

state在新内核(5.x+/6.x)是一个atomic_t,其低位编码:

  • RCU_WATCHING bit:RCU 是否在观察本 CPU(1=watching, 0=in EQS)

  • 上下文类型:KERNEL / USER / IDLE / GUEST

全局静态键context_tracking_enabled(Static Key)确保未开启CONFIG_CONTEXT_TRACKING_USER或 CPU 不在 dyntick mask 时零开销跳过。


三、关键状态转换与调用点

状态机

┌──────────────┐ ct_user_enter() ┌──────────────┐ │ IN_KERNEL │ ──────────────────▶ │ IN_USER │ │ (RCU watching)│ ◀────────────────── │(RCU EQS/off) │ └──────────────┘ ct_user_exit() └──────────────┘ ▲ │ ▲ │ │ │ ct_idle_enter/exit() │ │ │ ▼ │ ▼ IDLE (也是一种 EQS)
  • IN_KERNEL:CPU 执行内核代码 → RCU 正常监视(rcu_irq_enter 类语义)

  • IN_USER / IDLE / GUEST:CPU 不在内核 → RCU 认为这是Extended Quiescent State,可停止 grace period 对该 CPU 的等待


体系结构入口处的埋点

场景

汇编/早期 C 入口调用

内核→用户态返回(syscall/exception 返回)

ct_user_enter()/user_enter_irqoff()

用户态→内核(syscall/异常进入)

ct_user_exit()/user_exit_irqoff()

idle loop 进入

ct_idle_enter()rcu_eqs_enter()

idle loop 退出

ct_idle_exit()rcu_eqs_exit()

KVM guest 进入/退出

ct_guest_enter()/ct_guest_exit()

这些函数通常在noinstr标记的入口代码中调用(IRQs off),在 RCU 开始/停止 watching 的前后。


四、核心函数工作流程

以用户态进出为例(新内核__ct_user_enter/__ct_user_exit,旧版context_tracking_user_enter/exit):

1.ct_user_enter()— 内核 → 用户态

ct_user_enter(CONTEXT_USER): 若 !context_tracking_enabled() → return 若 in_interrupt() → return // 嵌套 IRQ 中不处理 若 state 已是 IN_USER → return // 幂等 ── 以下 this_cpu active == true ── vtime_user_enter(current) // 停止内核时间记账,开始用户时间 rcu_user_enter() // ★ 通知 RCU 进入 Extended Quiescent State → ct_kernel_exit_state() // 递增 dynticks 计数器,清除 RCU_WATCHING 位 this_cpu_write(state, IN_USER)

关键点:rcu_user_enter()使 RCU 对该 CPU "不再 watching",该 CPU 被视为已通过 quiescent state,后续不需要 tick。


2.ct_user_exit()— 用户态 → 内核(syscall/异常)

ct_user_exit(CONTEXT_USER): 若 !enabled → return 若 in_interrupt() → return 若 state != IN_USER → return (已在内核,幂等) ── this_cpu active == true ── rcu_user_exit() // ★ 通知 RCU 退出 EQS → ct_kernel_enter_state() // 递增 dynticks,设置 RCU_WATCHING 位 vtime_user_exit(current) // 恢复内核时间记账 this_cpu_write(state, IN_KERNEL)

必须在任何 RCU read-side critical section 之前调用,否则会读到已停用的 RCU。


3. 异常嵌套处理 —exception_enter()/exception_exit()

// include/linux/context_tracking.h static inline enum ctx_state exception_enter(void) { prev = __ct_state(); // 读取当前状态 if (prev != CT_STATE_KERNEL) // 若原在 USER,先退出 USER ct_user_exit(prev); return prev; } static inline void exception_exit(enum ctx_state prev) { if (prev != CT_STATE_KERNEL) ct_user_enter(prev); // 异常返回时恢复 USER 状态 }

保证异常发生在用户态时,短暂把 context 切回 KERNEL(让 RCU watching),返回时再切回去。


4. 任务切换时的处理

void context_tracking_task_switch(prev, next) { if (ct->active) { clear_tsk_thread_flag(prev, TIF_NOHZ); set_tsk_thread_flag(next, TIF_NOHZ); } }

TIF_NOHZ使新任务走 syscall slow path,从而在返回用户态时触发ct_user_enter()检查。init 进程启动时设TIF_NOHZ,通过 fork 继承给所有进程。


五、与 RCU 的协作关系

Context Tracking 动作

RCU 对应行为

rcu_user_enter()ct_kernel_exit_state()

dynticks 计数器++,RCU_WATCHING=0 → CPU 进入 EQS,RCU 可报告该 CPU 已过 quiescent state

rcu_user_exit()ct_kernel_enter_state()

dynticks 计数器++,RCU_WATCHING=1 → RCU 恢复监视,grace period 继续等待该 CPU

rcu_irq_enter()/exit()

处理中断中嵌套,dynticks_nmi_nesting 独立维护,不影响 user eqs

rcu_is_watching_curr_cpu()直接读context_tracking.state的 RCU_WATCHING 位判断。


六、配置与激活条件

  • 编译:CONFIG_CONTEXT_TRACKING_USER=y(隐含CONFIG_NO_HZ_FULL=y

  • 启动参数:nohz_full=cpulist— 指定哪些 CPU 激活context_tracking.active=true

  • CONTEXT_TRACKING_FORCE:强制所有 CPU 开启(调试用)

nohz_fullCPU 上active=false,只维护 state 用于一致性,不调rcu_user_enter/exit,开销接近零。


七、一句话总结

Context Tracking 是内核在用户态↔内核态 / idle / guest 边界插入的探针,通过维护每 CPU 的上下文状态并告知 RCU 进入/退出 Extended Quiescent State,使得 NO_HZ_FULL CPU 在用户态运行时可以停掉周期 tick 且 RCU grace period 仍能正确结束,同时驱动进程虚拟时间(vtime)精确记账。

http://www.jsqmd.com/news/1151003/

相关文章:

  • 双节锂电池主动均衡系统设计与STM32实现
  • 03.01.02.ComfyUI Ubuntu:环境搭建篇(集成 AnythingLLM调用ComfyUI的API 使用Custom Skills方式)
  • 终极Windows更新修复指南:3分钟快速解决更新卡顿问题
  • 如何快速集成移动端PDF预览:pdfh5.js的完整解决方案
  • Vin象棋:三步快速掌握AI象棋分析,免费享受专业级棋局助手
  • 国家中小学智慧教育平台电子课本下载器:如何轻松获取官方教材PDF的完整指南
  • 暗黑破坏神2存档编辑器完整指南:3步实现游戏数据自由修改
  • GPT-4 Turbo 128K 上下文实战:5步构建长文档智能问答系统
  • 如何零基础玩转XUnity.AutoTranslator:让外语游戏秒变中文的终极指南
  • [实战] 2026年工程图纸GDT形位公差识别与数字化检验计划(FAI/PPAP)构建指南
  • 5个高效实用的Stardew Valley模组:让你的农场管理变得轻松简单
  • 5分钟完全解锁索尼相机隐藏功能:OpenMemories-Tweak终极指南
  • VASPsol隐式溶剂模型:5分钟快速上手DFT溶剂化计算的终极指南
  • 数据库规范化理论实战:从1NF到BCNF的5个经典模式分解案例
  • Sublime Text轻量编辑器原理与实战配置指南
  • 3分钟免费拥有macOS同款鼠标指针:Windows用户的终极美化指南
  • SketchUp STL插件:5分钟掌握3D打印模型导入导出终极指南
  • 如何5分钟搭建智能茅台预约系统:告别手动抢购的终极解决方案
  • 如何将B站缓存的m4s视频转换为通用MP4格式:完整指南
  • TensorFlow Lite在嵌入式Linux上的交叉编译与部署
  • 物联网设备安全芯片SE050与STM32L031K6集成方案
  • 3分钟找回经典B站:Bilibili-Old让你重拾简单纯粹的观看体验
  • 本地部署AI漫剧生成:LibTV+Seedance2+豆包大模型完整指南
  • VLA-Pro:具身智能中的程序性记忆建模与动作感知检索
  • 北京华恒智信为物业服务行业破解基层人员老龄化、低认同、高流失难题
  • KITTI 数据集转 ROS Bag 实战:lidar2rosbag_KITTI 工具 3 步配置与 A-LOAM 适配要点
  • LabelImg 与 Roboflow Annotate 对比:3 个维度解析本地工具与云端平台的选型策略
  • 5分钟学会:如何将B站缓存视频永久保存为MP4格式
  • OpenClaw:面向业务人员的AI工作流调度中枢
  • JSON Schema自动化生成进入倒计时:ChatGPT+JSON Schema Draft-2020-12协同工作流(限前500名领取Schema质量评分工具)