知识回顾——RTThread中的多优先级
多优先级的使用:
RTThread支持可定义的多优先级,在RTThread中,优先级的数字越小,其逻辑优先级就越高。
在深入了解它的优先级调度机制之前,我们看一下RTT系统的优先级的使用方法:
打开RTT Studio,创建一个工程,打开它的rtconfig.h文件,可以看到一个宏定义:
#define RT_THREAD_PRIORITY_MAX 32
这句话定义了我们的最大优先级。接下来进行线程的优先级定义(这里直接引用一个led1线程初始化定义):
rt_thread_init(&led1_thread, //线程控制块地址 "led1", //线程名称 led1_thread_entry, //线程入口函数 RT_NULL, //入口函数参数 &rt_led1_thread_stack[0], //栈起始地址 sizeof(rt_led1_thread_stack), //线程栈大小 3, //优先级 20); //线程时间片
我们奖优先级定义为3,这样我们就可以使这个线程按照我们所需的优先度运行了。
优先级表的实现:
我们发现,使用优先级是非常简单的,但它的实现需要靠两个变量来支持:线程优先级组和线程优先级表。
打开rtt内核文件中的scheduler.c文件,在开头的一段就可以看到两个全局变量的定义:
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
rt_uint32_t rt_thread_ready_priority_group;
第一句话是一个rt_list_t 类型的定义,它是一个线程优先级表(也叫就绪列表)。本质上它是一个链表数组,每个链表的索引就是线程的优先级。假设一个线程被创立,它的节点就会被挂载到相应的链表中。
同一个优先级表的线程都会被插入到同一个链表中,而同优先级的线程运行需要RTT系统的时间片支持。
当线程需要插入或者移出时,就需要调度器出马了,在scheduler.c中,有调度器插入线程和移除线程两个函数,调度器通过这两个函数将线程插入就绪列表或将线程从就绪列表中移除:
/*插入函数*/ void rt_schedule_insert_thread(struct rt_thread *thread) { register rt_base_t temp; RT_ASSERT(thread != RT_NULL); /* disable interrupt */ temp = rt_hw_interrupt_disable(); /* it's current thread, it should be RUNNING thread */ if (thread == rt_current_thread) { thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); goto __exit; } /* READY thread, insert to ready queue */ thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); /* insert thread to ready list */ rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), &(thread->tlist)); RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n", RT_NAME_MAX, thread->name, thread->current_priority)); /* set priority mask */ #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] |= thread->high_mask; #endif rt_thread_ready_priority_group |= thread->number_mask; __exit: /* enable interrupt */ rt_hw_interrupt_enable(temp); }
/*删除函数*/ void rt_schedule_remove_thread(struct rt_thread *thread) { register rt_base_t level; RT_ASSERT(thread != RT_NULL); /* disable interrupt */ level = rt_hw_interrupt_disable(); RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("remove thread[%.*s], the priority: %d\n", RT_NAME_MAX, thread->name, thread->current_priority)); /* remove thread from ready list */ rt_list_remove(&(thread->tlist)); if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) { #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] &= ~thread->high_mask; if (rt_thread_ready_table[thread->number] == 0) { rt_thread_ready_priority_group &= ~thread->number_mask; } #else rt_thread_ready_priority_group &= ~thread->number_mask; #endif } /* enable interrupt */ rt_hw_interrupt_enable(level); }
优先级组的实现:
rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];
rt_uint32_t rt_thread_ready_priority_group;、
然后我们看一下全局变量定义的第二句话,这是线程就绪优先级组的定义。实际上,它就是一个32位的整型数,每一个位对应着一个优先级组。如果我们的优先级大于32个,那么RTT系统便会定义一个数组成员,每个成员都表示32个优先级。
那它是怎样运作的呢?这里做一个假设:一个优先级为5的线程已经准备好了,这时优先级组的第5个位就会置1。
之后下一个系统周期开始,调度器开始从最高位开始扫描,当它扫描到第5个位时,就会发现第五个位为1,调度器就会从优先级表中寻找下标为5的数组,取出相应线程控制块,从而跳转到相应的线程。
具体的程序是RTT中的kservice.c的__rt_ffs()函数:
int __rt_ffs(int value) { if (value == 0) return 0; if (value & 0xff) return __lowest_bit_bitmap[value & 0xff] + 1; if (value & 0xff00) return __lowest_bit_bitmap[(value & 0xff00) >> 8] + 9; if (value & 0xff0000) return __lowest_bit_bitmap[(value & 0xff0000) >> 16] + 17; return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25; }
我们可以看到,value值经过运算后调用_lowest_bit_bitmap这个数组:
const rt_uint8_t __lowest_bit_bitmap[] = { /* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 10 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 20 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 30 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 40 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 50 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 60 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 70 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 80 */ 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 90 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* A0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* B0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* C0 */ 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* D0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* E0 */ 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* F0 */ 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 };
通过此表,就会返回第一个(逻辑优先级从高到低的第一个)置1的位号。
通过优先级表,优先级组,线程调度器就能实现多优先级的调度功能。

浙公网安备 33010602011771号