文章目录
操作系统要想协调好各线程的调度管理,离不开时间与空间的管理:时间管理主要靠时钟节拍systick实现,时钟节拍是特定的周期性中断,这个中断可以看做是系统心跳(心跳频率一般设为10-1000HZ,时钟节拍率越快,系统额外开销越大),供系统处理所有和时间有关的事件,如线程的延时、线程的时间片轮转调度以及定时器超时等;空间管理主要是对可用存储空间的管理,一般MCU的存储空间可分为片内RAM与片内FLASH / ROM两种,片内RAM可由内存管理器动态分配/释放内部缓存空间(如Linux虚拟内存管理系统),片内FLASH可由文件系统进行分区分目录以文件为单位管理数据存储空间。
一、定时器对象管理
1.1 时钟节拍
时钟节拍定时器systick的配置和SysTick_Handler中断处理函数在前一篇CPU架构与BSP移植时已经简单介绍过了,下面再看看SysTick_Handler调用的rt_tick_increase函数代码:
// rt-thread-4.0.1\src\clock.c
static rt_tick_t rt_tick = 0;
/**
* This function will notify kernel there is one tick passed. Normally,
* this function is invoked by clock ISR.
*/
void rt_tick_increase(void)
{
struct rt_thread *thread;
/* increase the global tick */
#ifdef RT_USING_SMP
rt_cpu_self()->tick ++;
#else
++ rt_tick;
#endif
/* check time slice */
thread = rt_thread_self();
-- thread->remaining_tick;
if (thread->remaining_tick == 0)
{
/* change to initialized tick */
thread->remaining_tick = thread->init_tick;
/* yield */
rt_thread_yield();
}
/* check timer */
rt_timer_check();
}
/**
* This function will return current tick from operating system startup
* @return current tick
*/
rt_tick_t rt_tick_get(void)
{
/* return the global tick */
return rt_tick;
}
RTM_EXPORT(rt_tick_get);
/**
* This function will set current tick
*/
void rt_tick_set(rt_tick_t tick)
{
rt_base_t level;
level = rt_hw_interrupt_disable();
rt_tick = tick;
rt_hw_interrupt_enable(level);
}
/**
* This function will calculate the tick from millisecond.
* @param ms the specified millisecond
* - Negative Number wait forever
* - Zero not wait
* - Max 0x7fffffff
* @return the calculated tick
*/
rt_tick_t rt_tick_from_millisecond(rt_int32_t ms)
{
rt_tick_t tick;
if (ms < 0)
{
tick = (rt_tick_t)RT_WAITING_FOREVER;
}
else
{
tick = RT_TICK_PER_SECOND * (ms / 1000);
tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000;
}
/* return the calculated tick */
return tick;
}
RTM_EXPORT(rt_tick_from_millisecond);
时钟节拍每次中断除了系统节拍全局变量rt_tick(rt_tick 的值表示了系统从启动开始总共经过的时钟节拍数,即系统时间)自增外,还调用了软件定时器检查函数rt_timer_check()来检查软件定时器链表中是否有定时器超时,若有则执行该定时器的超时函数,rt_timer_check()代码如下:
// rt-thread-4.0.1\src\timer.c
/**
* This function will check timer list, if a timeout event happens, the
* corresponding timeout function will be invoked.
*
* @note this function shall be invoked in operating system timer interrupt.
*/
void rt_timer_check(void)
{
struct rt_timer *t;
rt_tick_t current_tick;
register rt_base_t level;
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n"));
current_tick = rt_tick_get();
/* disable interrupt */
level = rt_hw_interrupt_disable();
while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1]))
{
t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next,
struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]);
/*
* It supposes that the new tick shall less than the half duration of
* tick max.
*/
if ((current_tick - t->timeout_tick) < RT_TICK_MAX / 2)
{
RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t));
/* remove timer from timer list firstly */
_rt_timer_remove(t);
/* call timeout function */
t->timeout_func(t->parameter);
/* re-get tick */
current_tick = rt_tick_get();
RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t));
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("current tick: %d\n", current_tick));
if ((t->parent.flag & RT_TIMER_FLAG_PERIODIC) &&
(t->parent.flag & RT_TIMER_FLAG_ACTIVATED))
{
/* start it */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
rt_timer_start(t);
}
else
{
/* stop timer */
t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED;
}
}
else
break;
}
/* enable interrupt */
rt_hw_interrupt_enable(level);
RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check leave\n"));
}
rt_inline void _rt_timer_remove(rt_timer_t timer)
{
int i;
for (i = 0; i < RT_TIMER_SKIP_LIST_LEVEL; i++)
{
rt_list_remove(&timer->row[i]);
}
}
中断中的 rt_timer_check() 用于检查系统硬件定时器链表,如果有定时器超时,将调用相应的超时函数。且所有定时器在定时超时后都会从定时器链表中被移除,而周期性定时器会在它再次启动时被加入定时器链表。了解了软件定时器的超时检查过程和超时函数调用点,下面再看软件定时器的数据结构和接口函数。
1.2 定时器对象管理
定时器,是指从指定的时刻开始,经过一定的指定时间后触发一个事件,例如定个时间提醒第二天能够按时起床。定时器有硬件定时器和软件定时器之分:
- 硬件定时器:是芯片本身提供的定时功能。一般是由外部晶振提供给芯片输入时钟,芯片向软件模块提供一组配置寄存器,接受控制输入,到达设定时间值后芯片中断控制器产生时钟中断。硬件定时器的精度一般很高,可以达到纳秒级别,并且是中断触发方式;
- 软件定时器:是由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务。
RT-Thread 的定时器提供两类定时器机制:第一类是单次触发定时器,这类定时器在启动后只会触发一次定时器事件,然后定时器自动停止。第二类是周期触发定时器,这类定时器会周期性的触发定时器事件,直到用户手动的停止,否则将永远持续执行下去。
另外,根据超时函数执行时所处的上下文环境,RT-Thread 的定时器可以分为 HARD_TIMER 模式与 SOFT_TIMER 模式,如下图:
- HARD_TIMER 模式:定时器超时函数在中断上下文环境中执行(执行时间应该尽量短,执行时不应导致当前上下文挂起、等待),可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_HARD_TIMER 来指定,也是RT-Thread 定时器默认的方式;
- SOFT_TIMER 模式:通过宏定义 RT_USING_TIMER_SOFT 来决定是否启用该模式。该模式被启用后,系统会在初始化时创建一个 timer 线程,然后 SOFT_TIMER 模式的定时器超时函数在都会在 timer 线程的上下文环境中执行。可以在初始化 / 创建定时器时使用参数 RT_TIMER_FLAG_SOFT_TIMER 来指定设置 SOFT_TIMER 模式。
在 RT-Thread 操作系统中,定时器控制块由结构体 struct rt_timer 定义并形成定时器内核对象,再链接到内核对象容器中进行管理。它是操作系统用于管理定时器的一个数据结构,会存储定时器的一些信息,例如初始节拍数,超时时的节拍数,也包含定时器与定时器之间连接用的链表结构,超时回调函数等。rt_timer的数据结构定义如下:
// rt-thread-4.0.1\include\rtdef.h
/**
* timer structure
*/
struct rt_timer
{
struct rt_object parent; /**< inherit from rt_object */
rt_list_t row[RT_TIMER_SKIP_LIST_LEVEL];
void (*timeout_func)(void *parameter); /**< timeout function */
void *parameter; /**< timeout function's parameter */
rt_tick_t init_tick; /**< timer timeout tick */
rt_tick_t timeout_tick; /**< timeout tick */
};
typedef struct rt_timer *rt_timer_t;
/**
* clock & timer macros
*/
#define RT_TIMER_FLAG_DEACTIVATED 0x0 /**< timer is deactive */
#define RT_TIMER_FLAG_ACTIVATED 0x1 /**< timer is active */
#define RT_TIMER_FLAG_ONE_SHOT 0x0 /**< one shot timer */
#define RT_TIMER_FLAG_PERIODIC 0x2 /**< periodic timer */
#define RT_TIMER_FLAG_HARD_TIMER 0x0 /**< hard timer,the timer's callback function will be called in tick isr. */
#define RT_TIMER_FLAG_SOFT_TIMER 0x4 /**< soft timer,the timer's callback function will be called in timer thread. */
#ifndef RT_TIMER_SKIP_LIST_LEVEL
#define RT_TIMER_SKIP_LIST_LEVEL 1
#endif
// rt-thread-4.0.1\src\timer.c
/* hard timer list */
static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
/* soft timer list */
static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL];
结构体rt_timer的第一个成员即是前面介绍过的抽象内核对象结构rt_object,此时rt_timer.parent.type为RT_Object_Class_Timer,rt_timer.parent.list以双向链表形式保存了所有初始化或创建的定时器对象,rt_timer.parent.flag则用以标识前面介绍过的定时器工作模式,现根据宏定义汇总如下表所示:
flag位 | 0 | 1 | 备注 |
---|---|---|---|
bit0 | RT_TIMER_FLAG_DEACTIVATED:定时器未激活,即初始化值 | RT_TIMER_FLAG_ACTIVATED:定时器激活,当定时器start后将会置为此状态 | 激活/非激活状态 |
bit1 | RT_TIMER_FLAG_ONE_SHOT:单次触发定时器,即定时器时间一到自动失效 | RT_TIMER_FLAG_PERIODIC:周期触发定时器,即时间一到自动开始下一次定时 | 单次定时器/周期定时器 |
bit2 | RT_TIMER_FLAG_HARD_TIMER:HARD_TIMER模式,即在中断环境中执行超时函数 | RT_TIMER_FLAG_SOFT_TIMER:SOFT_TIMER模式,即在线程环境中执行超时函数 | 硬件时钟/软件时钟标志 |
看完了定时器rt_timer继承的抽象对象rt_object此时各成员的含义,下面看定时器rt_timer私有扩展成员的含义。
rt_list_t类型的row[RT_TIMER_SKIP_LIST_LEVEL]算是一个结构体数组,看到这里可能会疑惑,rt_list_t本身已经是一个双向链表结构,怎么还以数组形式出现呢?我们先看看这