中级篇_06_优先级的实现

多优先级是怎么实现的

  多优先级是怎么实现的,简单来说,一个数组就可以组成一个优先级列表。比如a[10],可以支持10个优先级。数组中的每一个元素代表一个优先级,优先级就是将一些线程分为不同的响应级别,优先级越高越容易得到执行的可能。这样理解起来很简单,但是对于rt_thread来说,应该更精细,更完善。
  rt_thread是怎么实现优先级的,rt_thread是怎么将这些优先级同线程串联到一起的,这个是我们多优先级是怎么实现的所关注的。为了防止大家傻傻分不清我接下来要说的三个表,先在第一个内容下标注出来,来引起大家的注意:

Kenrel下的scheduler.c文件

rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX];		//线程优先级表
struct rt_thread *rt_current_thread;	
rt_uint8_t rt_current_priority;
#if RT_THREAD_PRIORITY_MAX > 32							//支持大于32个优先级
/* Maximum priority level, 256 */
rt_uint32_t rt_thread_ready_priority_group;				//线程就绪优先级表
rt_uint8_t rt_thread_ready_table[32];					//二级位图表
#else
/* Maximum priority level, 32 */						//支持小于32个优先级
rt_uint32_t rt_thread_ready_priority_group;				//线程就绪优先级表
#endif
  • 第一个表 rt_thread_priority_table[RT_THREAD_PRIORITY_MAX]
     你总会看到这个表,这个表内存储的是为不同优先级设计的链表数组,不同级别的优先级会在不同的链表下,同一级别的优先级会在相同的链表内。具体的样子<传送门>
  • 第二个表 rt_uint32_t rt_thread_ready_priority_group
     讲道理,你说这是一个表,已经不是特别合适了。因为他的操作已经不是像第一个表一样,是用数组一个字节来表示信息了;这个表是使用32位的每一位来表示的。一共是32位,用来代表32个优先级。每一位是一个优先级,快速查找优先级的算法就是利用这个来实现的。
  • 第三个表 rt_uint8_t rt_thread_ready_table[32]
     通常,我们并不用这个,因为,我们在rtconfig.h中定义RT_THREAD_PRIORITY_MAX通常为32,。但是,实际上,是可以支持到256位的,秘诀就是这个表。这个个第二个表中不一样,这里使用rt_uint8_t申明了这个数组,一共是32个元素,每个元素有8位。8*32=256,正好是256个优先级。RT_Thread实现256个优先级,是每8位整合为一位,塞到rt_thread_ready_priority_group中,因为这样可以直接借用__rt_ffs,而不用再重新编写一个新的位图。

  这样就先简单提一下,便于后面的深入理解。

__rt_ffs是怎么快速找到最先转入就绪态的线程的

/**
 * This function finds the first bit set (beginning with the least significant bit)
 * in value and return the index of that bit.
 *
 * Bits are numbered starting at 1 (the least significant bit).  A return value of
 * zero from any of these functions means that the argument was zero.
 *
 * @return return the index of the first bit set. If value is 0, then this function
 * shall return 0.
 */
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;
}

  这里频繁调用了__lowest_bir_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
};

  这是一个常量表,这里就没有运算,就是读值。这个表的意思就是,你给一个uint8_t型的数,就给你返回这个数的二进制形态下从最低位数,第一个不为零的位数是哪一位。所以,这个返回的数只可能是0,1,2,3,4,5,6,7。我们从表上看也是这样的。嗯,高大上的说法是:时间换空间,通俗点说是不用for循环来判断到底是最低的哪一位为1,时间更快,也更稳定。总之的结果就是,对于实时嵌入系统,这样可以保证时间的准确,不会因为随机情况而产生时间花费不一致的情况。

const rt_uint8_t __lowest_bit_bitmap[]适用于8位,对于一个32位的数,需要转换4次。也就是int __rt_ffs(int value)函数。

Created with Raphaël 2.2.0 开始 优先级最高为8 __rt_ffs 结束 优先级最高不大于32 __rt_ffs 优先级最高不大于256 __rt_ffs yes no yes no yes

这个流程图画出来,看起来挺丑的。嗯嗯~~不过,是这个意思。所以,在优先级大于32而小于256的时候,需要rt_uint8_t rt_thread_ready_table[32]

支持最多的优先级达到256个,怎么实现的1

  下面来看看具体的实现代码。

//scheduler.c下的rt_scheduler()函数
        register rt_ubase_t highest_ready_priority;

#if RT_THREAD_PRIORITY_MAX <= 32
        highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1;
#else
        register rt_ubase_t number;

        number = __rt_ffs(rt_thread_ready_priority_group) - 1;
        highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;
