RT-Thread 的时钟管理

时钟管理

时间是操作系统中至关重要的概念。操作系统需要通过时间来规范任务的执行,例如线程的延时、线程的时间片轮转调度以及定时器超时等。本章将介绍 RT-Thread 的时钟节拍和基于时钟节拍的定时器,阐述时钟节拍的产生原理,并指导如何使用 RT-Thread 的定时器。

时钟节拍

时钟节拍是操作系统中最小的时间单位,它是一种周期性的硬件中断。该中断可以被视为系统的心跳,中断之间的时间间隔取决于具体的应用需求,通常在 1 ms 到 100 ms 之间。时钟节拍率越高,系统的实时响应越快,但系统的开销也会相应增加。从系统启动开始计数的时钟节拍数称为系统时间。

在 RT-Thread 中,时钟节拍的长度可以通过宏定义 RT_TICK_PER_SECOND 来调整,其值等于 1 / RT_TICK_PER_SECOND 秒。

时钟节拍的实现方式

时钟节拍通常由配置为中断触发模式的硬件定时器产生。当硬件定时器产生中断时,系统会调用 void rt_tick_increase(void) 函数,通知操作系统已经过去了一个系统时钟节拍。不同的硬件定时器中断实现方式各不相同,以下代码以 STM32 定时器为例:

void SysTick_Handler(void)
{
   
    /* 进入中断 */
    rt_interrupt_enter();
    
    rt_tick_increase();
    
    /* 退出中断 */
    rt_interrupt_leave();
}

在中断函数中,rt_tick_increase() 函数用于对全局变量 rt_tick 进行自增,其具体代码如下:

void rt_tick_increase(void)
{
   
    struct rt_thread *thread;

    /* 全局变量 rt_tick 自加 */
    ++ rt_tick;

    /* 检查时间片 */
    thread = rt_thread_self();

    -- thread->remaining_tick;
    if (thread->remaining_tick == 0)
    {
   
        /* 重新赋初值 */
        thread->remaining_tick = thread->init_tick;

        /* 线程让出处理器 */
        rt_thread_yield();
    }

    /* 检查定时器 */
    rt_timer_check();
}

全局变量 rt_tick 会在每个时钟节拍到来时自增 1,rt_tick 的值表示系统从启动以来总共经过的时钟节拍数,即系统时间。此外,在每个时钟节拍到来时,系统还会检查当前线程的时间片是否用完以及是否有定时器超时。

注意:

rt_timer_check() 函数在中断中被调用,用于检查系统定时器链表,如果发现有定时器超时,则会调用相应的超时函数。所有定时器在超时后都会被从定时器链表中移除,而周期性定时器会在再次启动时被加入定时器链表。

获取时钟节拍

全局变量 rt_tick 记录了系统从启动开始经过的总节拍数。可以使用 rt_tick_get() 函数获取当前 rt_tick 的值,从而获取当前的时钟节拍值。该接口常用于记录系统运行时间或测量任务的执行时间。函数接口如下:

rt_tick_t rt_tick_get(void);

rt_tick_get() 函数的返回值说明如下:

返回值 描述
rt_tick 当前时钟节拍值。

定时器管理

定时器是指从指定的时刻开始,经过一定的时间后触发一个事件的机制。定时器分为硬件定时器和软件定时器:

  1. 硬件定时器: 由芯片本身提供的定时功能,通常由外部晶振提供时钟输入。芯片提供一组配置寄存器,用于接受控制输入。当达到设定的时间值时,芯片的中断控制器会产生时钟中断。硬件定时器的精度通常很高,可达纳秒级别,且采用中断触发方式。

  2. 软件定时器: 由操作系统提供的系统接口,它基于硬件定时器实现,可以提供不受数目限制的定时器服务。

RT-Thread 提供了基于软件实现的定时器,定时单位为时钟节拍 (OS Tick) 的整数倍。例如,如果一个 OS Tick 是 10ms,那么定时器的时间只能是 10ms、20ms、100ms 等,而不能是 15ms。

RT-Thread 定时器介绍

RT-Thread 的定时器提供两种触发方式:

  • 单次触发定时器: 启动后只会触发一次定时器事件,然后自动停止。

  • 周期触发定时器: 会周期性地触发定时器事件,除非用户手动停止,否则将一直持续执行。

根据定时器超时函数执行时所处的上下文环境,RT-Thread 的定时器可以分为 HARD_TIMER 模式和 SOFT_TIMER 模式:

定时器上下文环境

HAER_TIMER模式

HARD_TIMER 模式的定时器超时函数在中断上下文中执行。可以通过在初始化/创建定时器时使用 RT_TIMER_FLAG_HARD_TIMER 参数来指定。

