如何进行rt-thread的线程调度与管理

今天就跟大家聊聊有关如何进行rt-thread的线程调度与管理,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

rt-thread的线程调度与管理

1.调度是什么?

调度一般就是合理的安排,协调资源,统一指挥去完成一件事,而在操作系统中,线程调度就是有多个就绪优先级的任务,找到最高优先级任务,交给CPU去运行。

如何进行rt-thread的线程调度与管理


 

rt-thread调度器就是起到判决线程当前的优先级,然后去执行当前最高优先级的就绪的线程。

调度又可以细分为两种。可打断调度:关键防止优先级倒置 ;不可打断调度:先来先服务,不可中断。

 

2.调度怎么实现?

在创建任务的时候,指定了任务的优先级,一般来说,每个任务都有自己特定的优先级。所以内核线程对象中有不同的优先级的任务列表。

如何进行rt-thread的线程调度与管理

如果最大指定为32个优先级,那么可以用u32,每一个bit表示一个优先级就绪的状态。使用位图的优点就是速度快,而且内存占用小。

一般来说,调度去找到最高优先级的任务时,就需要去做判断。如何去找到最高优先级的任务。一般来说,有两种办法:

  • 软件计算
  • 硬件计算

这两种的差别仅仅在于计算效率的问题,本质目的并无差别。

而寻找最高优先级的事情也是有两种实现的策略:

1.遍历就绪的队列,找到最小的就绪的队列,寻找的时间不确定,时间复杂度O(n)。

2.采用空间换时间的办法,事先做好一个bitmap

例如系统中最大有8个优先级,那么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
};

 

一般每一位代表一个就绪的状态,所以__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;
}

 

如果当前系统的线程状态为0b0110 0000,那么转换成十六进制就是0x60,根据表中的状态此时的最高优先级是5+1=6。所以可以得出系统的优先级,此时计算的复杂度为O(1)。

虽然rtt是支持同等优先级的,但是在具体的业务逻辑的设计中,在使用RTOS常用的设计方法中,一般都是要求程序的运行逻辑是可预测的,就是在程序执行的过程中,可以预测到程序下一步的动作。所以rtos中同等优先级,按照时间片轮训的这种方式设计业务逻辑的情况并不多。使用相同优先级会增加系统的业务逻辑的复杂性。

 

3.什么时候系统做调度?

RTT是抢占式的系统调用,所以系统什么时候去做的调度非常的关键。系统调度分为主动调度和被动两种。

 

3.1 任务主动block

当A线程在正常运行时,主动放弃CPU的使用权,比如去执行rt_thread_delay或者去等待一个IPC的事件到来时,都会释放CPU进行调度,此时去系统中寻找已经就绪的最高优先级的线程进行调度。

如何进行rt-thread的线程调度与管理

这种方式应用的场景比较丰富,比如当前线程没有获取到资源时,需让出CPU的使用权,或者事情做完了,主动让出CPU的使用权,这就是系统做调度的时机。

A线程的优先级要高于B线程的优先级,所以在A放弃CPU使用权后,已经就绪的最高优先级线程B就开始执行了。

 

3.2 被更高优先级的任务唤醒

这种方式就是当比当前运行线程的优先级高的线程处于就绪态时,会调度到比当前线程更高的优先级线程中去。

如何进行rt-thread的线程调度与管理

按照理解A线程是正在运行的线程,此时更高任务优先级的线程C就绪处于就绪状态了,所以系统的tick函数中判断已经有比线程A更高优先级的线程处于就绪状态,于是执行了rt_schedule()函数执行了系统调度。当前A线程运行状态压栈,更高优先级的C线程的状态出栈,开始运行C线程。

 

3.3 yield放弃cpu使用

首先理解一下什么是yield,解释成让出,放弃比较合理。该出让只针对于同等优先级的线程。

如何进行rt-thread的线程调度与管理

这种情况只适用于A线程的优先级等于B线程的优先级的情况。因为RTT支持同等优先级的方式创建线程,相同的优先级的切换是靠时间片轮询来进行的。所以,当A线程正常运行的时候,如果执行了yield函数,那么只相当于将A线程的时间片消耗完,此时同等优先级的D线程开始运行。

由于在RTOS中,需要的是完成任务的确定性与可靠性,同等优先级的情况比较有限,所以这一块应用的不多。

 

3.4 中断中执行调度

以上的三种属于主动进行调度的过程,其系统的执行流程都是可以预测的,但是中断去执行调度却是比较特殊。是被动调度。

如何进行rt-thread的线程调度与管理

这种方式是在中断中执行调度的,当A线程正常运行时,此时来了一个中断,由于中断的优先级是高于线程的。所以,中断处理事情,如果在中断中执行了调度函数,那么在中断退出后,将直接切换到当前系统中更高优先级的线程去运行。如果如果当前系统的最高优先级还是A,那么中断退出后,执行的最高优先级线程依然是A。若存在线程E线程优先级高于A并且处于就绪状态,此时,中断退出后,切换到E线程去执行。

 

4.调度做了哪些事情?

系统进行调度的时候做了哪些事情?

第一步:查找当前系统中当前以及就绪的最高优先级的线程,若有高于当前运行系统运行的线程栈则执行线程切换

第二步:关闭中断,将系统当前运行的寄存器压入栈空间

第三步:   找到需要运行的线程的PC指针,并找到栈起始处弹出栈中的寄存器状态

第四部:打开中断,执行异常ret,让系统恢复执行

此时,就切换到已经就绪的更高优先级的线程去运行了。

看完上述内容,你们对如何进行rt-thread的线程调度与管理有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注亿速云行业资讯频道,感谢大家的支持。

原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/219556.html

(0)
上一篇 2022年1月2日
下一篇 2022年1月2日

相关推荐

发表回复

登录后才能评论