RT-Thread内核之线程调度(二)

1.什么是临界区?
临界区是指访问共用资源的程序片段。通俗来说,临界区是一段程序代码,但这段代码在访问公共的资源(如全局变量,如某个硬件设备),因此,这段代码的执行必须加锁以保证互斥访问。

2.什么是调度器?
调度器的本质就是一个调度函数,该调度函数的本质就是按照一定的调度算法选出一个最优的个体(线程、进程或任务),并切换到该实体去执行,实质就是让该实体获得CPU的使用权。那么调度器在系统中什么时候会被调用呢?最容易想到的就是系统时钟中断处理程序中,正占用CPU的实体主要让出CPU时,有新的实体就绪时。

3.RT-Thread的调度算法分析?
RT-Thread的调度器是一个名为rt_schedule的函数,RT-Thread是一个基于优先级调度的实时操作系统,因此调度算法的核心是找出系统就绪线程中的最高优先级,通过优先级找到对应的线程,最终切换到新线程中去运行。

void rt_schedule(void) {
rt_base_t level;
struct rt_thread *to_thread;
struct rt_thread *from_thread;
/* 禁止全局中断 /
level = rt_hw_interrupt_disable();
/** 检查调度器是否上锁,如果没有上锁执行调度
* 如果被上锁,直接退出
*/
if (rt_scheduler_lock_nest == 0) {

    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

    /** 由优先级找到对应的线程句柄 */
    to_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next,
                              struct rt_thread,
                              tlist);   /** 如果最高优先级线程非当前线程,切换到新线程去执行 */
    if (to_thread != rt_current_thread) {

        rt_current_priority = (rt_uint8_t)highest_ready_priority;
        from_thread         = rt_current_thread;
        rt_current_thread   = to_thread;
        RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (from_thread, to_thread));
        RT_DEBUG_LOG(RT_DEBUG_SCHEDULER,
                     ("[%d]switch to priority#%d "
                      "thread:%.*s(sp:0x%p), "
                      "from thread:%.*s(sp: 0x%p)\n",
                      rt_interrupt_nest, highest_ready_priority,
                      RT_NAME_MAX, to_thread->name, to_thread->sp,
                      RT_NAME_MAX, from_thread->name, from_thread->sp));

ifdef RT_USING_OVERFLOW_CHECK

        _rt_scheduler_stack_check(to_thread);

endif

        /** 如果中断嵌套的层数为0,为正常的上下文切换 */
        if (rt_interrupt_nest == 0) {
            rt_hw_context_switch((rt_uint32_t)&from_thread->sp,
                                 (rt_uint32_t)&to_thread->sp);
        } else {
          /** 如果中断嵌套的层数不为0,说明是在中断处理中进行的任务切换
           * 调用中断上下文切换函数
           */
            RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n"));
            rt_hw_context_switch_interrupt((rt_uint32_t)&from_thread->sp,
                                           (rt_uint32_t)&to_thread->sp);
        }
    }
}
/** 使能全局中断 */
rt_hw_interrupt_enable(level); }

此处最关心的就是系统是如何找出最高的就绪优先级,涉及几个全局变量:rt_thread_ready_priority_group,rt_thread_ready_table[]
以及一个函数:__rt_ffs
首先我们需要看懂一个数组:

const rt_uint8_t __lowest_bit_bitmap[] = {
0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 00 - 15*/
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 16 - 31 */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 32 - 47 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 48 - 63 */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 64 - 79 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 80 - 95 */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 96 - 111 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 112 - 127 */
7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 128 - 143 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 144 - 159 */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 160 - 175 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 176 - 191 */
6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 192 - 207 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 208 - 223 */
5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, /* 224 - 239 */
4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /* 240 - 255 */ };

该数组含有256个元素,记录了0-255这256个数对应的二进制数从右到左第一个为1的bit的编号,一个无符号的char型由8bit组成(bit0 - bit7)。如果你对此有疑问,完全可以拿一个数试验以下。我们以63为例,十进制的63对应的二进制为:0111 1111。因此,63对应的第一个为1的位为0,其实这个数组就是通过这种方式计算出来的。那么这里有几个疑问?
1.为什么从0开始?因为数组的下标是从开始的,因此RT-Thread的最高优先级为0
2.使用这个数组有什么用?

rt_uint32_t rt_thread_ready_priority_group; rt_uint8_t
rt_thread_ready_table[32];

就绪表是一个含有32个元素的8bit无符号数,可以看成32组,每一组为8bit,可以代表256个位图。因此,rt_thread_ready_priority_group是一个32位的无符号数。系统中任何一个任务就绪后都会影响到rt_thread_ready_table和rt_thread_ready_priority_group。相反通过查找就绪组和就绪表就能找出最高的优先级。下面以例子为证:假设当前系统中有一个优先级为23的线程就绪了。首先需要找出优先级23对应就绪表中的位置:23 / 8 <==> 23 >> 3 = 2,说明对应在就绪表中的行坐标为rt_thread_ready_table[2],列坐标为rt_thread_ready_table[2].bit[7]。而23对应的二进制值为:0001 0111,让我们更为惊讶的是二进制的高5位表示的是该优先级所在的组号,而低3位表示的是该优先级在组中的位号。00010 = 2 (组号) 111 = 7 (位号)。在RT-Thread线程结构中,有几项成员专门来记录线程优先级的组号,位号信息。

if RT_THREAD_PRIORITY_MAX > 32

rt_uint8_t  number;         /**< 线程优先级对应的组号: current_priority >> 3 */
rt_uint8_t  high_mask;      /**< 线程位号掩码: (1 << 位号) 位号: (current_priority & 7) */

endif

rt_uint32_t number_mask;    /**< 组号掩码: (1 << 组号) */

if RT_THREAD_PRIORITY_MAX > 32

        thread->number      = thread->current_priority >> 3;            /* 5bit */
        thread->number_mask = 1 << thread->number;
        thread->high_mask   = 1 << (thread->current_priority & 0x07);   /* 3bit */

else

        thread->number_mask = 1 << thread->current_priority;

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;        /** 设置组号 */

反过来通过__rt_ffs找出最高优先级的过程:

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

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;
}

虽然rt_thread_ready_priority_group为32位无符号数,已经超出__lowest_bit_bitmap数组的数组元素(0 - 255)但是可以将32位看成是4个连续的8位,进而求出32位数的第一个为1的位。通过组号再调用依次__lowest_bit_bitmap即可计算出位号。在由:优先级 = (组号 << 3) + 位号 就可以计算出最高的优先级了。

你可能感兴趣的:(My,OS)