news 2026/6/9 2:32:24

cfs调度类深入解刨——clock科普篇

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
cfs调度类深入解刨——clock科普篇

上一篇《cfs调度类深入解刨——EAS科普篇》中讲述了kernel中CFS调度器的EAS特性具体作用及原理。
本篇文章讲述时钟定时器的原理及实现方式。


定时器
sched_clock_data
相关结构
系列文章列表


定时器

在Linux内核中,内核定时器是维持整个操作系统“时间流动”与“延迟异步任务”的硬核基础设施。从进程的时间片轮转、网络协议栈的超时重传,到硬件驱动的消抖,全都依赖定时器来敲击鼓点。
Linux内核为了同时兼顾“高吞吐量(管理海量定时任务)”与“高精度(纳秒级硬实时响应)”的矛盾诉求,在底层精妙地设计了两套完全独立的物理骨架:时间轮(Timer Wheel)与高精度定时器(HRTimer)。

时钟源(Clock Source)
系统一切时间的物质基石是物理时钟源。它负责产生最原始的、高频稳定的物理振荡。
物理本质:主板上的石英晶体振荡器(Crystal Oscillator)。通电后,晶体以极其精准的频率(如14.318MHz或24MHz)发生微观物理震荡。
硬件分化(不同维度的账本):为了在多核和高并发下精准数数,CPU内部和主板上衍生出了不同的高级时钟源硬件:
TSC(Time Stamp Counter,时间戳计数器):x86 CPU内部的超高频寄存器。每个CPU周期自增1,读取它只需要一条汇编指令(rdtsc),速度极快,是内核做纳秒级高精度测量的首选。
HPET(高精度事件定时器):主板芯片组上的独立硬件,频率通常在10MHz 以上,稳定但读取需要走总线,开销比TSC大。
ARM Generic Timer:ARM架构下的架构定时器(System Counter),向所有CPU核心提供同步的绝对时间基准。


时钟信号(Clock Signal)与硬件中断
原始的晶振高频信号无法直接被内核识别,必须经过硬件芯片的规整和计数,转化为电信号和硬件中断投递给CPU。



硬件信号转化为内核时间轴
当Timer IRQ击穿CPU斩断当前运行的进程后,Linux内核的时间子系统正式登场,将离散的中断信号抽象为有连续逻辑的时间轴。
内核在这一阶段维护了两个大名鼎鼎的时间“度量衡”:
低精度度量衡:jiffies(时钟滴嗒)
内核设定一个固定频率(如HZ=1000)。硬件定时器每隔1ms触发一次Timer IRQ,内核就在硬中断里将全局变量jiffies加1。

高精度度量衡:ktime_t(纳秒级绝对时间)
内核在初始化时会调用clocksource_register()注册当前系统中最优秀的硬件时钟源(如TSC)。
当需要高精度时间时,内核直接通过调用timekeeping层的接口(如ktime_get()),利用数学公式将当前的TSC寄存器刻度换算为绝对的纳秒(ns)时间戳。


驱动内核定时器(高低并行的时间派发)
有了内核时间轴(jiffies和ktime_t)之后,时钟信号终于可以用来唤醒、推进我们在前文详细拆解的内核定时器了。
根据时钟信号精度的不同,内核的定时器分为了两条完全独立的执行逻辑:


传统低精度定时器(timer_list)的驱动闭环
信号联动:硬件每发出一个周期的Tick信号,update_process_times()就会被调用一次。
时间轮推进:它会触发run_local_timers()。内核的时间轮算法(Timer Wheel)像剥洋葱一样,检查当前jiffies对应的数组格子(Slot)里有没有过期的timer_list任务。如果有,激活软中断去执行你的驱动回调函数。


高精度定时器(hrtimer)的驱动闭环
信号机制:这里不再依赖周期性的Tick信号,而是使用“按需精准倒计时”。
树状推进:当高精硬件定时器倒计时归零爆发出精准IRQ时,内核直接切入hrtimer_interrupt()。它直接抓取红黑树最左侧(最紧急)的hrtimer节点,对比当前TSC纳秒时间。如果超时,立刻原地拔起执行。
信号再编程:执行完后,内核会看一眼红黑树里剩下排第二的那个定时器还有多少纳秒过期,然后当场写寄存器重新配置硬件定时器,命令它在精准的Delta纳秒后再次发出中断信号。


