支持定时器与时间片

本贴最后更新于 524 天前,其中的信息可能已经事过景迁

1- 实现基于定时器的线程阻塞,每个线程内部都内置一个定时器,定时器启动后,线程将放弃 CPU 的使用权。

2- 当同一个优先级下有两个或两个以上线程的时候,线程支持时间片功能,即我们可以指定线程持续运行一次的时间,单位为 tick。

定时器设计

调度器每次调度时都会查看就绪列表,挑选出就绪列表中优先级最高线程执行。

定时器的设计原理是,定时器启动后会把线程移除就绪列表,放入定时器列表。于是调度器扫描就绪列表时找不到线程,故不会执行线程。

SysTick 中断发生时,系统会扫描定时器列表,如果定时结束,会把线程移除定时器列表,放回就绪列表中。

时间片设计1

当线程优先级相同时,我们可以设定相同优先级的运行时间。当运行时间到时,就把该线程从就绪列表中移除,然后放入就绪列表当前优先级的尾部。调度器会从就绪列表当前优先级的头部挑选线程执行

1. 定时器代码实现

定时器模块的两个操作块:定时器控制块定时器列表

定时器控制块是一个结构体,记录了定时器的内部属性,比如定时器节点(是链表节点,好比一个钩子,可以钩在定时器列表上),超时函数指针(定时结束后执行的函数),定时时间

定时器控制块(每个线程都会内置定时器,会在线程初始化时将定时器初始化):

/* 定时器结构体 */ struct rt_timer { struct rt_object parent; /* 从rt_object继承 */ 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;

注:init_tick 是定时器延时的相对时间,即从定时器启动时开始计算的时间。

timeout_tick 是从操作系统的时钟启动时开始计算的定时器延时时间。

比如说:系统定义了一个全局的系统时基计数器 rt_tick(在 clock.c 中定义),每产生一次系统时基中断(即 SysTick 中断)时,rt_tick 计数加一。假设线程要延时 10 个 tick,即 init_tick 等于 10,此时 rt_tick 等于 2,那么 timeout_tick 就等于 10 加 2 等于 12,当 rt_tick 递增到 12 的时候,线程延时到期,这个就是 timeout_tick 的实际含义。

定时器列表

static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL];

用于挂载延时线程

定时器函数实现

定时器提供给外部的的接口函数:

  • 系统定时器列表初始化2​ ​- ==void rt_system_timer_init(void)==

  • 定时器初始化 3- 在线程初始化时调用

    void rt_timer_init(rt_timer_t timer, const char *name, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag)
  • 定时器启动 4- **==rt_timer_start==**

    • 定时器模块的核心,负责把定时器按照延时做升序排列插入定时器列表
  • 定时器停止 5​ ​- ==**​ rt_err_t rt_timer_stop(rt_timer_t timer)**==​​

  • 定时器控制函数 - ==**rt_timer_control**==

    • 用于设置定时器的内部属性
    • 通常在用户指定的线程函数内部调用,主要设置定时器的延时时间
  • 定时器扫描函数6​ ​- **==void rt_timer_check(void)==**​​

    • 扫描定时器列表,超时时间发生时,就调用对应的超时函数
    • 在 SysTick 的中断函数中被调用

系统定时器列表初始化

遍历系统定时器列表,使其内部节点自指

/* 系统定时器列表初始化 */ void rt_system_timer_init(void) { int i; for(i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++) { rt_list_init(rt_timer_list + i); } }

定时器初始化

  • 设置定时器初始状态 - 非激活态
  • 设置超时函数
/* 内部函数 - 初始化内置定时器 */ static void _rt_timer_init(rt_timer_t timer, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, rt_uint8_t flag) { int i; /* 定时器对象初始化,将其插入对象容器 */ rt_object_init((rt_object_t)timer,RT_Object_Class_Timer,name); /* 设置标志 */ timer->parent.flag = flag; /* 先设置为非激活状态 */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; timer->timeout_func = timeout; timer->parameter = parameter; /* 初始化定时器实际超时的系统节拍数 */ timer->timeout_tick = 0; /* 初始化定时器需要超时的节拍数 */ timer->init_tick = time; /* 初始化定时器的内置节点 */ for(i = 0 ; i < RT_TIMER_SKIP_LIST_LEVEL; i++) { rt_list_init(&(timer->row[i])); } }