在中断上下文中执行时,超时函数的要求与中断服务例程的要求相同:执行时间应尽量短,不应导致当前上下文挂起或等待。例如,在中断上下文中执行的超时函数不应尝试申请或释放动态内存等。

RT-Thread 定时器默认采用 HARD_TIMER 模式。

SOFT_TIMER模式

SOFT_TIMER 模式可通过宏定义 RT_USING_TIMER_SOFT 来使能。当该模式启用后,系统会在初始化时创建一个 timer 线程,并且所有 SOFT_TIMER 模式的定时器超时函数都会在 timer 线程的上下文中执行。可以通过在初始化/创建定时器时使用 RT_TIMER_FLAG_SOFT_TIMER 参数来指定。

定时器工作机制

RT-Thread 定时器模块维护两个重要的全局变量:

  1. rt_tick:当前系统经过的 tick 时间,每当硬件定时器中断发生时,该值会加 1。
  2. rt_timer_list:定时器链表,系统新创建并激活的定时器会按照超时时间排序,插入到 rt_timer_list 链表中。

定时器链表示意图

例如,当系统当前的 rt_tick 值为 20,并且系统中已创建并启动了三个定时器,分别为:定时 50 个 tick 的 Timer1,100 个 tick 的 Timer2 和 500 个 tick 的 Timer3。这三个定时器分别加上系统当前时间 rt_tick=20 后,将按照超时时间从小到大的顺序链接到 rt_timer_list 链表中。

rt_tick 随着硬件定时器的触发持续增长时,当 rt_tick 从 20 增长到 70 时,会触发 Timer1 的超时函数,同时将 Timer1rt_timer_list 链表中删除。同理,在经过 100 个 tick 和 500 个 tick 后,会分别触发 Timer2Timer3 的超时函数,并将其从链表中删除。

如果在 rt_tick = 30 时,有任务新创建了一个 tick 值为 300 的 Timer4 定时器,由于 Timer4 的超时时间为 rt_tick + 300 = 330,因此它将被插入到 Timer2Timer3 之间。

定时器链表插入示意图

定时器控制块

RT-Thread 中,定时器控制块由结构体 struct rt_timer 定义,形成定时器内核对象,并链接到内核对象容器中进行管理。它是操作系统用于管理定时器的数据结构,存储定时器的相关信息,例如初始节拍数、超时时的节拍数、定时器之间的链表结构以及超时回调函数等。定义如下:

struct rt_timer
{
   
    struct rt_object parent;
    rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL];  /* 定时器链表节点 */

    void (*timeout_func)(void *parameter);    /* 定时器超时调用的函数 */
    void      *parameter;                         /* 超时函数的参数 */
    rt_tick_t init_tick;                         /* 定时器初始超时节拍数 */
    rt_tick_t timeout_tick;                     /* 定时器实际超时时的节拍数 */
};
typedef struct rt_timer *rt_timer_t;

其中,list 成员用于将激活的定时器链接到 rt_timer_list 链表中。

定时器跳表算法(Skip List)算法

如前所述,系统新创建并激活的定时器会按照超时时间排序,插入到 rt_timer_list 链表中。rt_timer_list 是一个有序链表。RT-Thread 使用跳表算法来加速链表元素的搜索。

跳表是一种基于并联链表的数据结构,实现简单,插入、删除、查找的时间复杂度均为 O(log n)。跳表在链表的基础上增加了“跳跃”功能,从而实现快速查找。

有序链表示意图

例如,在一个有序链表中搜索元素 {13, 39},需要比较的次数分别是 {3, 5},总共比较 8 次。

有序链表索引示意图

使用跳表算法后,可以提取部分节点作为索引。例如,提取 {3, 18, 77} 作为一级索引。这样搜索元素 39 时仅需比较 3 次。可以继续提取一级索引的元素作为二级索引,进一步加快搜索速度。

三层跳表示意图

跳表通过上层索引,减少搜索时的比较次数,提高查找效率,这是一种以空间换取时间的算法。RT-Thread 中通过宏定义 RT_TIMER_SKIP_LIST_LEVEL 配置跳表的层数,默认为 1,表示采用一级有序链表算法。每增加 1,表示在原链表基础上增加一级索引。

定时器的管理方式

本节将介绍 RT-Thread 定时器的相关接口,帮助读者在代码层面理解定时器的工作原理。

系统启动时需要初始化定时器管理系统。可以通过以下函数接口完成:

void rt_system_timer_init(void);

如果需要使用 SOFT_TIMER 模式,则需要在系统初始化时调用以下函数:

void rt_system_timer_thr
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值