#endif
register rt_ubase_t number;

        number = __rt_ffs(rt_thread_ready_priority_group) - 1;
        highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1;

highest_ready_priority就是返回的一个值,这个值在小于32个优先级时,是一个小于32的值,代表最高优先级就绪位。小于32个优先级的很容易看懂,对于256个优先级时候,每一句是什么意思呢?这一句需要看懂,需要看这256个优先级是怎么样转换的?

//rtdef.h下rt_thread的struct
    /* priority */
    rt_uint8_t  current_priority;                       /**< current priority */
    rt_uint8_t  init_priority;                          /**< initialized priority */
#if RT_THREAD_PRIORITY_MAX > 32
    rt_uint8_t  number;
    rt_uint8_t  high_mask;
#endif
    rt_uint32_t number_mask;
//thread.c下的_rt_thread_init函数
    /* priority init */
    RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX);
    thread->init_priority    = priority;
    thread->current_priority = priority;

    thread->number_mask = 0;
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number = 0;
    thread->high_mask = 0;
#endif

  上面个是在线程控制块TCB中struct rt_thread中添加支持256优先级的numberhigh_mask。并且在rt_thread_init中进行初始化。

//scheduler.c
/**
 * This function will start a thread and put it to system ready queue
 *
 * @param thread the thread to be started
 *
 * @return the operation status, RT_EOK on OK, -RT_ERROR on error
 */
rt_err_t rt_thread_startup(rt_thread_t thread)
{
    /* thread check */
    RT_ASSERT(thread != RT_NULL);
    RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT);
    RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread);

    /* set current priority to init priority */
    thread->current_priority = thread->init_priority;

    /* calculate priority attribute */
#if RT_THREAD_PRIORITY_MAX > 32
    thread->number      = thread->current_priority >> 3;            /* 5bit */
    thread->number_mask = 1L << thread->number;
    thread->high_mask   = 1L << (thread->current_priority & 0x07);  /* 3bit */
#else
    thread->number_mask = 1L << thread->current_priority;
#endif

    RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n",
                                   thread->name, thread->init_priority));
    /* change thread stat */
    thread->stat = RT_THREAD_SUSPEND;
    /* then resume it */
    rt_thread_resume(thread);
    if (rt_thread_self() != RT_NULL)
    {
        /* do a scheduling */
        rt_schedule();
    }

    return RT_EOK;
}

  thread->number = thread->current_priority >> 3 ,位移操作,向右位移3位,用算术的角度描述是除以8(当然是商部分,不包含余数的)。记住这个数代表前5位数据,用thread->number来存储。对于256位的优先级来说,priority>>3的数据将会是一个小于32的数据,经thread->number_mask = 1L << thread->number运算会存储一个数据作为number_mask代表高五位thread->high_mask = 1L << (thread->current_priority & 0x07) 作为低三位

//scheduler.c
/*
 * This function will insert a thread to system ready queue. The state of
 * thread will be set as READY and remove from suspend queue.
 *
 * @param thread the thread to be inserted
 * @note Please do not invoke this function in user application.
 */
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();

    /* change stat */
    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));

    /* set priority mask */
#if RT_THREAD_PRIORITY_MAX <= 32
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("insert thread[%.*s], the priority: %d\n",
                                      RT_NAME_MAX, thread->name, thread->current_priority));
#else
    RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                 ("insert thread[%.*s], the priority: %d 0x%x %d\n",
                  RT_NAME_MAX,
                  thread->name,
                  thread->number,
                  thread->number_mask,
                  thread->high_mask));
#endif

#if RT_THREAD_PRIORITY_MAX > 32
    rt_thread_ready_table[thread->number] |= thread->high_mask;
#endif
    rt_thread_ready_priority_group |= thread->number_mask;

    /* enable interrupt */
    rt_hw_interrupt_enable(temp);
}

  这里的与位操作,另外,high_mask和number_mask都是被我们存储过的。至此已经完全加入到就绪列表,经由rt_scheduler()调度,并找出highest_ready_priority,开始进入**rt_thread_priority_table[]找到这条被标记的优先级下的链表,然后经由上下文切换函数rt_hw_context_switch()**切换到需要执行的函数。

对同一优先级下线程切换-时间片思考

  看完支持优先级之后,是不是有一点疑惑,难道同一个优先级下只有一个线程吗?怎么保证我切进这个优先级后能找到我需要的那个函数呢?RT-Thread支持时间片,如果不支持时间片,在同一个优先级下的两条线程是怎么进行执行的?


  1. RTThread物联网操作系统<传送门> ↩︎

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值