sched_clock_data

在Linux内核中,struct sched_clock_data(简称 scd)是调度器时钟(sched_clock)在配置了 CONFIG_HAVE_UNSTABLE_SCHED_CLOCK时,用于纠正和同步多核间时钟漂移、保证时钟单调性(Monotonicity)的核心数据结构。 它的存在是因为硬件时钟源(如x86的TSC)可能是不稳定的。

内核调度器(CFS/EEVDF)对时间极度敏感,它需要高频调用sched_clock获取当前CPU的纳秒级时间戳。该时钟必须满足两个严苛条件:
极快(Fast):在几纳秒内返回,不能加锁,不能有大开销。
单调递增(Monotonic):时间绝对不能倒流。


硬件时钟源的痛点
在理想状态下,内核直接读取硬件的TSC(Time Stamp Counter)寄存器。但早期或非恒定频率的CPU会导致以下问题:
非恒定频率:CPU降频或进入超频状态(如因节能、省电技术cpufreq介入),会导致TSC的递增速度变慢或变快。
多核不对齐:不同CPU核心之间的TSC初始值或计数可能存在细微偏差。如果一个进程从CPU0切换到CPU1,可能读取到一个比过去更小的时间(时间倒流),引发调度器的严重混乱。

为了解决这个问题,Linux内核设计了基于Per-CPU的sched_clock_data结构体,来过滤和校准这个不稳定的硬件时钟。


sched_clock_data并不是孤立的数据,它通过双层组合的方式,将低级时钟源与高级时钟轴结合起来

  1. 底层:硬件时钟源(Hardware Clock Source)
    TSC (Time Stamp Counter):
    在启用不稳定时钟支持时,系统默认读取TSC(作为 sched_clock的源)。
    若 TSC 被标记为 unstable(不稳定),其 sched_clock() 的 delta 增量将被打上标签(cs->flags|=CLOCK_SOURCE_UNSTABLE;,通过scd进行多重过滤限制)。

  2. 顶层:GTOD (Generic Time of Day) 与 CLOCK_MONOTONIC
    内核全局的timekeeping模块(基于HPET、晶振等更稳定的全局硬件)维护着CLOCK_MONOTONIC。
    GTOD的精度虽然高且绝对稳定,但它的读取通常需要持有全局锁,且计算路径长,无法满足调度器每秒数百万次的无锁高频访问。

  3. 协同机制
    内核采用 “GTOD 搭建框架,sched_clock填充内核” 的策略:
    以系统的周期性时钟中断(Tick)为基准,每一次 Tick 到来时,内核就会更新一次sched_clock_data的tick_gtod和tick_raw基准线。在两次Tick之间,调度器通过增量计算(Delta)来保证高分辨率。