定时器停止

  • 只有处于激活状态的定时器才可以停止
  • 将定时器从定时器列表中移除 - 调用封装函数 **==_rt_timer_remove==**
rt_err_t rt_timer_stop(rt_timer_t timer) { register rt_base_t level; /* 只有处于激活状态的定时器才可以被停止,否则退出错误码返回 */ if(!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) return -RT_ERROR; /* 关中断 */ level = rt_hw_interrupt_disable(); _rt_timer_remove(timer); rt_hw_interrupt_enable(level); /* 改变定时器的状态为非激活状态 */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; return RT_EOK; }

static 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_err_t rt_timer_start(rt_timer_t timer) { unsigned int row_lvl = 0; rt_list_t *timer_list; register rt_base_t level; rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]; unsigned int tst_nr; static unsigned int random_nr; /* 关中断 */ level = rt_hw_interrupt_disable(); /* 将定时器从系统定时器列表移除 */ _rt_timer_remove(timer); /* 将定时器状态设置为非激活态 */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; /* 获取 timeout tick, 最大的 timeout tick 不能大于 RT_TICK_MAX/2 */ timer->timeout_tick = rt_tick_get() + timer->init_tick; rt_hw_interrupt_enable(level); /* 将定时器插入到定时器列表 */ /* 获取定时器列表根节点地址, rt_timer_list 是一个全局变量 */ timer_list = rt_timer_list; /* 获取系统定时器列表第一根链表的根节点地址 */ row_head[0] = &timer_list[0]; for(row_lvl = 0; row_lvl < RT_TIMER_SKIP_LIST_LEVEL; row_lvl++) { for(; row_head[row_lvl] != timer_list[row_lvl].prev; row_head[row_lvl] = row_head[row_lvl]->next) { struct rt_timer *t; /* 获取定时器列表节点地址 */ rt_list_t *p = row_head[row_lvl]->next; /* 根据节点地址获取父结构的指针 */ t = rt_list_entry(p, /* 节点地址 */ struct rt_timer, /* 节点所在父结构的数据类型 */ row[row_lvl]); /* 节点在父结构中叫什么,即名字 */ /* 两个定时器的超时时间相同,则继续在定时器列表中寻找下一个节点 */ if( (t->timeout_tick - timer->timeout_tick) == 0) { continue; } else if(t->timeout_tick - timer->timeout_tick < RT_TICK_MAX /2) { break; } } if (row_lvl != RT_TIMER_SKIP_LIST_LEVEL - 1) { row_head[row_lvl + 1] = row_head[row_lvl] + 1; } } /* random_nr 是一个静态变量,用于记录启动了多少个定时器 */ random_nr++; tst_nr = random_nr; /* 将定时器插入到系统定时器列表 */ rt_list_insert_after(row_head[RT_TIMER_SKIP_LIST_LEVEL - 1], &(timer->row[RT_TIMER_SKIP_LIST_LEVEL - 1]) ); /* 设置定时器标志位为激活态 */ timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; /* 开中断 */ rt_hw_interrupt_enable(level); return -RT_EOK; }

定时器扫描函数

/* * 用于扫描系统定时器列表,当有超时时间发生时,就调用对应的超时函数 * * 该函数会在SysTick的中断函数中被调用 * * */ void rt_timer_check(void) { struct rt_timer *t; rt_tick_t current_tick; register rt_base_t level; /* 获取系统时基计数器 rt_tick 的值 */ current_tick = rt_tick_get(); /* 关中断 */ 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]); if( (current_tick - t->timeout_tick) < RT_TICK_MAX / 2) { /* 先将定时器从系统定时器列表移除 */ _rt_timer_remove(t); /* 调用超时函数 */ t->timeout_func(t->parameter); /* 重新获取rt_tick */ current_tick = rt_tick_get(); /* 周期定时器 */ if( (t->parent.flag & RT_TIMER_FLAG_PERIODIC) && (t->parent.flag & RT_TIMER_FLAG_ACTIVATED) ) { /* 启动定时器 */ t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; rt_timer_start(t); } else { /* 停止计时器 */ t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; } } else break; } /* 开中断 */ rt_hw_interrupt_enable(level); }

2. 线程代码改动

