1. 线程就绪优先级组
-
简介
线程就绪优先级组定义如下:
rt_uint32_t rt_thread_ready_priority_group;
从代码上看,线程就绪优先级组就是一个 32 位的整形数, 每一个位对应一个优先级。一个就绪优先级组最多只能表示 32 个优先级,如果优先级超过 32 个怎么办,则可以定义一个线程就绪优先级数组, 每一个数组成员都可以表示 32 个优先级,具体支持到少由系统的 RAM 的大小决定。
线程就绪优先级组的每一个位对应一个优先级,位 0 对应优先级 0,位 1 对应优先级 1,以此类推。比如,当优先级为 10 的线程已经准备好,那么就将线程就绪优先级组的位 10 置 1,表示线程已经就绪,然后根据 10 这个索引值,在线程优先级表 10(rt_thread_priority_table[10]) 的这个位置插入线程。 有关线程就绪优先级组的位号与线程优先级对应的关系如下图所示:
-
快速查找最高优先级的方法
/** * 将 8 位整形数的取值范围 0~255 作为数组__lowest_bit_bitmap[]的索引, * 索引值第一个出现 1(从最低位开始)的位号作为该数组索引下的成员值 */ const rt_uint8_t __lowest_bit_bitmap[] = { 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; /** * 该函数用于从一个32位的数中寻找第一个被置1的位(从低位开始), * 然后返回该位的索引(即位号) * * \return 返回第一个置1位的索引号。如果全为0,则返回0。 */ int __rt_ffs (int value) { /* 如果值为0,则直接返回0 */ if (value == 0) return 0; /* * 检查bits[07:00] * 这里加1的原因是避免当第一个置1的位是位0时,返回的索引号与值都为0时返回的 * 索引号重复 */ if (value & 0xFF) return __lowest_bit_bitmap[value & 0xFF] + 1; /* 检查bits[15:08] */ if (value & 0xFF00) return __lowest_bit_bitmap[(value & 0xFF00) >> 8] + 9; /* 检查bits[23:16] */ if (value & 0xFF0000) return __lowest_bit_bitmap[(value & 0xFF0000) >> 16] + 17; /* 检查bits[31:24] */ if (value & 0xFF000000) return __lowest_bit_bitmap[(value & 0xFF000000) >> 24] + 25; }
2. 线程优先级列表
线程优先级表定义如下:
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
线程优先级表的数据类型为 rt_list,每个索引号对应线程的优先级,该索引下维护着一条双向链表,当线程就绪时,线程就会根据优先级插入到对应索引的链表, 同一个优先级的线程都会被插入到同一条链表中(当同一个优先级下有多个线程时,需要时间片的支持,后面的章节再讲解) 。 一个有 5 个线程就绪的就绪列表示意图如下图所示,其中thread1和thread4为同一优先级。
3. 线程初始化过程
rt_err_t rt_thread_init rt_thread_init (struct rt_thread *thread, /* 线程控制块 */
const char *name, /* 线程名字,字符串形式 */
void (*entry) (void *parameter), /* 线程入口地址 */
void *parameter, /* 线程形参 */
void *stack_start, /* 线程栈起始地址 */
rt_uint32_t stack_size, /* 线程栈大小 */
rt_uint8_t priority) /* 线程优先级 */
{
// 1. 线程对象初始化,并添加到对象容器中
// 2. 初始化线程控制块成员(链表节点、入口地址、形参、栈地址、栈大小、栈指针)
// 3. 初始化线程优先级(初始优先级、当前优先级、当前优先级掩码)
// 4. 初始化线程错误码和状态
}
rt_err_t rt_thread_startup (rt_thread_t thread)
{
// 1. 设置线程优先级(当前优先级、当前优先级掩码)
// 2. 设置线程状态为挂起状态(SUSPEND)
// 3. 将线程插入到就绪列表
// 3.1. 设置线程状态为就绪状态(READY)
// 3.2. 将线程插入到线程优先级列表末尾
// 3.3. 设置线程就绪优先级组中对应的位为1
// 4. 如果当前线程不为RT_NULL,则执行系统调度
}
4. 系统调度过程
void rt_schedule (void)
{
// 1. 获取就绪的最高优先级
// 2. 获取最高优先级线程(目标线程)对应的线程控制块
// 3. 如果目标不是当前线程,则执行线程切换
}