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) + 位号 就可以计算出最高的优先级了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-11-13 08:03:03

RT-Thread内核之线程调度(二)的相关文章

RT Thread学习历程(1):串口乱码问题

因为学习实时系统,最近接触到RT Thread. 把RT Thread官网上的示例代码烧录到STM32的板子上之后,在串口软件上接收到的全是乱码,一开始以为是串口软件的问题,换了2个软件之后情况都一样,最后发现是晶振的问题,我用的是STM32F407VGT6,晶振要设为8MHz,代码相应的设置晶振的部分也要修改.

Linux内核分析(二)----内核模块简介|简单内核模块实现

Linux内核分析(二) 昨天我们开始了内核的分析,网上有很多人是用用源码直接分析,这样造成的问题是,大家觉得很枯燥很难理解,从某种意义上来说linux系统本身就是由一个个模块构成的,所以我会结合内核模块的设计,去分析内核,从而达到对linux内核的理解. 今天我们会分析到以下内容: 1.      Linux内核模块简介 2.      简单内核模块实现 l  Linux内核模块简介 1.       何为内核模块 在上一篇博文中我们先通过内核配置,在配置的过程中我们对内核的组件进行了选择(当

变量赋值 php内核的实现(二)

<?php$a=1;$b=&$a;$c=2;$a=&$c;echo $a."\n";echo $b; 2 1 结论: 首先保存 左值的内存地址, 因这个内存地址会被再次被赋值 1)右值是引用 进入2.2 2.3 2.4步骤 例子: <?php $a=1; $c=2; $b=&$a; //执行到这里时,属于第2种情况 $c=&$a; //执行到这里时,属于第1种情况, 2)右值不是引用,右值的refcount_gc减1 2.1)如果refcou

RT-thread内核之线程调度器分析

一.前言 RT-Thread中提供的线程调度器是基于全抢占式优先级的调度,在系统中除了中断处理函数.调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身.系统总共支持256个优先级(0 - 255,数值越小的优先级越高,0为最高优先级,255分配给空闲线程使用,一般用户不使用.在一些资源比较紧张的系统中,可以根据情况选择只支持8个或32个优先级的系统配置).在系统中,当有比当前线程优先级还要高的线程就绪时,当前线程将立刻被换出,高优先级线程抢占处

RT-thread内核之线程调度器

http://www.cnblogs.com/King-Gentleman/p/4278012.html 一.前言 RT-Thread中提供的线程调度器是基于全抢占式优先级的调度,在系统中除了中断处理函数.调度器上锁部分的代码和禁止中断的代码是不可抢占的之外,系统的其他部分都是可以抢占的,包括线程调度器自身.系统总共支持256个优先级(0 - 255,数值越小的优先级越高,0为最高优先级,255分配给空闲线程使用,一般用户不使用.在一些资源比较紧张的系统中,可以根据情况选择只支持8个或32个优先

“Linux内核分析”实验二报告

张文俊 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.第二周学习内容总结 1.计算机工作“三大法宝” 首先,计算机工作原理最重要的三个内容就是:存储程序计算机工作模型.中断机制和函数调用堆栈. 存储程序计算机工作模型是计算机系统最最基础性的逻辑结构: 中断机制是多道程序操作系统的基点,没有中断机制程序只能从头一直运行结束才有可能开始运行其他程序: 函数调用堆栈是高级语言得以

RT-thread内核之线程调度算法分析

一个操作系统如果只是具备了高优先级任务能够“立即”获得处理器并得到执行的特点,那么它仍然不算是实时操作系统.因为这个查找最高优先级线程的过程决定了调度时间是否具有确定性,例如一个包含n个就绪任务的系统中,如果仅仅从头找到尾,那么这个时间将直接和n相关,而下一个就绪线程抉择时间的长短将会极大的影响系统的实时性.当所有就绪线程都链接在它们对应的优先级队列中时,抉择过程就将演变为在优先级数组中寻找具有最高优先级线程的非空链表. RT-Thread内核中采用了基于位图(bitmap)的优先级算法(时间复

RT thread 设备驱动之串口设备

本文以stm32f4xx平台介绍串口驱动,主要目的是: 1.RTT中如何编写中断处理程序 2.如何编写RTT设备驱动接口代码 3.了解串行设备的常见处理机制 所涉及的主要源码文件有:usart.c,usart.h,serial.c,serial.h 一.RTT的设备驱动程序概述 编写uart的驱动程序,首先需要了解RTT的设备框架,这里以usart的驱动来具体分析RTT的IO设备管理.注:参考<RTT实时操作系统编程指南> I/O设备管理一章. 我们可以将USART的硬件驱动分成两个部分,如下

linux4.10.8 内核移植(二)---初步裁剪、分区修改和文件系统

一.初步裁剪 在内核根目录下 执行: make menuconfig 1.1 system type裁剪 选择 SAMSUNG S3C24XX SoCs Support 进入其中,这里是配置我们的单板,取消与2440无关的配置: 1.2 文件系统裁剪 以模块加入的可以保留,其他的看情况进行裁剪. 1.3 device driver裁剪 里面有些驱动不是我们所需要的,我们的目标板根本不支持那些的功能就可以裁剪掉: 1.3.1 Network device support USB适配器我们并不支持,