要想使用好rtos,做出更加稳定可靠的产品,必须非常清楚底层的调度原理。由于rtos的实时性与可控性,所以只有了解了其核心部分的设计思想,才能用起来得心应手,游刃有余。本文从rt-thread的调度器设计的运行方式上解释一下调度器的行为,从而更加深刻的理解rt-thread操作系统的调度时机。
1.调度是什么?
调度一般就是合理的安排、协调资源,统一指挥去完成一件事,而在操作系统中,线程调度就是有多个就绪优先级的任务,找到最高优先级任务,交给cpu去运行。
rt-thread调度器就是起到判决线程当前的优先级,然后选择当前系统中最高优先级的就绪态的线程交给cpu去管理。
调度又可以细分为两种。可打断调度:关键防止优先级倒置;不可打断调度:先来先服务,不可中断。rt-thread 属于实时操作系统,所以其调度器实现的是可打断的调度,当有更高优先级的线程或者更重要的任务就行,则可以打断当前任务的执行状态,去执行优先级更高的任务。那么此时,调度的时机就非常的关键了。
2.调度怎么实现?
rt-thread在创建任务的时候,会指定任务的优先级,一般来说,每个任务都有自己特定的唯一的优先级。所以内核线程对象中有不同的优先级的任务列表。
如果最大指定为32个优先级,那么可以用32位数据类型表示,每一个bit表示一个优先级就绪的状态。使用位图的优点就是速度快,而且内存占用小。
一般来说,调度去找到最高优先级的任务时,就需要去做判断。如何去找到最高优先级的任务。一般来说,有两种办法:
软件计算
硬件计算
这两种的差别仅仅在于计算效率的问题,本质目的并无差别。
而用软件计算方法寻找最高优先级有两种实现的策略:
1.遍历就绪的队列,找到最小的优先级就绪的队列,寻找的时间不确定,时间复杂度o(n)。
2.采用空间换时间的办法,事先做好一个bitmap
例如系统中最大有8个优先级,那么bitmap如下:
1constrt_uint8_t__lowest_bit_bitmap[]=
2{
3/*00*/0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
4/*10*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
5/*20*/5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
6/*30*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
7/*40*/6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
8/*50*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
9/*60*/5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
10/*70*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
11/*80*/7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
12/*90*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
13/*a0*/5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
14/*b0*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
15/*c0*/6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
16/*d0*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
17/*e0*/5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
18/*f0*/4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
19};
一般每一位代表一个就绪的状态,所以__rt_ffs程序的设计如下
1int__rt_ffs(intvalue)
2{
3if(value==0)return0;
4
5if(value&0xff)
6return__lowest_bit_bitmap[value&0xff]+1;
7
8if(value&0xff00)
9return__lowest_bit_bitmap[(value&0xff00)>>8]+9;
10
11if(value&0xff0000)
12return__lowest_bit_bitmap[(value&0xff0000)>>16]+17;
13
14return__lowest_bit_bitmap[(value&0xff000000)>>24]+25;
15}
如果当前系统的线程状态为0b0110 0000,那么转换成十六进制就是0x60,根据表中的状态此时的最高优先级是5+1=6。所以可以得出系统的优先级,此时计算的复杂度为o(1)。
虽然rt-thread 是支持同等优先级的,但是在具体的业务逻辑的设计中,在使用rtos常用的设计方法中,一般都是要求程序的运行逻辑是可预测的,就是在程序执行的过程中,可以预测到程序下一步的动作。所以rtos中同等优先级,按照时间片轮训的这种方式设计业务逻辑的情况并不多。使用相同优先级会增加系统的业务逻辑的复杂性。
3.什么时候系统做调度?
rt-thread 是抢占式的系统调用,所以系统什么时候去做的调度非常的关键。系统调度行为具体又分为主动调度和被动调度两种。
3.1 任务主动block
当a线程在正常运行时,主动放弃cpu的使用权,比如去执行rt_thread_delay或者等待一个ipc消息时,当前线程会主动放弃cpu资源,此时去系统中寻找已经就绪的最高优先级的线程进行调度。
这种方式应用的场景比较丰富,比如当前线程没有获取到资源时,需让出cpu的使用权,或者事情做完了,主动让出cpu的使用权,这就是系统做调度的时机。
a线程的优先级要高于b线程的优先级,所以在a放弃cpu使用权后,已经就绪的最高优先级线程b就开始执行了。
3.2 被更高优先级的任务唤醒
这种方式就是当比当前运行线程的优先级高的线程处于就绪态时,高优先级的就绪态线程会被唤醒,低优先级线程将暂停运行,此时会调度到比当前线程更高的优先级线程中去。
按照理解a线程是正在运行的线程,此时更高任务优先级的线程c就绪处于就绪状态了。比如创建了一个比a优先级更高的c进程,并startup c线程,此时会执行rt_schedule()将线程切换到优先级更高的c线程。此时a线程运行状态以及处理器寄存器状态压栈,更高优先级的c线程的状态以及处理器寄存器状态出栈,并且开始运行c线程。
3.3 yield放弃cpu使用
首先理解一下什么是yield,解释成让出,放弃比较合理。该出让只针对于同等优先级的线程。
这种情况只适用于a线程的优先级等于b线程的优先级的情况。因为rtt支持同等优先级的方式创建线程,相同的优先级的切换是靠时间片轮询来进行的。所以,当a线程正常运行的时候,如果执行了yield函数,那么只相当于将a线程的时间片消耗完,此时同等优先级的d线程开始运行。 由于在rtos中,需要的是完成任务的确定性与可靠性,同等优先级的情况比较有限,所以这一块应用的不多。
3.4 中断中执行调度
以上的三种属于主动进行调度的过程,其系统的执行流程都是可以预测的,但是中断去执行调度却是比较特殊。是被动调度。
这种方式是在中断中执行调度的,当a线程正常运行时,此时来了一个中断,由于中断的优先级是高于线程的。所以,中断处理事情,如果在中断中执行了调度函数,那么在中断退出后,将直接切换到当前系统中更高优先级的线程去运行。如果如果当前系统的最高优先级还是a,那么中断退出后,执行的最高优先级线程依然是a。若存在线程e线程优先级高于a并且处于就绪状态,此时,中断退出后,切换到e线程去执行。
4.调度做了哪些事情?
系统进行调度的时候做了哪些事情?
第一步:查找当前系统中当前以及就绪的最高优先级的线程,若有高于当前运行系统运行的线程栈则执行线程切换
第二步:关闭中断,将系统当前运行状态以及处理器的寄存器压入栈空间
第三步:找到需要运行的线程的pc指针,并找到栈起始处弹出栈空间中的寄存器状态
第四部:打开中断,执行异常ret,让系统恢复执行
此时,就切换到已经就绪的更高优先级的线程去运行了。
5.总结
rt-thread 线程的调度器是整个系统的灵魂,整个操作系统在运行过程中何时切换线程、什么情况下去处理任务,以及做更高效的业务逻辑的应用都离不开系统调度。掌握了调度器运行的规律,并且合理的使用线程调度时机,可以设计出更加稳定可靠的产品。通过阅读代码,就能预测程序下一步的执行动作。真正的做到手中有粮,心中不慌。
三星S21系列手机有什么特点?
背投电视和液晶电视哪个好
罗德与施瓦茨升级R&S PWC200,以提高5G FR1基站OTA测试精度
酷睿i9头号竞争对手:Ryzen 9性能更强大!
有限状态机变量赋值的一些小概念
RT-Thread操作系统的调度设计原理
为什么手机后置镜头都流行垂直排列
苹果新专利:可以反馈屏幕显示内容纹理的触控笔
小米max2配置最新消息:小米Max2或配有杀手锏功能,支持反向充电
声控开关灯如何利用声音来控制灯?
华为mate10什么时候上市?华为mate10最新消息:在三星note8和iphone8的双面夹击下,华为mate10能否脱颖而出?
我国锂离子电池技术获新突破,电池能量密度将获极大提升
IOTA与奥迪达成合作 共同探讨物联网技术的应用领域
高低压继电器的应用范围和使用方法
介绍二极管导通时间引发开关稳压器的故障
华为818手机节力度空前,P10 Plus新配色开售被秒抢!
台湾启动“半导体射月计划” 加强半导体AI竞争力
华为Mate 30系列宣传海报提前亮相,配麒麟990、双超级快充
如何在iOS 14中默认激活Gmail
利用CS5463芯片可调整温度漂移误差个提高测量精度