相关结构
structsched_clock_data{在多核系统中同步和校正时钟。每个CPU可能有自己的结构体实例,以避免锁竞争,并缓存一些时间值 u64 tick_raw;通常用于存储原始的硬件计数器值(例如TSC寄存器值)。这个值是从硬件直接读取的,没有经过任何转换 u64 tick_gtod;上一次Tick时,GTOD(全局时间基础,来自更稳定的全局时钟源)的值 u64 clock;经过计算后的调度时钟值,以纳秒为单位,并且是单调递增的。通常这个值是通过将原始计数器值转换为纳秒并可能进行一些调整(如偏移)得到的};enumperf_sw_ids{表示perf软件事件ID PERF_COUNT_SW_CPU_CLOCK=0,CPU时钟,高分辨率计时器,测量CPU时间 PERF_COUNT_SW_TASK_CLOCK=1,任务时钟,测量特定任务占用的CPU时间 PERF_COUNT_SW_PAGE_FAULTS=2,总缺页异常(包括主要和次要缺页) PERF_COUNT_SW_CONTEXT_SWITCHES=3,上下文切换次数 PERF_COUNT_SW_CPU_MIGRATIONS=4,进程在不同CPU间迁移的次数 PERF_COUNT_SW_PAGE_FAULTS_MIN=5,次要缺页异常(不需要磁盘I/O) PERF_COUNT_SW_PAGE_FAULTS_MAJ=6,主要缺页异常(需要磁盘I/O) PERF_COUNT_SW_ALIGNMENT_FAULTS=7,对齐错误引起的缺页异常(某些架构) PERF_COUNT_SW_EMULATION_FAULTS=8,指令模拟导致的故障(如未对齐访问,某些架构模拟) PERF_COUNT_SW_DUMMY=9,虚拟事件,用于占位或测试 PERF_COUNT_SW_BPF_OUTPUT=10,BPF程序输出事件,用于perf与BPF交互 PERF_COUNT_SW_CGROUP_SWITCHES=11,cgroup内部或cgroup间切换事件(跟踪cgroup变化) PERF_COUNT_SW_MAX,/* non-ABI */};structrq{每个cpu的rq(runqueues)队列......unsignedintclock_update_flags;时钟更新标志,用于指示时钟更新状态 u64 clock;运行队列的时钟,用于调度时间记录,记录实际的物理时间 u64 clock_task ____cacheline_aligned;任务时钟,与时钟在同一缓存行,记录执行task的时间 u64 clock_pelt;PELT(Per Entity Load Tracking)时钟,用于负载跟踪unsignedlonglost_idle_time;丢失的空闲时间,用于计算CPU空闲时间,记录CPU本应处于空闲状态、但实际因硬件/内核逻辑未被统计为空闲的 “虚假空闲时长” u64 clock_pelt_idle;cpu进入的空闲状态的PELT时钟时间 u64 clock_idle;空闲时钟#ifndefCONFIG_64BITu64 clock_pelt_idle_copy;u64 clock_idle_copy;#endif......};structcfs_rq{......u64 exec_clock;实际执行时间累计值 u64 throttled_clock;被限制的时间(时钟) u64 throttled_clock_pelt;当 CFS 运行队列被限制时(即由于 CPU 带宽控制,CFS 运行队列不能运行任务),已经过去的PELT时钟时间 u64 throttled_clock_pelt_time;限流起始的PELT时间戳 u64 throttled_clock_self;被限制的自身时钟 u64 throttled_clock_self_time;被限制的自身时间点......};

系列文章列表

《cfs调度类深入解刨——核心结构的用意》
《cfs调度类深入解刨——核心结构细节分析》
《cfs调度类深入解刨——最新内核细节分析1》
《cfs调度类深入解刨——最新内核细节分析2》
《cfs调度类深入解刨——最新内核细节分析3》
《cfs调度类深入解刨——最新内核细节分析4》
《cfs调度类深入解刨——最新内核细节分析5》
《cfs调度类深入解刨——最新内核细节分析6》
《cfs调度类深入解刨——MMU科普篇》
《cfs调度类深入解刨——irq科普篇》
《cfs调度类深入解刨——EAS科普篇》

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/9 2:32:24

Windows终极优化指南:WinUtil一键解决系统臃肿与性能问题

Windows终极优化指南:WinUtil一键解决系统臃肿与性能问题 【免费下载链接】winutil Chris Titus Techs Windows Utility - Install Programs, Tweaks, Fixes, and Updates 项目地址: https://gitcode.com/GitHub_Trending/wi/winutil 你是否曾为Windows系统越…

作者头像 李华
网站建设 2026/6/9 2:30:13

西电B测点空气质量数据前端可视化套件(含ECharts图表与实拍素材)

本文还有配套的精品资源,点击获取 简介:一套开箱即用的空气质量监测前端展示方案,专为西安电子科技大学B测点设计。支持折线图、柱状图等多种ECharts图表形式,可对接实时或历史AQI、PM2.5、PM10、温湿度等环境数据。资源包内置…

作者头像 李华
网站建设 2026/6/9 2:28:40

巧用对称性让仿真速度翻倍:以Lumerical FDTD Solutions为例,详解周期性结构仿真设置

巧用对称性让仿真速度翻倍:Lumerical周期性结构仿真高阶指南在光子晶体、超表面和阵列天线的设计过程中,工程师们常常需要面对包含数百个重复单元的周期性结构。传统全尺寸仿真不仅消耗大量计算资源,更可能因时间成本过高而拖慢研发进度。本文…

作者头像 李华