定时器向线程提供接口函数,支持线程以下功能:

  • 线程初始化时同时初始化定时器7

    • 在初始化函数中调用定时器初始化函数
  • 线程延时8

    • 将线程移出就绪列表 (调度器提供的接口)
    • 设置定时器属性 - 设置延时时间
    • 启动定时器 - 将定时器以延时时间升序排列的方式挂载到系统定时器列表
    • 执行新的系统调度(此时调度器已经找不到原来的线程,会执行新的线程)
  • 超时函数9

    • 将挂起的线程再插入到就绪列表中,恢复线程的可执行状态

线程初始化

  • 线程初始化的同时初始化定时器
/* 初始化线程定时器 */ rt_timer_init(&(thread->thread_timer), /* 静态定时器对象 */ thread->rt_object.name, /* 定时器的名字,直接使用的是线程的名字 */ rt_thread_timeout, /* 超时函数 */ thread, /* 超时函数形参 */ 0, /* 延时时间 */ RT_TIMER_FLAG_ONE_SHOT); /* 定时器的标志 */

线程延时

/* 让线程睡眠一段时间 * * 调用定时器提供的接口 * */ rt_err_t rt_thread_sleep(rt_tick_t tick) { register rt_base_t temp; struct rt_thread *thread; /* 关中断 */ temp = rt_hw_interrupt_disable(); /* 获取当前线程的线程控制块 */ thread = rt_current_thread; /* 挂起线程,操作就绪列表 */ rt_thread_suspend(thread); /* 设置线程定时器的超时时间 - 操作定时器 */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick); /* 启动定时器 */ rt_timer_start(&(thread->thread_timer)); /* 开中断 */ rt_hw_interrupt_enable(temp); /* 执行系统调度 */ rt_schedule(); return RT_EOK; }
static rt_err_t rt_thread_suspend(rt_thread_t thread) { register rt_base_t temp; /* 只有就绪的线程才能被挂起,否则退出返回错误码 */ if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_READY){ return -RT_ERROR; } temp = rt_hw_interrupt_disable(); /* 改变线程状态 */ thread->stat = RT_THREAD_SUSPEND; /* 将线程从就绪列表删除 */ rt_schedule_remove_thread(thread); /* 停止线程定时器 */ rt_timer_stop(&(thread->thread_timer)); /* 开中断 */ rt_hw_interrupt_enable(temp); return RT_EOK; }

超时函数

  • 将线程从悬挂列表中删除
  • 将线程插入到就绪列表
/** * 线程超时函数 * 当线程延时到期或者等待的资源可用或者超时时,该函数会被调用 * * @param parameter 超时函数的形参 */ void rt_thread_timeout(void *parameter) { struct rt_thread *thread; thread = (struct rt_thread *)parameter; /* 设置错误码为超时 */ thread->error = -RT_ETIMEOUT; /* 将线程从挂起列表中删除 */ rt_list_remove(&(thread->tlist)); /* 将线程插入到就绪列表 */ rt_schedule_insert_thread(thread); /* 系统调度 */ rt_schedule(); }

支持时间片

时间片设计1

rt_ubase_t init_tick; /* 剩余时间片 */ rt_ubase_t remaining_tick; /* 用于实现阻塞延时 */

修改系统时基更新函数

void rt_tick_increase(void) { /* 获取当前线程 */ struct rt_thread *thread; /* 系统时基计数器加 1 操作,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_err_t rt_thread_yield(void) { register rt_base_t level; struct rt_thread *thread; /* 关中断 */ level = rt_hw_interrupt_disable(); thread = rt_current_thread; /* 如果线程在就绪态,且同一个优先级下不止一个线程 */ if( ((thread->stat) & RT_THREAD_STAT_MASK ) == RT_THREAD_READY && thread->tlist.next != thread->tlist.prev) { /* 将时间片耗完的线程从就绪列表移除 */ rt_list_remove(&(thread->tlist)); /* 将线程插入到该优先级下的列表的尾部 */ rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), &(thread->tlist)); } /* 开中断 */ rt_hw_interrupt_enable(level); /* 执行调度 */ rt_schedule(); return RT_EOK; }

main 函数代码:

#include <rtthread.h> #include <rthw.h> #include "ARMCM3.h" #include <rtconfig.h> #include <core_cm3.h> /* ************************************************************************* * 全局变量 ************************************************************************* */ rt_uint8_t flag1; rt_uint8_t flag2; rt_uint8_t flag3; extern rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX]; /* ************************************************************************* * 线程控制块& STACK &线程声明 ************************************************************************* */ /* 定义线程控制块 */ struct rt_thread rt_flag1_thread; struct rt_thread rt_flag2_thread; struct rt_thread rt_flag3_thread; ALIGN(RT_ALIGN_SIZE) /* 定义线程栈 */ rt_uint8_t rt_flag1_thread_stack[512]; rt_uint8_t rt_flag2_thread_stack[512]; rt_uint8_t rt_flag3_thread_stack[512]; /* 线程声明 */ void flag1_thread_entry(void *p_arg); void flag2_thread_entry(void *p_arg); void flag3_thread_entry(void *p_arg); /* ************************************************************************* * 函数声明 ************************************************************************* */ void delay(rt_uint32_t count); int main(void) { /* 关中断 */ rt_hw_interrupt_disable(); /* SysTick中断频率设置 */ SysTick_Config( SystemCoreClock / RT_TICK_PER_SECOND ); // 25MHz / 100 /* 系统定时器列表初始化 */ rt_system_timer_init(); /* 调度器初始化 */ rt_system_scheduler_init(); /* 初始化空闲线程 */ rt_thread_idle_init(); /* 初始化线程 */ rt_thread_init( &rt_flag1_thread, /* 线程控制块 */ "rt_flag1_thread", /* 线程名字,字符串形式 */ flag1_thread_entry, /* 线程入口地址 */ RT_NULL, /* 线程形参 */ &rt_flag1_thread_stack[0], /* 线程栈起始地址 */ sizeof(rt_flag1_thread_stack), /* 线程栈大小,单位为字节 */ 2, /* 优先级 */ 4); /* 时间片 */ /* 将线程1插入就绪列表 */ //rt_list_insert_before( &(rt_thread_priority_table[0]),&(rt_flag1_thread.tlist) ); rt_thread_startup(&rt_flag1_thread); /* 初始化线程 */ rt_thread_init( &rt_flag2_thread, /* 线程控制块 */ "rt_flag2_thread", /* 线程名字,字符串形式 */ flag2_thread_entry, /* 线程入口地址 */ RT_NULL, /* 线程形参 */ &rt_flag2_thread_stack[0], /* 线程栈起始地址 */ sizeof(rt_flag2_thread_stack), /* 线程栈大小,单位为字节 */ 3, /* 优先级 */ 2); /* 时间片 */ /* 将线程2插入就绪列表 */ //rt_list_insert_before( &(rt_thread_priority_table[1]),&(rt_flag2_thread.tlist) ); rt_thread_startup(&rt_flag2_thread); /* 初始化线程 */ rt_thread_init( &rt_flag3_thread, /* 线程控制块 */ "rt_flag3_thread", /* 线程名字,字符串形式 */ flag3_thread_entry, /* 线程入口地址 */ RT_NULL, /* 线程形参 */ &rt_flag3_thread_stack[0], /* 线程栈起始地址 */ sizeof(rt_flag3_thread_stack), /* 线程栈大小,单位为字节 */ 3, /* 优先级 */ 3); /* 时间片 */ /* 将线程3插入就绪列表 */ rt_thread_startup(&rt_flag3_thread); /* 启动系统调度器 */ rt_system_scheduler_start(); } /* ************************************************************************* * 函数实现 ************************************************************************* */ void delay (rt_uint32_t count) { for(; count!=0; count--); } void flag1_thread_entry( void *p_arg ) { for ( ;; ) { flag1 = 1; rt_thread_delay(3); //阻塞延时 flag1 = 0; rt_thread_delay(3); } } void flag2_thread_entry( void *p_arg ) { for ( ;; ) { flag2 = 1; delay(100); //软件延时 flag2 = 0; delay(100); } } void flag3_thread_entry( void *p_arg ) { for ( ;; ) { flag3 = 1; // rt_thread_delay(3); delay( 100 ); flag3 = 0; // rt_thread_delay(3); delay( 100 ); } } void SysTick_Handler(void) { /* 进入中断 */ rt_interrupt_enter(); rt_tick_increase(); /* 离开中断 */ rt_interrupt_leave(); }

实验现象:

image


  1. 支持时间片

  2. 系统定时器列表初始化

  3. 定时器初始化

  4. 定时器启动

  5. 定时器停止

  6. 定时器扫描函数

  7. 线程初始化

  8. 线程延时

  9. 超时函数

  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    85 引用 • 165 回帖 • 4 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...