4.5.3 单链表定时器
4.5.3 单链表定时器
📚 本节导读
学习时长: 约 40 分钟
难度级别: ⭐⭐⭐⭐☆
前置知识: 双向链表操作、中断上下文与任务上下文的区别、系统时钟概念、位运算
单链表定时器(Single List Timer)是 OneOS 内核提供的另一种软件/硬件定时器实现方案。它把所有活跃定时器维护在一个按剩余时间升序排列的链表中,链表头部就是"下一个最先到期"的定时器。它最大的特点是支持硬件定时器(回调在 tick ISR 中直接执行,延迟极低)与软件定时器(回调在定时器任务中执行,灵活度高)两种模式,并内置了周期性定时器的补偿机制。
说明:本节的单链表版定时器 API 实现源码见下方"六、单链表定时器 API 实现";统一的 API 签名、参数、返回值与使用示例见
4.5.4-定时器API.md。本节主体聚焦单链表定时器的数据结构、排序链表机制、硬件/软件两种 tick 处理流程与对外 API 实现。
🎯 学习目标
- 理解单链表定时器的工作原理和排序链表结构
- 掌握"相对 tick(round_ticks 差值)"的链表思想
- 掌握硬件定时器和软件定时器的区别与使用场景
- 理解周期性定时器补偿机制
- 掌握单链表版
os_timer_*对外 API 的实现流程 - 能够在哈希桶定时器和单链表定时器之间做出合理选择
一、单链表定时器概念
1.1 设计思想
单链表定时器将所有活跃定时器维护在一个按剩余时间升序排列的链表中。链表头部是剩余时间最短的定时器,尾部是剩余时间最长的定时器。每次 tick 中断只需处理链表头部的定时器(递减或触发),因此每个 tick 的检查开销是 O(1)。
类比:单链表定时器像"一列按剩余时间从短到长排好的队伍"。每次到点,只关心队首那个人(最先到期),处理完他再看新的队首,无需关心队伍后排。
1.2 核心特点
- 排序链表:定时器按剩余时间升序排列,每次 tick 只处理链表头部
- 硬件定时器:回调在 tick ISR 中执行,延迟最小,但限制最多
- 软件定时器:回调在定时器任务上下文中执行,功能更灵活
- 周期性补偿:支持对周期性定时器执行时间偏差进行补偿
1.3 单链表定时器使用场景
一句话核心
单链表定时器适用于定时器数量较少(一般 50 个以内)、对响应延迟敏感、或需要在 ISR 中执行回调的场景。
机制对比表
| 维度 | 单链表定时器 | 哈希桶定时器 | 信号量 | 事件 |
|---|---|---|---|---|
| 插入时间复杂度 | O(n) | O(1) | - | - |
| 定时/延时能力 | 强(硬/软两种) | 强(仅软件) | 有(超时等待) | 有(超时等待) |
| 是否支持 ISR 回调 | 支持(硬定时器) | 不支持 | 否 | 否 |
| 回调上下文 | tick ISR / 定时器任务 | 定时器任务 | 唤醒任务 | 唤醒任务 |
| 典型数量 | < 50 | > 50 | - | - |
典型场景
场景一:硬定时器——高实时 GPIO 翻转 / 寄存器操作
- 谁在做什么:一个精确到 tick 周期的硬件动作(如产生精确 PWM 边沿、定时读取外设寄存器)。
- 为什么用硬定时器:回调直接在 tick ISR 中执行,到期即触发,无任务调度延迟,实时性最高。
- 实际工作情况:
os_timer_create时传入OS_TIMER_FLAG_HARD_TIMER,到期后 ISR 中翻转 GPIO,抖动为一个 tick 内。
场景二:软定时器——周期数据采集、状态检查、超时处理
- 谁在做什么:周期采样任务、连接超时检测等对延迟不太敏感、但需要较灵活操作的逻辑。
- 为什么用软定时器:回调在任务上下文执行,可以调用信号量、内存分配等 API。
- 实际工作情况:
OS_TIMER_FLAG_SOFT_TIMER,定时器任务到期后执行回调,回调里发信号量唤醒采集任务。
选型边界表(何时不该用它)
| 场景 | 该用什么 | 为什么 |
|---|---|---|
| 定时器数量很多(> 50)且频繁创建销毁 | 哈希桶定时器 | 单链表插入 O(n),数量多时开销大 |
| 只需要软件定时器、无需 ISR 回调 | 哈希桶定时器 | 哈希桶插入 O(1),更适合大批量 |
| 只在中断中等待一个超时 | 任务阻塞 + tick 队列 | 无需专门定时器对象 |
结合源码的使用注意点
- 若只编译
OS_HARD_SINGLE_LIST_TIMER,未指定标志的定时器会默认变硬定时器(k_single_list_timer_check_flag强制flag &= ~OS_TIMER_FLAG_TASK_SCHED);反之只编译 soft 时默认变软定时器。 - 硬定时器回调在 tick ISR 中执行,绝不能阻塞、不能调用延时/信号量等阻塞 API,否则会卡死整个系统。
- 周期性补偿
OS_TIMER_PERIODIC_COMPENSATE默认值为0(不启用),需显式改为非零值才生效。
二、内部数据结构
2.1 定时器控制块(dummy_timer)字段详解
单链表定时器复用了内核统一的对象控制块 dummy_timer(定义在 kernel/include/os_dummy.h)。其中与哈希桶不同的地方在于:单链表定时器不使用 active_node 的哈希桶相关字段,而是直接通过 list_dummy 节点挂到排序链表中。
来源: kernel/include/os_dummy.h
struct dummy_timer
{
void (*timeout_func_dummy)(void *timeout_param); /* Timeout function. */
void *parameter_dummy; /* Timeout function's parameter. */
os_tick_t init_ticks_dummy; /* Timer timeout tick. */
os_tick_t round_ticks_dummy; /* Timeout tick remaining. */
#ifdef OS_USING_HASH_BUCKET_TIMER
uint32_t index_dummy;
#endif
os_list_node_t list_dummy;
dummy_timer_active_node_t active_node_dummy;
char name_dummy[OS_NAME_MAX + 1];
};单链表定时器用到的字段:
| 字段 | 类型 | 含义(单链表上下文) |
|---|---|---|
timeout_func_dummy | 函数指针 | 超时回调函数 |
parameter_dummy | void * | 回调函数的参数 |
init_ticks_dummy | os_tick_t | 初始超时 tick 数 |
round_ticks_dummy | os_tick_t | 相对上一个定时器的剩余 tick 差值(排序链表的核心),链表头部节点的 round_ticks 是"到真正超时还需 tick 数" |
list_dummy | os_list_node_t | 排序链表节点,把所有活跃定时器串成一条按 round_ticks 升序的链表 |
active_node_dummy | dummy_timer_active_node_t | 活跃节点,其 flag 字段保存定时器状态标志 |
提醒:用户不应直接访问这些内部字段,应通过
os_timer_*API 操作(见4.5.4-定时器API.md)。
2.2 定时器标志与全局变量
定时器标志(来自 os_timer.h)
来源: kernel/include/os_timer.h
#ifdef OS_USING_SINGLE_LIST_TIMER
#if defined(OS_HARD_SINGLE_LIST_TIMER) && defined(OS_SOFT_SINGLE_LIST_TIMER)
#define OS_TIMER_FLAG_HARD_TIMER 0x0U /* Hard timer,the timer's callback function will be called in tick isr. */
#define OS_TIMER_FLAG_SOFT_TIMER 0x4U /* Soft timer,the timer's callback function will be called in timer task. */
#endif /* end of OS_HARD_SINGLE_LIST_TIMER & OS_SOFT_SINGLE_LIST_TIMER */
/* used to cfg periodic timer compensate(timeout cb still too long) */
#define OS_TIMER_PERIODIC_COMPENSATE 0
#endif /* end of OS_USING_SINGLE_LIST_TIMER */注:
os_timer.h中的OS_TIMER_FLAG_HARD_TIMER(0x0)与OS_TIMER_FLAG_SOFT_TIMER(0x4)分别与源码内部使用的OS_TIMER_FLAG_TICK_SCHED(0x00)和OS_TIMER_FLAG_TASK_SCHED(0x04)值相同,是对用户暴露的等价别名。实际判断定时器是硬还是软,看active_node.flag的 bit2(值 0x4)。
内部标志定义(来自 os_timer_slist.c)
来源: kernel/source/os_timer_slist.c
#define OS_TIMER_FLAG_DEACTIVATED 0x0U /* Timer is deactive. */
#define OS_TIMER_FLAG_ACTIVATED 0x2U /* Timer is active. */
#define OS_TIMER_FLAG_TICK_SCHED 0x00 /* 0x00 timer cb be called in tick isr. */
#define OS_TIMER_FLAG_TASK_SCHED \
0x04 /* 0x04 timer cb be called in timer task, remember the same as OS_TIMER_FLAG_SOFT_TIMER */
#define OS_TIMER_FLAG_NOT_INITED 0x0U
#define OS_TIMER_FLAG_INITED 0x8U
#define OS_TIMER_FLAG_STATIC 0x0U
#define OS_TIMER_FLAG_DYNAMIC 0x10U
#define OS_TIMER_FLAG_DO_TIMEOUT 0x20U /* now timer is timeout. */标志位含义表:
| 标志 | 位 | 含义 |
|---|---|---|
OS_TIMER_FLAG_ACTIVATED | bit1(0x2) | 定时器是否已激活 |
OS_TIMER_FLAG_TASK_SCHED | bit2(0x4) | 置位 = 软定时器(回调在定时器任务);清零 = 硬定时器(回调在 tick ISR) |
OS_TIMER_FLAG_INITED | bit3(0x8) | 定时器是否已初始化 |
OS_TIMER_FLAG_DYNAMIC | bit4(0x10) | 是否堆上动态分配 |
OS_TIMER_FLAG_DO_TIMEOUT | bit5(0x20) | 该定时器正处于"超时处理中"(回调尚未返回) |
全局变量(来自 os_timer_slist.c)
来源: kernel/source/os_timer_slist.c
#ifdef OS_HARD_SINGLE_LIST_TIMER
static os_timer_t *gs_tick_sched_timer_head;
static os_list_node_t gs_tick_sched_timer_list = OS_LIST_INIT(gs_tick_sched_timer_list);
#endif /* end of OS_HARD_SINGLE_LIST_TIMER */
#ifdef OS_SOFT_SINGLE_LIST_TIMER
#define OS_TIMER_TASK_PRIO 0
/* for soft timer, used for periodic timer compensate, if need define it */
#if OS_TIMER_PERIODIC_COMPENSATE
#define OS_TIMER_TICK_DIFF(a, b) (a >= b) ? (a - b) : (OS_UINT32_MAX - b + a + 1)
#endif /* end of OS_TIMER_PERIODIC_COMPENSATE */
OS_ALIGN(OS_ALIGN_SIZE)
static uint8_t gs_sched_timer_task_stack[OS_TIMER_TASK_STACK_SIZE];
static os_task_t gs_task_sched_timer_task;
static os_timer_t *gs_task_sched_timer_head;
static os_bool_t gs_task_sched_timer_need_sched;
static os_list_node_t gs_task_sched_timer_list = OS_LIST_INIT(gs_task_sched_timer_list);
#endif /* end of OS_SOFT_SINGLE_LIST_TIMER */全局变量说明:
| 变量 | 属于谁 | 数量 | 用途 |
|---|---|---|---|
gs_tick_sched_timer_head / gs_tick_sched_timer_list | 模块全局(hard) | 各 1 个 | 硬件定时器排序链表头指针与链表头 |
gs_task_sched_timer_head / gs_task_sched_timer_list | 模块全局(soft) | 各 1 个 | 软件定时器排序链表头指针与链表头 |
gs_task_sched_timer_need_sched | 模块全局(soft) | 1 个 | 是否有软件定时器到期需要调度 |
gs_task_sched_timer_task / gs_sched_timer_task_stack | 模块全局(soft) | 各 1 个 | 软件定时器任务控制块与栈 |
注意:硬、软定时器各自维护独立的排序链表(
gs_tick_sched_timer_list与gs_task_sched_timer_list),互不干扰。同一时间系统最多同时存在两条排序链表(一条硬、一条软)。
2.3 核心函数声明
来源: kernel/source/os_kernel_internal.h
#ifdef OS_USING_SINGLE_LIST_TIMER
extern void k_single_list_timer_module_init(void);
extern os_tick_t k_single_list_timer_get_next_remain_ticks(void);
extern os_bool_t k_single_list_timer_update_active_list(os_tick_t ticks);
#ifdef OS_SOFT_SINGLE_LIST_TIMER
extern os_bool_t k_single_list_timer_need_handle(void);
#endif
#ifdef OS_HARD_SINGLE_LIST_TIMER
extern void k_single_list_timer_tick_proc(void);
#endif
#endif三、排序链表的"相对 tick"机制
3.1 什么是"相对 tick"
单链表定时器不直接保存"每个定时器还剩多少绝对 tick",而是让链表里每个定时器的 round_ticks 表示"相对于前一个定时器还要多等多少 tick"。
- 链表头节点的
round_ticks= 距离真正超时还差的 tick 数(因为前面没有其他定时器)。 - 后续每个节点的
round_ticks= "这个定时器比前一个晚到期多少个 tick"。 - 因此,任意定时器的绝对剩余时间 = 它之前所有节点的
round_ticks之和。
类比:这就像列车时刻表——每趟车只记录"与上一趟车相隔多少分钟",而不记录绝对发车时刻。要知道某趟车几点开,把前面所有"相隔时间"加起来即可。这样每次 tick 只需关注第一趟车(头节点)。
3.2 数值演算:相对 tick 的插入与删除
沿用一套基准数据:假设链表中已有三个定时器 A、B、C,按到期先后排序。
| 定时器 | 期望绝对超时(相对现在) | 存储的 round_ticks(相对差值) |
|---|---|---|
| A(头) | 2 | 2 |
| B | 2 + 3 = 5 | 3 |
| C | 5 + 3 = 8 | 3 |
插入定时器 D(期望 6 tick 到期):从头遍历,比较 round_ticks 差值:
| 遍历步骤 | 比较 | 动作 |
|---|---|---|
| 与 A 比较 | D 差值 6 ≥ A 差值 2 | D 差值 = 6 - 2 = 4,继续 |
| 与 B 比较 | D 差值 4 ≥ B 差值 3 | D 差值 = 4 - 3 = 1,继续 |
| 与 C 比较 | D 差值 1 ≥ C 差值 3?否 | C 差值 = 3 - 1 = 2,D 插到 C 前 |
插入后链表为:A(2) → B(3) → D(1) → C(2)。此时绝对剩余分别为 2、5、6、8,仍有序。关键是 round_ticks 仍表示相对前一个的差值,总和不变。
删除定时器 B:B 的 round_ticks = 3,它的下一个是 D(差值 1)。删除 B 后,D 的前驱变成 A,需要把 B 的 3 加给 D:D 差值 = 1 + 3 = 4。链表变为 A(2) → D(4) → C(2),绝对剩余 2、6、8,依然正确。
这一思想正是源码
k_single_list_timer_insert_sort_list与k_single_list_timer_del_sort_list所做的工作。
3.3 核心辅助函数:k_single_list_timer_insert_sort_list
来源: kernel/source/os_timer_slist.c
static void k_single_list_timer_insert_sort_list(os_timer_t *timer)
{
os_list_node_t *timer_head_list;
os_timer_t *iter;
timer_head_list = k_single_list_get_timer_head_list(timer);
os_list_for_each_entry(iter, timer_head_list, os_timer_t, list)
{
/* if the add timer round_ticks is the same as the exist timer, add the last */
if (timer->round_ticks >= iter->round_ticks)
{
timer->round_ticks -= iter->round_ticks;
}
else
{
/* add before the iter, so the iter->round_ticks should delete timer->round_ticks */
iter->round_ticks -= timer->round_ticks;
break;
}
}
/* if the timer list is null or the timer is add the last of list, the iter = timer_head_list */
os_list_add_tail(&iter->list, &timer->list);
if (OS_TIMER_IS_TASK_SCHED(timer))
{
#ifdef OS_SOFT_SINGLE_LIST_TIMER
gs_task_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}
else
{
#ifdef OS_HARD_SINGLE_LIST_TIMER
gs_tick_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}
return;
}分步详解
第一步:确定所属链表(无条件执行)
timer_head_list = k_single_list_get_timer_head_list(timer);k_single_list_get_timer_head_list()根据定时器是硬(OS_TIMER_FLAG_TASK_SCHED清零)还是软(置位),返回gs_tick_sched_timer_list或gs_task_sched_timer_list。- 若既非软也非硬(未配置对应宏),返回
OS_NULL,触发OS_ASSERT_EX断言。
第二步:遍历找到插入点并调整差值(条件:链表非空)
os_list_for_each_entry(iter, timer_head_list, os_timer_t, list)
{
/* if the add timer round_ticks is the same as the exist timer, add the last */
if (timer->round_ticks >= iter->round_ticks)
{
timer->round_ticks -= iter->round_ticks;
}
else
{
/* add before the iter, so the iter->round_ticks should delete timer->round_ticks */
iter->round_ticks -= timer->round_ticks;
break;
}
}- 进入条件:链表里有定时器可遍历。
- 若新定时器差值 ≥ 当前节点差值:新定时器的差值减去当前节点差值,继续向后(说明新定时器比当前节点到期更晚)。
- 若新定时器差值 < 当前节点差值:说明应插在当前节点之前,把当前节点差值减去新定时器差值,跳出循环。
- 若链表为空,
for不执行,iter停留在链表头节点。
第三步:挂入链表并更新头指针(无条件执行)
os_list_add_tail(&iter->list, &timer->list);
if (OS_TIMER_IS_TASK_SCHED(timer))
{
#ifdef OS_SOFT_SINGLE_LIST_TIMER
gs_task_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}
else
{
#ifdef OS_HARD_SINGLE_LIST_TIMER
gs_tick_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}- 把新定时器插到
iter之前(os_list_add_tail到iter即插在iter前面)。 - 重新计算链表头指针
gs_xxx_sched_timer_head(头节点永远是剩余时间最短的定时器)。
条件走向总结
k_single_list_timer_insert_sort_list() 条件走向总结:
├── 确定所属链表(硬链表 / 软链表;未配置则断言)
├── 遍历链表:
│ ├── timer->round_ticks >= iter->round_ticks(新定时器更晚到期)
│ │ └── timer->round_ticks -= iter->round_ticks,继续向后
│ └── timer->round_ticks < iter->round_ticks(新定时器更早到期)
│ └── iter->round_ticks -= timer->round_ticks,break
│ (链表为空时 for 不执行)
└── 插入 timer 到 iter 之前,重新计算链表头指针3.4 核心辅助函数:k_single_list_timer_del_sort_list
来源: kernel/source/os_timer_slist.c
static void k_single_list_timer_del_sort_list(os_timer_t *timer)
{
os_list_node_t *timer_head_list;
os_timer_t *next_timer;
timer_head_list = k_single_list_get_timer_head_list(timer);
/* the timer is the last node, direct delete, no need to proc round_ticks */
if (timer_head_list == timer->list.next)
{
os_list_del(&timer->list);
}
else
{
next_timer = os_list_entry(timer->list.next, os_timer_t, list);
next_timer->round_ticks += timer->round_ticks;
os_list_del(&timer->list);
}
if (OS_TIMER_IS_TASK_SCHED(timer))
{
#ifdef OS_SOFT_SINGLE_LIST_TIMER
gs_task_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}
else
{
#ifdef OS_HARD_SINGLE_LIST_TIMER
gs_tick_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}
return;
}分步详解
第一步:判断被删定时器是否尾节点(条件 timer_head_list == timer->list.next)
timer_head_list = k_single_list_get_timer_head_list(timer);
/* the timer is the last node, direct delete, no need to proc round_ticks */
if (timer_head_list == timer->list.next)
{
os_list_del(&timer->list);
}- 进入条件:被删定时器是链表尾节点(它的下一个就是链表头,说明它后面没有其他定时器)。
- 尾节点之后无节点需要"继承"差值,直接删除即可。
第二步:非尾节点,把差值加给下一个(条件 timer->list.next != timer_head_list)
else
{
next_timer = os_list_entry(timer->list.next, os_timer_t, list);
next_timer->round_ticks += timer->round_ticks;
os_list_del(&timer->list);
}- 被删定时器后面还有节点,则把被删定时器的差值加给它下一个节点(因为删掉它后,下一个节点的前驱变了,需要补齐差值),再删除。
第三步:更新链表头指针(无条件执行)
if (OS_TIMER_IS_TASK_SCHED(timer))
{
#ifdef OS_SOFT_SINGLE_LIST_TIMER
gs_task_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}
else
{
#ifdef OS_HARD_SINGLE_LIST_TIMER
gs_tick_sched_timer_head = os_list_first_entry_or_null(timer_head_list, os_timer_t, list);
#endif
}- 删除后重新计算链表头指针。
条件走向总结
k_single_list_timer_del_sort_list() 条件走向总结:
├── 确定所属链表
├── timer->list.next == 链表头(被删定时器是尾节点)
│ └── 直接删除,不调整差值
├── 否则(被删定时器后面还有节点)
│ └── 下一个节点 round_ticks += 被删定时器 round_ticks,再删除
└── 重新计算链表头指针本节 3.3 / 3.4 的相对 tick 插入/删除原理,配合 3.2 的数值演算表,是理解单链表定时器"为何 tick 处理只需看头节点"的关键。
四、硬件定时器 vs 软件定时器
4.1 硬件定时器(k_single_list_timer_tick_proc)
硬件定时器的回调函数在 tick ISR 上下文中直接执行:
SysTick 中断
│
└─ os_tick_increase()
│
└─ k_single_list_timer_tick_proc()
│
└─ 遍历到期定时器,在 ISR 中直接调用回调函数特点:
- 延迟最小:定时器到期后立即在 ISR 中执行,延迟为一个 tick 内
- 限制严格:回调函数必须在 ISR 中执行,不能阻塞、不能调用可能阻塞的 API
- 执行时间必须极短:长时间运行会阻塞所有中断和任务调度
- 适用场景:需要极高实时性的操作,如 GPIO 翻转、外设寄存器操作
硬件定时器 tick 处理源码
来源: kernel/source/os_timer_slist.c
void k_single_list_timer_tick_proc(void)
{
os_timer_t *head_timer;
os_list_node_t timeout_timer_list;
uint8_t test_flag;
void (*timeout_func)(void *timeout_param);
void *timeout_param;
OS_KERNEL_INIT();
OS_KERNEL_ENTER();
if (OS_NULL == gs_tick_sched_timer_head)
{
OS_KERNEL_EXIT();
return;
}
/* compatibility for lowpower sleep */
if (gs_tick_sched_timer_head->round_ticks > 1)
{
gs_tick_sched_timer_head->round_ticks--;
OS_KERNEL_EXIT();
return;
}
else
{
gs_tick_sched_timer_head->round_ticks = 0;
}
while (1)
{
head_timer = gs_tick_sched_timer_head;
if (OS_NULL == head_timer || head_timer->round_ticks)
{
break;
}
timeout_func = head_timer->timeout_func;
timeout_param = head_timer->parameter;
head_timer->active_node.flag |= OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
/* update the gs_tick_sched_timer_head */
gs_tick_sched_timer_head = os_list_first_entry_or_null(&gs_tick_sched_timer_list, os_timer_t, list);
test_flag = OS_TIMER_FLAG_PERIODIC;
/* if the timer is one shot timer, stop it */
if (test_flag != (head_timer->active_node.flag & test_flag))
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
}
/* if the timer timeout call os_timer_start/os_timer_stop/os_timer_destroy,
* if os_timer_destory called, the head_timer param is not exist
*/
os_list_init(&timeout_timer_list);
os_list_add(&timeout_timer_list, &head_timer->list);
OS_KERNEL_EXIT();
if (OS_NULL != timeout_func)
{
timeout_func(timeout_param);
}
OS_KERNEL_ENTER();
/* the timeout_func may call timer api, for example: os_timer_destroy/os_timer_start/os_timer_stop,
* if os_timer_destroy called, the head_timer will be null
*/
head_timer = os_list_first_entry_or_null(&timeout_timer_list, os_timer_t, list);
if (OS_NULL != head_timer)
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
/* no need to test the timer is one shot or perdoic,
* one shot timer start in timeout func or other task, perdoic timer should re-insert into list
*/
test_flag = OS_TIMER_FLAG_ACTIVATED;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
/* reset the round_tick, then add to the list */
head_timer->round_ticks = head_timer->init_ticks;
k_single_list_timer_insert_sort_list(head_timer);
}
}
}
OS_KERNEL_EXIT();
return;
}分步详解
第一步:头指针判空(条件 gs_tick_sched_timer_head == OS_NULL)
OS_KERNEL_ENTER();
if (OS_NULL == gs_tick_sched_timer_head)
{
OS_KERNEL_EXIT();
return;
}- 硬件定时器链表为空则直接返回,无定时器需要处理。
第二步:头定时器是否到期(条件 round_ticks > 1)
/* compatibility for lowpower sleep */
if (gs_tick_sched_timer_head->round_ticks > 1)
{
gs_tick_sched_timer_head->round_ticks--;
OS_KERNEL_EXIT();
return;
}
else
{
gs_tick_sched_timer_head->round_ticks = 0;
}- 进入条件(未到期):头定时器
round_ticks > 1,只需减 1(离到期又近了一个 tick),本 tick 不触发回调。 - 进入条件(到期):
round_ticks == 1,置 0,进入下面的 while 触发回调。round_ticks == 0(低功耗唤醒后的边界)也进入 while。
第三步:循环触发所有到期定时器(条件 head_timer != OS_NULL && head_timer->round_ticks == 0)
while (1)
{
head_timer = gs_tick_sched_timer_head;
if (OS_NULL == head_timer || head_timer->round_ticks)
{
break;
}
timeout_func = head_timer->timeout_func;
timeout_param = head_timer->parameter;
head_timer->active_node.flag |= OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
/* update the gs_tick_sched_timer_head */
gs_tick_sched_timer_head = os_list_first_entry_or_null(&gs_tick_sched_timer_list, os_timer_t, list);
test_flag = OS_TIMER_FLAG_PERIODIC;
/* if the timer is one shot timer, stop it */
if (test_flag != (head_timer->active_node.flag & test_flag))
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
}
/* if the timer timeout call os_timer_start/os_timer_stop/os_timer_destroy,
* if os_timer_destory called, the head_timer param is not exist
*/
os_list_init(&timeout_timer_list);
os_list_add(&timeout_timer_list, &head_timer->list);
OS_KERNEL_EXIT();
if (OS_NULL != timeout_func)
{
timeout_func(timeout_param);
}
OS_KERNEL_ENTER();
/* the timeout_func may call timer api, for example: os_timer_destroy/os_timer_start/os_timer_stop,
* if os_timer_destroy called, the head_timer will be null
*/
head_timer = os_list_first_entry_or_null(&timeout_timer_list, os_timer_t, list);
if (OS_NULL != head_timer)
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
/* no need to test the timer is one shot or perdoic,
* one shot timer start in timeout func or other task, perdoic timer should re-insert into list
*/
test_flag = OS_TIMER_FLAG_ACTIVATED;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
/* reset the round_tick, then add to the list */
head_timer->round_ticks = head_timer->init_ticks;
k_single_list_timer_insert_sort_list(head_timer);
}
}
}
OS_KERNEL_EXIT();
return;
}- 进入条件:头定时器存在且
round_ticks == 0(本 tick 到期)。 - 取出回调函数与参数,置
OS_TIMER_FLAG_DO_TIMEOUT(标记正在处理),从链表删除该定时器,更新头指针。 - 若是单次定时器,清除
OS_TIMER_FLAG_ACTIVATED(到期后停止)。 - 触发回调前
OS_KERNEL_EXIT()放开中断;回调在 ISR 中执行,结束后OS_KERNEL_ENTER()重新关中断。
第四步:到期后处理(条件 timeout_func != OS_NULL 之后重新入链)
head_timer = os_list_first_entry_or_null(&timeout_timer_list, os_timer_t, list);
if (OS_NULL != head_timer)
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
test_flag = OS_TIMER_FLAG_ACTIVATED;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
head_timer->round_ticks = head_timer->init_ticks;
k_single_list_timer_insert_sort_list(head_timer);
}
}
}
OS_KERNEL_EXIT();- 回调返回后,若定时器未被销毁且仍处于激活态(周期定时器或被重新 start 的定时器),则重置
round_ticks = init_ticks并重新插入排序链表,实现周期重激活。 - 若回调里调用
os_timer_destroy销毁了自己,head_timer为OS_NULL,则跳过。
条件走向总结
k_single_list_timer_tick_proc() 条件走向总结(tick ISR 中执行):
├── 硬件链表头为 OS_NULL(无硬件定时器)→ 直接返回
├── 头定时器 round_ticks > 1(未到期)
│ └── round_ticks--,返回
├── 头定时器 round_ticks <= 1(到期,置 0)
│ └── while 循环:
│ ├── 头定时器为 OS_NULL 或 round_ticks != 0 → break
│ ├── 到期 → 取出回调、从链表删除、更新头指针
│ │ ├── 单次定时器 → 清除 ACTIVATED
│ │ ├── OS_KERNEL_EXIT() 后在 ISR 中调用回调
│ │ └── 回调返回后:
│ │ ├── 定时器仍激活(周期/被重启)→ round_ticks=init_ticks 重新插入
│ │ └── 定时器已销毁 → 跳过
│ └── 继续 while 处理下一个到期定时器4.2 软件定时器(k_single_list_timer_need_handle + 定时器任务)
软件定时器的回调函数在定时器任务上下文中执行:
SysTick 中断
│
└─ os_tick_increase()
│
└─ k_single_list_timer_need_handle()
│
└─ 标记有到期定时器,触发调度
│
└─ 定时器任务获得 CPU 后执行回调函数特点:
- 延迟稍大:从定时器到期到回调执行,可能经过一次任务调度
- 限制较少:回调函数在任务上下文中执行,可以使用大多数 API
- 可以执行较复杂操作:如内存分配、信号量操作、日志输出等
- 适用场景:大多数定时器应用场景,如周期数据采集、状态检查、超时处理
软件定时器 tick 处理源码(k_single_list_timer_need_handle)
来源: kernel/source/os_timer_slist.c
os_bool_t k_single_list_timer_need_handle(void)
{
os_bool_t need_handle;
os_timer_t *iter;
if (OS_NULL == gs_task_sched_timer_head)
{
return OS_FALSE;
}
need_handle = OS_FALSE;
if (gs_task_sched_timer_head->round_ticks == 1)
{
/* now the timer arrived the timeout tick */
gs_task_sched_timer_need_sched = OS_TRUE;
gs_task_sched_timer_head->round_ticks = 0;
#if OS_TIMER_PERIODIC_COMPENSATE
os_tick_t now;
now = os_tick_get();
/* update all timerout timers' who's round_ticks = 0 timeout tick */
os_list_for_each_entry(iter, &gs_task_sched_timer_list, os_timer_t, list)
{
if (!iter->round_ticks)
{
iter->active_node.timeout_tick = now;
}
else
{
break;
}
}
#endif
}
else if (!gs_task_sched_timer_head->round_ticks)
{
gs_task_sched_timer_need_sched = OS_TRUE;
/* maybe the timer timeout cb do a long time */
os_list_for_each_entry(iter, &gs_task_sched_timer_list, os_timer_t, list)
{
if (!iter->round_ticks)
{
continue;
}
else
{
iter->round_ticks--;
break;
}
}
}
else
{
gs_task_sched_timer_head->round_ticks--;
}
if (gs_task_sched_timer_need_sched && (OS_TASK_STATE_SUSPEND == gs_task_sched_timer_task.state))
{
gs_task_sched_timer_task.state &= ~OS_TASK_STATE_SUSPEND;
gs_task_sched_timer_task.state |= OS_TASK_STATE_READY;
k_readyq_put(&gs_task_sched_timer_task);
need_handle = OS_TRUE;
}
return need_handle;
}分步详解
第一步:头指针判空(条件 gs_task_sched_timer_head == OS_NULL)
if (OS_NULL == gs_task_sched_timer_head)
{
return OS_FALSE;
}- 软件定时器链表为空则返回
OS_FALSE,无需调度。
第二步:按头定时器状态分三种情况处理
if (gs_task_sched_timer_head->round_ticks == 1)
{
/* now the timer arrived the timeout tick */
gs_task_sched_timer_need_sched = OS_TRUE;
gs_task_sched_timer_head->round_ticks = 0;
#if OS_TIMER_PERIODIC_COMPENSATE
os_tick_t now;
now = os_tick_get();
/* update all timerout timers' who's round_ticks = 0 timeout tick */
os_list_for_each_entry(iter, &gs_task_sched_timer_list, os_timer_t, list)
{
if (!iter->round_ticks)
{
iter->active_node.timeout_tick = now;
}
else
{
break;
}
}
#endif
}
else if (!gs_task_sched_timer_head->round_ticks)
{
gs_task_sched_timer_need_sched = OS_TRUE;
/* maybe the timer timeout cb do a long time */
os_list_for_each_entry(iter, &gs_task_sched_timer_list, os_timer_t, list)
{
if (!iter->round_ticks)
{
continue;
}
else
{
iter->round_ticks--;
break;
}
}
}
else
{
gs_task_sched_timer_head->round_ticks--;
}- 子分支 A(
round_ticks == 1,本 tick 到期):置gs_task_sched_timer_need_sched = OS_TRUE(需要调度),头定时器round_ticks置 0。若启用补偿,则记录当前 tick 到所有已到期(round_ticks == 0)定时器的timeout_tick。 - 子分支 B(
round_ticks == 0,上次到期回调执行太久导致漏计):置need_sched = OS_TRUE,并把下一个非零差值的定时器差值减 1(补偿漏掉的 tick)。 - 子分支 C(其他,
round_ticks > 1,未到期):头定时器差值减 1,不触发调度。
第三步:唤醒定时器任务(条件 need_sched 且定时器任务挂起)
if (gs_task_sched_timer_need_sched && (OS_TASK_STATE_SUSPEND == gs_task_sched_timer_task.state))
{
gs_task_sched_timer_task.state &= ~OS_TASK_STATE_SUSPEND;
gs_task_sched_timer_task.state |= OS_TASK_STATE_READY;
k_readyq_put(&gs_task_sched_timer_task);
need_handle = OS_TRUE;
}
return need_handle;- 进入条件:有到期定时器需要处理,且软件定时器任务处于挂起态。
- 把定时器任务置为 READY、放入就绪队列,返回
OS_TRUE,由os_tick_increase()置is_sched = OS_TRUE。
条件走向总结
k_single_list_timer_need_handle() 条件走向总结(tick ISR 中执行):
├── 软定时器链表头为 OS_NULL → 返回 OS_FALSE
├── 头定时器 round_ticks == 1(本 tick 到期)
│ ├── need_sched = TRUE,round_ticks = 0
│ │ └──(补偿模式)记录所有 round_ticks==0 定时器的 timeout_tick
│ └── 若定时器任务挂起 → 唤醒 → 返回 OS_TRUE
├── 头定时器 round_ticks == 0(回调耗时漏计)
│ ├── need_sched = TRUE
│ └── 下一非零差值定时器 round_ticks--;若任务挂起 → 唤醒 → 返回 OS_TRUE
├── 否则(round_ticks > 1,未到期)
│ └── 头定时器 round_ticks--,返回 OS_FALSE4.3 软件定时器任务(k_single_list_timer_task_entry)
软件定时器的回调由定时器任务 k_single_list_timer_task_entry 执行。其核心逻辑是:反复取链表头,若 round_ticks == 0 则触发回调。
来源: kernel/source/os_timer_slist.c
static void k_single_list_timer_task_entry(void *parameter)
{
os_timer_t *head_timer;
os_task_t *current_task;
os_list_node_t timeout_timer_list;
uint8_t test_flag;
void (*timeout_func)(void *timeout_param);
void *timeout_param;
OS_KERNEL_INIT();
OS_UNREFERENCE(parameter);
while (1)
{
OS_KERNEL_ENTER();
if (!gs_task_sched_timer_need_sched || OS_NULL == gs_task_sched_timer_head)
{
/* No timer needs handle,suspend myself. */
current_task = k_task_self();
k_readyq_remove(current_task);
current_task->state &= ~OS_TASK_STATE_READY;
current_task->state |= OS_TASK_STATE_SUSPEND;
gs_task_sched_timer_need_sched = OS_FALSE;
/* give up cpu use right, so need sched */
OS_KERNEL_EXIT_SCHED();
/* when return from task, do continue and will return to while start; */
continue;
}
head_timer = gs_task_sched_timer_head;
/* the timeout timer may delete or next timer is not timeout */
if (head_timer->round_ticks > 0)
{
gs_task_sched_timer_need_sched = OS_FALSE;
OS_KERNEL_EXIT();
continue;
}
timeout_func = head_timer->timeout_func;
timeout_param = head_timer->parameter;
head_timer->active_node.flag |= OS_TIMER_FLAG_DO_TIMEOUT;
/* delete timerout timer from single-list, beacuse of timer timeout, so not need to compensate round_ticks */
os_list_del(&head_timer->list);
/* update the gs_task_sched_timer_head*/
gs_task_sched_timer_head = os_list_first_entry_or_null(&gs_task_sched_timer_list, os_timer_t, list);
test_flag = OS_TIMER_FLAG_PERIODIC;
/* if the timer is one shot timer, stop it */
if (test_flag != (head_timer->active_node.flag & test_flag))
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
}
/* if the timer timeout call os_timer_start/os_timer_stop/os_timer_destroy,
* if os_timer_destory called, the head_timer param is not exist
*/
os_list_init(&timeout_timer_list);
os_list_add(&timeout_timer_list, &head_timer->list);
OS_KERNEL_EXIT();
if (OS_NULL != timeout_func)
{
timeout_func(timeout_param);
}
OS_KERNEL_ENTER();
/* the timeout_func may call timer api, for example: os_timer_destroy/os_timer_start/os_timer_stop,
* if os_timer_destroy called, the head_timer will be null
*/
head_timer = os_list_first_entry_or_null(&timeout_timer_list, os_timer_t, list);
if (OS_NULL != head_timer)
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
/* no need to test the timer is one shot or perdoic,
* one shot timer start in timeout func or other task, perdoic timer should re-insert into list
*/
test_flag = OS_TIMER_FLAG_ACTIVATED;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
/* reset the round_tick, then add to the list */
head_timer->round_ticks = head_timer->init_ticks;
#if OS_TIMER_PERIODIC_COMPENSATE
os_tick_t diff;
/* compensate for periodic timer's timeout cb exec time */
test_flag = OS_TIMER_FLAG_PERIODIC;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
diff = OS_TIMER_TICK_DIFF(os_tick_get(), head_timer->active_node.timeout_tick);
if (diff < head_timer->round_ticks)
{
head_timer->round_ticks -= diff;
}
}
#endif
k_single_list_timer_insert_sort_list(head_timer);
}
}
OS_KERNEL_EXIT();
}
}分步详解
第一步:判断是否挂起(条件 !need_sched || head == OS_NULL)
if (!gs_task_sched_timer_need_sched || OS_NULL == gs_task_sched_timer_head)
{
/* No timer needs handle,suspend myself. */
current_task = k_task_self();
k_readyq_remove(current_task);
current_task->state &= ~OS_TASK_STATE_READY;
current_task->state |= OS_TASK_STATE_SUSPEND;
gs_task_sched_timer_need_sched = OS_FALSE;
OS_KERNEL_EXIT_SCHED();
continue;
}- 进入条件:没有到期定时器需要处理(
need_sched == FALSE)或链表为空。定时器任务把自己挂起,让出 CPU。
第二步:判断头定时器是否真正到期(条件 round_ticks > 0)
head_timer = gs_task_sched_timer_head;
/* the timeout timer may delete or next timer is not timeout */
if (head_timer->round_ticks > 0)
{
gs_task_sched_timer_need_sched = OS_FALSE;
OS_KERNEL_EXIT();
continue;
}- 进入条件:头定时器差值仍 > 0,说明尚未到期(可能是被唤醒后重新检查,或漏计补偿后仍有余量),清
need_sched后继续循环,等待下一个 tick。
第三步:到期触发回调(条件 round_ticks == 0)
timeout_func = head_timer->timeout_func;
timeout_param = head_timer->parameter;
head_timer->active_node.flag |= OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
gs_task_sched_timer_head = os_list_first_entry_or_null(&gs_task_sched_timer_list, os_timer_t, list);
test_flag = OS_TIMER_FLAG_PERIODIC;
/* if the timer is one shot timer, stop it */
if (test_flag != (head_timer->active_node.flag & test_flag))
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
}
os_list_init(&timeout_timer_list);
os_list_add(&timeout_timer_list, &head_timer->list);
OS_KERNEL_EXIT();
if (OS_NULL != timeout_func)
{
timeout_func(timeout_param);
}- 取出回调与参数,置
DO_TIMEOUT、从链表删除、更新头指针。 - 单次定时器清除
ACTIVATED。 OS_KERNEL_EXIT()后在任务上下文调用回调(可在回调里做较复杂操作),执行完OS_KERNEL_ENTER()恢复。
第四步:周期重激活 + 补偿(条件回调后定时器仍存在且激活)
head_timer = os_list_first_entry_or_null(&timeout_timer_list, os_timer_t, list);
if (OS_NULL != head_timer)
{
head_timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
os_list_del(&head_timer->list);
test_flag = OS_TIMER_FLAG_ACTIVATED;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
head_timer->round_ticks = head_timer->init_ticks;
#if OS_TIMER_PERIODIC_COMPENSATE
os_tick_t diff;
test_flag = OS_TIMER_FLAG_PERIODIC;
if (test_flag == (head_timer->active_node.flag & test_flag))
{
diff = OS_TIMER_TICK_DIFF(os_tick_get(), head_timer->active_node.timeout_tick);
if (diff < head_timer->round_ticks)
{
head_timer->round_ticks -= diff;
}
}
#endif
k_single_list_timer_insert_sort_list(head_timer);
}
}- 回调返回后,若定时器仍激活(周期或被重启),重置
round_ticks = init_ticks。 - 若启用补偿(
OS_TIMER_PERIODIC_COMPENSATE非零)且是周期定时器,计算回调执行耗时diff = OS_TIMER_TICK_DIFF(os_tick_get(), timeout_tick),若diff < round_ticks则round_ticks -= diff,从而修正周期偏差。 - 重新插入排序链表。
条件走向总结
k_single_list_timer_task_entry() 条件走向总结(定时器任务):
├── 无到期定时器或链表空 → 挂起自己,让出 CPU
├── 头定时器 round_ticks > 0(尚未到期)
│ └── 清 need_sched,继续循环
└── 头定时器 round_ticks == 0(到期)
├── 取出回调、从链表删除、更新头指针
├── 单次定时器 → 清除 ACTIVATED
├── OS_KERNEL_EXIT() 后在任务上下文调用回调
└── 回调返回后:
├── 定时器已销毁 → 跳过
└── 定时器仍激活(周期/被重启)
├── round_ticks = init_ticks
├──(补偿模式)round_ticks -= 回调耗时 diff
└── 重新插入排序链表五、周期性定时器补偿
5.1 问题背景
周期性定时器在每次到期后重新启动。如果回调函数的执行时间较长,可能导致定时器实际触发周期偏离预期周期。
无补偿:
期望: |--10ms--|--10ms--|--10ms--|
实际: |--10ms--|--3ms--|--10ms--|--3ms--| (回调函数执行了 3ms)
累积偏差越来越大
有补偿:
期望: |--10ms--|--10ms--|--10ms--|
实际: |--10ms--|--3ms--|--7ms--|--10ms--| (下个周期补偿了 3ms)
偏差被修正5.2 补偿实现
补偿由 OS_TIMER_PERIODIC_COMPENSATE 宏控制。其核心是 OS_TIMER_TICK_DIFF 宏,用来计算"回调执行期间流逝的 tick 数":
来源: kernel/source/os_timer_slist.c
/* for soft timer, used for periodic timer compensate, if need define it */
#if OS_TIMER_PERIODIC_COMPENSATE
#define OS_TIMER_TICK_DIFF(a, b) (a >= b) ? (a - b) : (OS_UINT32_MAX - b + a + 1)
#endif /* end of OS_TIMER_PERIODIC_COMPENSATE */OS_TIMER_TICK_DIFF(a, b) 计算 a 到 b 的 tick 差(考虑 os_tick 回绕):若 a >= b 直接相减;否则按 32 位回绕计算。
数值演算
| 情形 | 代入 | 结果 |
|---|---|---|
a >= b(未回绕) | a=100, b=97 | 100-97 = 3 |
a < b(os_tick 回绕) | a=2, b=4294967295 | (0xFFFFFFFF - 4294967295) + 2 + 1 = 0+3 = 3 |
补偿逻辑(在 4.3 定时器任务中):周期定时器回调返回后,diff = OS_TIMER_TICK_DIFF(os_tick_get(), timeout_tick) 是回调耗时,若 diff < round_ticks,则 round_ticks -= diff,让下一个周期"缩短"回调耗时,保持长期周期准确。
5.3 配置
OS_TIMER_PERIODIC_COMPENSATE 默认值为 0(不启用)。需在 oneos_config.h 中改为非零值才启用:
#define OS_TIMER_PERIODIC_COMPENSATE 1六、单链表定时器 API 实现
本节给出单链表定时器下对外 API 的完整实现源码(
kernel/source/os_timer_slist.c)与内部辅助函数详解。它们与哈希桶定时器版本的差异点已在本节各小节标注;统一的 API 签名、参数、返回值与使用示例见4.5.4-定时器API.md,本节不再重复。
单链表实现的对外 API(均在 OS_USING_SINGLE_LIST_TIMER 下编译):
| API | 功能 | 本节小节 |
|---|---|---|
os_timer_create | 创建定时器(静态/动态) | 6.1 |
os_timer_destroy | 销毁定时器 | 6.2 |
os_timer_start | 启动定时器 | 6.3 |
os_timer_stop | 停止定时器 | 6.4 |
os_timer_is_soft | 检查是否为软件定时器(仅单链表) | 6.5 |
哈希桶定时器也有同名
os_timer_create等 API,实现见4.5.2的"哈希桶定时器 API 实现"章节。os_timer_set_timeout_ticks/os_timer_get_timeout_ticks/os_timer_get_name/os_timer_is_active/os_timer_is_periodic/os_timer_set_oneshot/os_timer_set_periodic等函数在两实现下逐字相同,仅os_timer.h声明,统一见4.5.4-定时器API.md。
6.1 os_timer_create —— 创建定时器(单链表版)
来源: kernel/source/os_timer_slist.c
os_timer_id os_timer_create(os_timer_dummy_t *timer_cb,
const char *name,
void (*function)(void *parameter),
void *parameter,
os_tick_t timeout,
uint8_t flag)
{
os_timer_t *timer;
OS_KERNEL_INIT();
#ifndef OS_USING_HEAP
OS_ASSERT(OS_NULL != timer_cb);
#endif
OS_ASSERT(OS_FALSE == os_is_irq_active());
if (timer_cb != OS_NULL)
{
timer = (os_timer_t *)timer_cb;
flag = k_single_list_timer_check_flag(flag);
OS_KERNEL_ENTER();
k_single_list_timer_do_init(timer, name, function, parameter, timeout, flag & (~OS_TIMER_FLAG_DYNAMIC));
OS_KERNEL_EXIT();
}
#ifdef OS_USING_HEAP
else
{
OS_ASSERT(OS_FALSE == os_is_irq_active());
timer = (os_timer_t *)OS_KERNEL_MALLOC(sizeof(os_timer_t));
if (OS_NULL == timer)
{
OS_KERN_LOG(KERN_ERROR, TIMER_TAG, "Malloc timer memory failed");
}
else
{
flag = k_single_list_timer_check_flag(flag);
OS_KERNEL_ENTER();
k_single_list_timer_do_init(timer, name, function, parameter, timeout, flag | OS_TIMER_FLAG_DYNAMIC);
OS_KERNEL_EXIT();
}
}
#endif
return OS_TYPE_CONVERT(os_timer_id, timer);
}分步详解
第一步:参数校验(条件 !OS_USING_HEAP 或中断上下文)
OS_KERNEL_INIT();
#ifndef OS_USING_HEAP
OS_ASSERT(OS_NULL != timer_cb);
#endif
OS_ASSERT(OS_FALSE == os_is_irq_active());- 进入条件(无堆):若未启用
OS_USING_HEAP,则timer_cb不能为OS_NULL,否则断言。 - 进入条件(中断):
os_is_irq_active()为真时断言。创建定时器不能在中断上下文调用。
第二步:静态创建分支(条件 timer_cb != OS_NULL)
if (timer_cb != OS_NULL)
{
timer = (os_timer_t *)timer_cb;
flag = k_single_list_timer_check_flag(flag);
OS_KERNEL_ENTER();
k_single_list_timer_do_init(timer, name, function, parameter, timeout, flag & (~OS_TIMER_FLAG_DYNAMIC));
OS_KERNEL_EXIT();
}- 进入条件:用户传入静态控制块地址。
- 关键差异:调用
k_single_list_timer_check_flag(flag)校正默认硬/软标志(只编译硬定时器时强制转硬,只编译软时强制转软,见4.5.3的 2.2 节相关说明)。 - 用
OS_KERNEL_ENTER/EXIT关中断保护,调用k_single_list_timer_do_init(...)(见下方 6.6)初始化,其中flag & (~OS_TIMER_FLAG_DYNAMIC)清除动态标志。 - 单链表版不遍历查重(它创建时只初始化、启动时才入链),也无"全局定时器链表"概念。
第三步:动态创建分支(条件 timer_cb == OS_NULL 且启用堆)
#ifdef OS_USING_HEAP
else
{
OS_ASSERT(OS_FALSE == os_is_irq_active());
timer = (os_timer_t *)OS_KERNEL_MALLOC(sizeof(os_timer_t));
if (OS_NULL == timer)
{
OS_KERN_LOG(KERN_ERROR, TIMER_TAG, "Malloc timer memory failed");
}
else
{
flag = k_single_list_timer_check_flag(flag);
OS_KERNEL_ENTER();
k_single_list_timer_do_init(timer, name, function, parameter, timeout, flag | OS_TIMER_FLAG_DYNAMIC);
OS_KERNEL_EXIT();
}
}
#endif- 进入条件:
timer_cb == OS_NULL且启用了堆。 - 从堆分配
os_timer_t大小控制块;分配失败记录日志并返回OS_NULL。 - 分配成功则校正标志后
k_single_list_timer_do_init(...),flag | OS_TIMER_FLAG_DYNAMIC置动态标志。
第四步:返回定时器 ID(无条件执行)
return OS_TYPE_CONVERT(os_timer_id, timer);- 把
os_timer_t *强转为os_timer_id返回。创建失败时返回OS_NULL。
条件走向总结
os_timer_create()(单链表版)条件走向总结:
├── 无 OS_USING_HEAP 且 timer_cb == OS_NULL → 断言
├── 处于中断上下文 → 断言
├── timer_cb != OS_NULL(静态创建)
│ └── 校正硬/软标志 → k_single_list_timer_do_init(清 DYNAMIC)→ 返回 timer
└── timer_cb == OS_NULL(动态创建,需堆)
├── OS_KERNEL_MALLOC 失败 → 返回 OS_NULL
└── 分配成功 → 校正硬/软标志 → k_single_list_timer_do_init(置 DYNAMIC)→ 返回 timer6.2 os_timer_destroy —— 销毁定时器(单链表版)
来源: kernel/source/os_timer_slist.c
os_err_t os_timer_destroy(os_timer_id timer_id)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_ASSERT(OS_FALSE == os_is_irq_active());
#ifndef OS_USING_HEAP
OS_ASSERT(OS_TIMER_FLAG_STATIC == (timer->active_node.flag & OS_TIMER_FLAG_DYNAMIC));
#endif
OS_KERNEL_ENTER();
/* in timer timeout callback func call os_timer_destroy,
* because the timer is delete from single-list, and add to the timerout list, so just delete form timerout list
*/
if (OS_TIMER_FLAG_DO_TIMEOUT == (timer->active_node.flag & OS_TIMER_FLAG_DO_TIMEOUT))
{
timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
os_list_del(&timer->list);
}
else
{
k_single_list_timer_deactivate(timer);
}
timer->active_node.flag &= ~OS_TIMER_FLAG_INITED;
OS_KERNEL_EXIT();
#ifdef OS_USING_HEAP
if (OS_TIMER_FLAG_DYNAMIC == (timer->active_node.flag & OS_TIMER_FLAG_DYNAMIC))
{
OS_KERNEL_FREE(timer);
}
#endif
return OS_SUCCESS;
}分步详解
第一步:准备与参数校验(条件:入口无条件执行)
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_ASSERT(OS_FALSE == os_is_irq_active());
#ifndef OS_USING_HEAP
OS_ASSERT(OS_TIMER_FLAG_STATIC == (timer->active_node.flag & OS_TIMER_FLAG_DYNAMIC));
#endif- 进入条件:调用
os_timer_destroy()时无条件执行。 OS_ASSERT(OS_NULL != timer)/OS_ASSERT(0 != (flag & INITED)):定时器非空且已初始化。OS_ASSERT(OS_FALSE == os_is_irq_active()):不能在中断上下文销毁。OS_ASSERT(OS_TIMER_FLAG_STATIC == (flag & DYNAMIC))(无堆时):未启用堆时定时器必为静态。- 注意:单链表版没有
os_is_irq_disabled/os_is_schedule_locked两个断言(与哈希桶版不同)。
第二步:判断是否在回调中销毁(条件 flag & DO_TIMEOUT)
OS_KERNEL_ENTER();
/* in timer timeout callback func call os_timer_destroy,
* because the timer is delete from single-list, and add to the timerout list, so just delete form timerout list
*/
if (OS_TIMER_FLAG_DO_TIMEOUT == (timer->active_node.flag & OS_TIMER_FLAG_DO_TIMEOUT))
{
timer->active_node.flag &= ~OS_TIMER_FLAG_DO_TIMEOUT;
timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
os_list_del(&timer->list);
}
else
{
k_single_list_timer_deactivate(timer);
}
timer->active_node.flag &= ~OS_TIMER_FLAG_INITED;
OS_KERNEL_EXIT();- 进入条件 A(回调中销毁):
flag & DO_TIMEOUT != 0,说明定时器正从排序链表摘除、挂在 timeout 列表等待回调。此时只需清DO_TIMEOUT与ACTIVATED,并从 timeout 列表删除(它已不在排序链表上)。 - 进入条件 B(普通销毁):未在回调中,调用
k_single_list_timer_deactivate(timer)停掉活跃计时并摘除排序链表节点(见下方 6.7)。 - 无论哪条分支,最后都清
OS_TIMER_FLAG_INITED,标志该对象失效。
为什么必须区分
DO_TIMEOUT? 单链表定时器在回调执行期间会被临时摘出排序链表(见 4.3/4.4),若此时直接deactivate去排序链表找它,会找不到或操作无效节点。DO_TIMEOUT标记了这个特殊状态,保证在回调中销毁自己是安全的。
第三步:释放动态内存(条件 OS_USING_HEAP 且为动态分配)
#ifdef OS_USING_HEAP
if (OS_TIMER_FLAG_DYNAMIC == (timer->active_node.flag & OS_TIMER_FLAG_DYNAMIC))
{
OS_KERNEL_FREE(timer);
}
#endif
return OS_SUCCESS;- 进入条件:启用了堆,且该定时器为动态创建(
OS_TIMER_FLAG_DYNAMIC == 0x10)。 - 满足则
OS_KERNEL_FREE(timer)释放堆内存;静态创建的不释放。返回OS_SUCCESS。
条件走向总结
os_timer_destroy()(单链表版)条件走向总结:
├── 参数校验失败(timer 为空 / 未初始化 / 在中断 / 无堆却动态)→ 断言
├── 进入临界区
│ ├── flag & DO_TIMEOUT != 0(回调中销毁)
│ │ └── 清 DO_TIMEOUT / ACTIVATED,从 timeout 列表删除
│ └── 否则 → k_single_list_timer_deactivate(停计时、摘排序链表)
│ └── 清 INITED
├── 若启用堆 且 flag&DYNAMIC != 0(动态创建)→ OS_KERNEL_FREE(timer)
└── 返回 OS_SUCCESS6.3 os_timer_start —— 启动定时器(单链表版)
来源: kernel/source/os_timer_slist.c
os_err_t os_timer_start(os_timer_id timer_id)
{
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
/* First deactivate timer, then start it. */
k_single_list_timer_deactivate(timer);
k_single_list_timer_activate(timer);
OS_KERNEL_EXIT();
return OS_SUCCESS;
}分步详解
第一步:准备与参数校验(条件:入口无条件执行)
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));- 进入条件:调用
os_timer_start()时无条件执行。 OS_ASSERT(OS_NULL != timer)/OS_ASSERT(0 != (flag & INITED)):定时器非空且已初始化。
第二步:先停后启(条件:已初始化)
OS_KERNEL_ENTER();
/* First deactivate timer, then start it. */
k_single_list_timer_deactivate(timer);
k_single_list_timer_activate(timer);
OS_KERNEL_EXIT();
return OS_SUCCESS;- 进入条件:参数校验通过后无条件执行。
- 先停:
k_single_list_timer_deactivate(timer)—— 若曾激活则从排序链表摘除。 - 后启:
k_single_list_timer_activate(timer)—— 置ACTIVATED、重置round_ticks = init_ticks并插入排序链表(见下方 6.8)。两辅助函数在回调中调用时都通过DO_TIMEOUT判断避免误操作。
为什么"先停后启"? 与哈希桶同理,重复
os_timer_start需从初始超时重新计时,先停保证不会重复插入排序链表。
条件走向总结
os_timer_start()(单链表版)条件走向总结:
├── timer 为空 / 未初始化 → 断言
├── 进入临界区
│ ├── k_single_list_timer_deactivate:若曾激活则摘排序链表(否则空操作)
│ └── k_single_list_timer_activate:置 ACTIVATED、重置 round_ticks、插入排序链表
└── 返回 OS_SUCCESS6.4 os_timer_stop —— 停止定时器(单链表版)
来源: kernel/source/os_timer_slist.c
os_err_t os_timer_stop(os_timer_id timer_id)
{
os_err_t ret;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
ret = OS_SUCCESS;
if (!(timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED))
{
ret = OS_FAILURE;
}
else
{
k_single_list_timer_deactivate(timer);
}
OS_KERNEL_EXIT();
return ret;
}分步详解
第一步:准备与参数校验(条件:入口无条件执行)
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));- 进入条件:调用
os_timer_stop()时无条件执行。 OS_ASSERT(OS_NULL != timer)/OS_ASSERT(0 != (flag & INITED)):定时器非空且已初始化。
第二步:判断是否在运行(条件 flag & ACTIVATED)
OS_KERNEL_ENTER();
ret = OS_SUCCESS;
if (!(timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED))
{
ret = OS_FAILURE;
}
else
{
k_single_list_timer_deactivate(timer);
}
OS_KERNEL_EXIT();
return ret;- 进入条件 A(未激活):
flag & ACTIVATED == 0,定时器本就没在运行,返回OS_FAILURE。 - 进入条件 B(激活中):
flag & ACTIVATED != 0,临界区内k_single_list_timer_deactivate(timer)停止并摘除排序链表节点,返回OS_SUCCESS。
条件走向总结
os_timer_stop()(单链表版)条件走向总结:
├── timer 为空 / 未初始化 → 断言
├── flag & ACTIVATED == 0(未在运行)→ ret = OS_FAILURE → 返回 OS_FAILURE
└── flag & ACTIVATED != 0(在运行)→ k_single_list_timer_deactivate → ret = OS_SUCCESS → 返回 OS_SUCCESS6.5 os_timer_is_soft —— 检查是否为软件定时器(单链表版)
来源: kernel/source/os_timer_slist.c
os_bool_t os_timer_is_soft(os_timer_id timer_id)
{
os_bool_t is_soft;
os_timer_t *timer;
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));
OS_KERNEL_ENTER();
is_soft = (timer->active_node.flag & OS_TIMER_FLAG_TASK_SCHED) ? OS_TRUE : OS_FALSE;
OS_KERNEL_EXIT();
return is_soft;
}分步详解
第一步:准备与参数校验(条件:入口无条件执行)
OS_KERNEL_INIT();
timer = OS_TYPE_CONVERT(os_timer_t *, timer_id);
OS_ASSERT(OS_NULL != timer);
OS_ASSERT(0 != (timer->active_node.flag & OS_TIMER_FLAG_INITED));- 进入条件:调用
os_timer_is_soft()时无条件执行。 OS_ASSERT(OS_NULL != timer)/OS_ASSERT(0 != (flag & INITED)):定时器非空且已初始化。
第二步:读取软/硬标志(条件:已初始化)
OS_KERNEL_ENTER();
is_soft = (timer->active_node.flag & OS_TIMER_FLAG_TASK_SCHED) ? OS_TRUE : OS_FALSE;
OS_KERNEL_EXIT();
return is_soft;- 进入条件:参数校验通过后无条件执行。
- 临界区内判断
flag & OS_TIMER_FLAG_TASK_SCHED(即0x04)是否非 0:非 0 → 软件定时器(OS_TRUE);0 → 硬件定时器(OS_FALSE)。
注意:
os_timer_is_soft只在OS_USING_SINGLE_LIST_TIMER下声明。哈希桶定时器只有软件定时器,无此区分(源码注释:"Hash timers do not have hardware timers")。
6.6 核心辅助函数 k_single_list_timer_do_init —— 定时器字段初始化
一句话定位:k_single_list_timer_do_init 在 os_timer_create 的静态/动态两分支中都被调用,负责初始化定时器控制块的字段,不持锁(由调用方 OS_KERNEL_ENTER/EXIT 保证临界区)。
来源: kernel/source/os_timer_slist.c
static void k_single_list_timer_do_init(os_timer_t *timer,
const char *name,
void (*function)(void *parameter),
void *parameter,
uint32_t timeout,
uint8_t flag)
{
timer->active_node.flag = (flag & (~OS_TIMER_FLAG_ACTIVATED)) | OS_TIMER_FLAG_INITED;
timer->timeout_func = function;
timer->parameter = parameter;
timer->init_ticks = ((0 == timeout) ? 1 : timeout);
timer->round_ticks = timer->init_ticks;
if (OS_NULL != name)
{
strncpy(&timer->name[0], name, OS_NAME_MAX);
timer->name[OS_NAME_MAX] = '\0';
}
else
{
timer->name[0] = '\0';
}
os_list_init(&timer->list);
}逐行解释:
| 代码 | 作用 |
|---|---|
flag = (flag & ~ACTIVATED) | INITED | 清"激活"位,置"已初始化"位 |
timeout_func = function / parameter = parameter | 保存回调与参数 |
init_ticks = (0 == timeout) ? 1 : timeout | 超时 tick,0 会被当作 1 |
round_ticks = init_ticks | 初始剩余等于初始超时 |
strncpy / name[OS_NAME_MAX] = '\0' | 复制名称并确保 \0 结尾 |
os_list_init(&timer->list) | 初始化排序链表节点 |
与哈希桶版
_k_timer_do_init的差异:单链表版不设置index字段(无桶索引),且初始化的是timer->list节点(排序链表)而非active_node.active_list。
6.7 核心辅助函数 k_single_list_timer_deactivate —— 停掉活跃计时
一句话定位:k_single_list_timer_deactivate 被 os_timer_stop、os_timer_start(先停)、os_timer_destroy 调用,负责停止一个激活中的定时器:清激活标志,若非回调中则从排序链表摘除。
来源: kernel/source/os_timer_slist.c
static void k_single_list_timer_deactivate(os_timer_t *timer)
{
uint8_t test_flag;
if (timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED)
{
timer->active_node.flag &= ~OS_TIMER_FLAG_ACTIVATED;
/* in timer timeout callback call this func, one shot timer is already deactive,
* peridoc timer not in sort list, so not need to del */
test_flag = OS_TIMER_FLAG_DO_TIMEOUT;
if (test_flag != (timer->active_node.flag & test_flag))
{
k_single_list_timer_del_sort_list(timer);
}
}
return;
}逐行解释:
| 代码 | 作用 |
|---|---|
if ((flag & ACTIVATED) != 0) | 仅在激活中才执行;否则空操作 |
flag &= ~ACTIVATED | 清激活标志 |
if (test_flag != (flag & DO_TIMEOUT)) | 若非回调中(未置 DO_TIMEOUT)才摘排序链表;若在回调中,定时器已不在排序链表上,直接跳过 |
k_single_list_timer_del_sort_list的完整源码与详解见上文 3.4 节。
6.8 核心辅助函数 k_single_list_timer_activate —— 激活并插入排序链表
一句话定位:k_single_list_timer_activate 被 os_timer_start 调用,负责置激活标志、重置 round_ticks 为 init_ticks,并将定时器按 round_ticks 升序插入排序链表(若非回调中)。
来源: kernel/source/os_timer_slist.c
static void k_single_list_timer_activate(os_timer_t *timer)
{
uint8_t test_flag;
if (OS_TIMER_FLAG_ACTIVATED != (timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED))
{
timer->active_node.flag |= OS_TIMER_FLAG_ACTIVATED;
/* in timer timeout callback call this func, just modify flag, enter out the call back will add in sort list */
test_flag = OS_TIMER_FLAG_DO_TIMEOUT;
if (test_flag != (timer->active_node.flag & test_flag))
{
timer->round_ticks = timer->init_ticks;
k_single_list_timer_insert_sort_list(timer);
}
}
return;
}逐行解释:
| 代码 | 作用 |
|---|---|
if (ACTIVATED != (flag & ACTIVATED)) | 仅未激活时激活,避免重复激活 |
| `flag | = ACTIVATED` |
if (test_flag != (flag & DO_TIMEOUT)) | 若非回调中,重置 round_ticks = init_ticks 并插入排序链表;若在回调中,仅置标志,回调退出后由 tick 处理流程重新入链 |
k_single_list_timer_insert_sort_list的完整源码与详解见上文 3.3 节。
6.9 核心辅助函数 k_single_list_timer_calc_remain_ticks —— 计算剩余 tick
一句话定位:k_single_list_timer_calc_remain_ticks 被 os_timer_get_remain_ticks 调用,按排序链表顺序累加 round_ticks,直到遇到该定时器为止,得到其剩余绝对 tick 数。
来源: kernel/source/os_timer_slist.c
/* if the timer not in the list(periodic timer in timeout cb) or timer not actived, the remian_tick will be 0 */
static os_tick_t k_single_list_timer_calc_remain_ticks(os_timer_t *timer)
{
os_list_node_t *timer_head_list;
os_tick_t remain_tick;
os_timer_t *iter;
remain_tick = 0;
timer_head_list = k_single_list_get_timer_head_list(timer);
if (timer->active_node.flag & OS_TIMER_FLAG_ACTIVATED)
{
os_list_for_each_entry(iter, timer_head_list, os_timer_t, list)
{
remain_tick += iter->round_ticks;
if (iter == timer)
{
break;
}
}
}
return remain_tick;
}数值演算:沿用 3.2 节基准数据——链表为 A(2) → B(3) → D(1) → C(2),查询 D 的剩余时间:
| 计算项 | 代入计算 | 结果 |
|---|---|---|
remain_tick(遍历 A) | 0 + A.round_ticks(2) | 2 |
remain_tick(遍历 B) | 2 + B.round_ticks(3) | 5 |
remain_tick(遍历 D) | 5 + D.round_ticks(1) | 6,iter == D → break |
| 最终结果 | 6(D 距真正超时还差 6 tick,正确) |
逐行解释:
| 代码 | 作用 |
|---|---|
timer_head_list = k_single_list_get_timer_head_list(timer) | 获取该定时器所属排序链表(硬/软) |
if ((flag & ACTIVATED) != 0) | 仅激活中的定时器计算;否则返回 0 |
os_list_for_each_entry(...) 累加 | 从头累加 round_ticks,直到找到该定时器 |
break | 找到即停止,remain_tick 即为绝对剩余 tick |
与哈希桶版
_k_timer_calc_remain_ticks的差异:单链表按链表顺序累加差值(O(n)),哈希桶用桶索引 + 补偿计算。
6.10 单链表版对外 API 特点小结
| API | 与哈希桶版的差异点 |
|---|---|
os_timer_create | 调 k_single_list_timer_check_flag 校正硬/软标志;用 OS_KERNEL_ENTER/EXIT 关中断;无静态查重 |
os_timer_destroy | 用 DO_TIMEOUT 判断"回调中销毁";动态标志位为 0x10 |
os_timer_start | 先 k_single_list_timer_deactivate 再 k_single_list_timer_activate |
os_timer_stop | 未激活返回 OS_FAILURE,激活则 k_single_list_timer_deactivate |
os_timer_is_soft | 仅单链表有,哈希桶无硬件定时器 |
七、配置选项
单链表定时器的启用与参数通过以下宏控制(定义在 oneos_config.h 中):
| 宏 | 类型 | 默认值 | 作用 |
|---|---|---|---|
OS_USING_KERNEL_TIMER | bool | - | 必须启用,内核定时器总开关 |
OS_USING_SINGLE_LIST_TIMER | bool | 可选方案 | 选择单链表定时器实现 |
OS_HARD_SINGLE_LIST_TIMER | bool | n | 启用硬件定时器(tick 调度) |
OS_SOFT_SINGLE_LIST_TIMER | bool | y | 启用软件定时器(任务调度) |
OS_TIMER_TASK_STACK_SIZE | int | 512 | 软件定时器任务栈大小(字节) |
OS_TIMER_PERIODIC_COMPENSATE | int | 0 | 周期性定时器补偿(非零启用) |
注意:
OS_HARD_SINGLE_LIST_TIMER与OS_SOFT_SINGLE_LIST_TIMER可同时启用,也可只启用其一;若两者都未定义,源码会触发#error "Please select timer of hard or soft"。
八、与哈希桶定时器的对比
| 特性 | 哈希桶定时器 | 单链表定时器 |
|---|---|---|
| 插入时间复杂度 | O(1) | O(n) |
| tick 处理复杂度 | 检查位掩码定位桶 | 递减链表头部 |
| 定时器数量 | 适合大量(> 50) | 适合少量(< 50) |
| 硬件定时器 | 不支持 | 支持 |
| 软件定时器 | 支持 | 支持 |
| 内存开销 | 桶数组固定开销 | 仅链表节点开销 |
| 确定性 | 插入时间确定 | 插入时间取决于定时器数量 |
| 低功耗 Tickless | 需要排序支持 | 天然支持 |
| 配置宏 | OS_USING_HASH_BUCKET_TIMER | OS_USING_SINGLE_LIST_TIMER |
选择建议
- 选择哈希桶定时器:定时器数量多(> 50)、频繁创建销毁、不需要硬件定时器
- 选择单链表定时器:定时器数量少(< 50)、需要硬件定时器的高实时性场景、需要周期性补偿
📝 本节小结
核心要点回顾
| 要点 | 说明 |
|---|---|
| 排序链表 + 相对 tick | 链表按 round_ticks 升序,每个节点的 round_ticks 是相对前一个的差值,头节点差值 = 距真正超时还差的 tick 数 |
| 插入 O(n)、tick 处理 O(1) | 插入需遍历找位置;但每次 tick 只需处理链表头节点 |
| 硬件定时器 | 回调在 tick ISR 中执行(k_single_list_timer_tick_proc),延迟最低、限制最严 |
| 软件定时器 | 回调在定时器任务中执行(k_single_list_timer_task_entry),灵活度高 |
| 周期性补偿 | OS_TIMER_TICK_DIFF 计算回调耗时,round_ticks -= diff 修正周期偏差 |
| 硬软独立链表 | gs_tick_sched_timer_list 与 gs_task_sched_timer_list 各自独立 |
关键易错点速查
| 易错点 | 正确做法 |
|---|---|
| 硬定时器回调里调用阻塞 API | 硬定时器回调在 tick ISR 中执行,绝不能阻塞/延时,否则卡死系统 |
误以为每个节点的 round_ticks 是绝对剩余 | 它是相对前一个的差值,绝对剩余 = 之前所有节点差值之和 |
| 删除中间节点后忘记调整差值 | 必须把被删节点差值加给它下一个节点(源码 _del_sort_list 处理) |
| 未启用对应宏就使用定时器 | 若既无 OS_HARD_SINGLE_LIST_TIMER 也无 OS_SOFT_SINGLE_LIST_TIMER,编译报错 |
| 需要周期性补偿却未改宏 | OS_TIMER_PERIODIC_COMPENSATE 默认 0,需显式设非零才生效 |
| 定时器数量很多仍用单链表 | 插入 O(n),数量大时用哈希桶定时器更合适 |
下一步
- 4.5.4 定时器 API:掌握定时器的创建、启动、停止、查询等用户 API 的完整用法与返回值检查。