4.5.1 系统时钟
4.5.1 系统时钟
📚 本节导读
学习时长: 约 25 分钟
难度级别: ⭐⭐⭐☆☆
前置知识: 中断处理基础、硬件定时器基础
🎯 学习目标
- 理解系统时钟(SysTick)在 RTOS 中的核心作用
- 掌握 tick 中断处理流程
- 理解 tick 频率配置与实时性能的关系
- 了解 tick 队列的数据结构和工作原理
- 理解系统时钟如何驱动任务延时和定时器
一、系统时钟概念
1.1 什么是系统时钟
系统时钟是 RTOS 的"心跳"(Heartbeat),它由硬件定时器产生周期性中断,为操作系统提供统一的时间基准。在 OneOS 中,系统时钟通常基于 Cortex-M 内核的 SysTick 定时器实现。
每个时钟中断称为一个系统节拍(System Tick)。所有与时间相关的操作——任务延时、定时器计时、时间片轮转——都依赖于系统节拍。
1.2 系统时钟的作用
┌─────────────────────────────────────────────────────────┐
│ 系统时钟 (SysTick) │
│ 周期: 1/OS_TICK_PER_SECOND 秒 │
└───────────────────────┬─────────────────────────────────┘
│
┌─────────────┼─────────────┐
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ 任务延时 │ │ 定时器 │ │ 时间片 │
│ tsleep/ │ │ 计时触发 │ │ 轮转调度 │
│ msleep │ │ │ │ │
└──────────┘ └──────────┘ └──────────┘系统时钟驱动以下核心功能:
- 任务延时:
os_task_tsleep(tick)和os_task_msleep(ms)使任务阻塞指定的 tick 数或毫秒数 - 定时器计时:所有软件定时器基于系统时钟做减计数,到期后触发回调
- 时间片轮转:同优先级任务的时间片在每次 tick 中断时递减,归零时触发调度
- Tick 队列管理:维护所有延时阻塞的任务,在 tick 中断中检查并唤醒到期任务
二、Tick 频率配置
2.1 OS_TICK_PER_SECOND
系统节拍频率由 OS_TICK_PER_SECOND 宏定义,在 oneos_config.h 中配置:
#define OS_TICK_PER_SECOND 100 /* 每秒 100 个 tick,即每 10ms 一个 tick */常用配置值:
| 频率 (Hz) | 周期 (ms) | 适用场景 |
|---|---|---|
| 100 | 10ms | 通用嵌入式系统,均衡性能与功耗 |
| 200 | 5ms | 需要较快响应时间的系统 |
| 500 | 2ms | 对实时性要求较高的系统 |
| 1000 | 1ms | 高精度定时需求 |
注意:频率越高,系统实时性越好,但中断开销也越大,功耗越高。选择时需要权衡系统实时性需求和 CPU 负载。
2.2 Tick 与真实时间的转换
OneOS 提供了 tick 与毫秒的转换函数,实现在 os_clock.c 中:
/* 毫秒转 tick */
os_tick_t os_tick_from_ms(uint32_t ms);
/* tick 转毫秒 */
uint32_t os_ms_from_tick(os_tick_t tick);
/* 获取每秒 tick 数 */
uint32_t os_get_ticks_per_second(void);转换公式:
tick = OS_TICK_PER_SECOND × (ms / 1000) + 向上取整(OS_TICK_PER_SECOND × (ms % 1000) / 1000)
ms = (1000 / OS_TICK_PER_SECOND) × tick2.3 获取和设置系统 tick
/* 获取当前系统 tick 值 */
os_tick_t os_tick_get_value(void);
/* 设置系统 tick 值(谨慎使用) */
void os_tick_set_value(os_tick_t tick);三、Tick 中断处理流程
3.1 os_tick_increase 函数
每次 tick 中断发生时,内核调用 os_tick_increase() 函数(位于 kernel/source/os_clock.c),该函数执行以下核心操作:
os_tick_increase()
│
├─ 1. 全局 tick 计数器 gs_os_tick++
│
├─ 2. 处理 tick 队列(检查并唤醒到期延时任务)
│
├─ 3. 处理时间片轮转(递减 current_task->remaining_time_slice)
│
├─ 4. 处理哈希桶定时器(若启用 OS_USING_HASH_BUCKET_TIMER)
│ ├─ k_timer_need_handle() 检查是否有到期定时器
│ └─ k_move_timer_list_one_step() 推进定时器列表
│
├─ 5. 处理单链表定时器(若启用 OS_USING_SINGLE_LIST_TIMER)
│ ├─ 硬件定时器:k_single_list_timer_tick_proc() 在 ISR 中处理
│ └─ 软件定时器:k_single_list_timer_need_handle() 检查是否有到期
│
└─ 6. 若需要调度,触发 OS_KERNEL_EXIT_SCHED()3.2 Tick 中断中的关键原则
重要:tick 中断处理函数在内核临界区中执行,应尽量保持简短高效。硬件定时器的回调函数在 tick ISR 中直接执行,必须遵守 ISR 的限制:不能阻塞、不能调用可能阻塞的 API、执行时间必须极短。
四、Tick 队列
4.1 数据结构
Tick 队列是内核管理延时阻塞任务的核心数据结构,定义在 kernel/source/os_clock.c 中:
#define TICK_Q_BUCKETS 8
static os_list_node_t gs_os_tickq_bucket[TICK_Q_BUCKETS]; /* 8 个双向链表桶 */
static os_tick_t gs_os_tick = 0; /* 系统全局 tick 计数 */4.2 核心函数
Tick 队列相关的核心函数声明在 kernel/source/os_kernel_internal.h 中:
void k_tickq_init(void); /* 初始化 tick 队列 */
void k_tickq_put(struct os_task *task, os_tick_t timeout); /* 将任务放入 tick 队列 */
void k_tickq_remove(struct os_task *task); /* 从 tick 队列中移除任务 */4.3 Tick 队列工作原理
Tick 队列采用哈希桶 + delta 链表的设计:
gs_os_tickq_bucket[8]
┌───┐ ┌───┐ ┌───┐ ┌───┐
│ 0 │ ──→ │T1 │ ──→ │T2 │ (空)
├───┤ └───┘ └───┘
│ 1 │ ──→ (空)
├───┤
│ 2 │ ──→ │T3 │
├───┤ └───┘
│...│
└───┘- 哈希桶索引:
bidx = task->tick_absolute & (TICK_Q_BUCKETS - 1),将任务按绝对超时时间的低 3 位分布到 8 个桶中 - Delta 排序:同一桶内,任务按超时时间排序(使用 delta 值避免溢出问题)
- Tick 处理:每次 tick 中断只检查
gs_os_tick & 7对应的桶,将到期任务唤醒并加入就绪队列
4.4 任务延时与 Tick 队列的关系
当任务调用 os_task_tsleep(tick) 或 os_task_msleep(ms) 时:
- 内核计算任务的绝对超时时间:
task->tick_absolute = gs_os_tick + timeout - 将任务从就绪队列中移除,状态设为
OS_TASK_STATE_SLEEP - 调用
k_tickq_put(task, timeout)将任务插入 tick 队列对应的桶中 - 触发调度,切换到其他就绪任务运行
- 当系统 tick 到达任务的绝对超时时间时,
os_tick_increase()将任务从 tick 队列中移除并唤醒
五、低功耗 Tickless 模式
OneOS 支持低功耗 Tickless 模式(OS_USING_TICKLESS_LPMGR),在系统空闲时停止系统时钟,使 CPU 进入深度睡眠模式以降低功耗:
#ifdef OS_USING_TICKLESS_LPMGR
os_tick_t os_tickless_get_sleep_ticks(void); /* 获取可睡眠的 tick 数 */
void os_tickless_update(os_tick_t sleep_ticks); /* 唤醒后更新 tick */
#endif工作原理:
- 系统空闲时,计算下一个需要唤醒的 tick(取任务延时和定时器到期时间的最小值)
- 设置硬件定时器在指定时间后唤醒 CPU
- 唤醒后,调用
os_tickless_update()补偿因睡眠而跳过的 tick,更新 tick 队列和定时器状态
📝 本节小结
系统时钟是 OneOS 的"心跳",通过 SysTick 硬件定时器产生周期性中断,为整个系统提供时间基准。OS_TICK_PER_SECOND 定义了每秒 tick 数,决定了系统的实时性粒度。Tick 队列采用哈希桶 + delta 链表的设计,在每次 tick 中断中高效地检查并唤醒到期任务。系统时钟驱动着任务延时、定时器计时和时间片轮转三大核心功能,是 RTOS 时序逻辑的基础。