RTOS任务调度思想汇总

#include"includes.h"
 
int main()
{	
	TargetInit();//所搭载的硬件板物理初始化
	OSInit();//操作系统初始化
	OSTaskCreate(Task0,&StackTask0[Stack_Size-1], Task0Prio,...);//任务0创建
	
	OSStart();//操作系统开始运行
	
	return 0;
}


void Task0(void)
{
	Uart_Printf("Stort OS/n ")//打印类的代码;
	OSTaskCreate(Task1,&StackTask1[Stack_Size-1], Task1Prio,...);//任务1创建
    OSTaskCreate(Task2,&StackTask2[Stack_Size-1], Task2Prio,...);//任务2创建
	for(;;)
	{
		Uart_Printf("Task1/n ")     //打印类的代码;
		OSTimeDly(时间0);//任务切换
	}
}

void Task1(void)
{
	for(;;)
	{
		Uart_Printf("Task1/n ")    //打印类的代码;
		OSTimeDly(时间1);//任务切换
	}
}

void Task2(void)
{
	for(;;)
	{
		Uart_Printf("Task2/n ")    //打印类的代码;
		OSTimeDly(时间2);//任务切换
	}
}
。。。```

Q1:每个任务的延时时间不同,那么每个任务在延时时间到达后被系统调用的先后顺序是如何执行的?
Answer:OS系统的时钟节拍通常是通过一个硬件定时器定时产生周期性的中断来实现的,在这个中断服务函数(Timer_ISR)中会遍历(使用循环来依次判断每个)所有任务的任务控制块成员_延时时间OSTCBDly,并使其减1,并使延时时间清0的任务进入就绪状态;可参考简易OS设计文档中的开头示例;
Q2:根据简易OS示例,当延时时间到达后重新调用任务0时,为什么只执行死循环内的任务代码,而不执行循环外的也在该任务函数内的代码?【★★★疑惑】
Q3:任务创建时的任务堆栈增长方向(所使用的处理器支持的增长方向)的确认?
Answer:例如PC端的处理器支持向上增长(低→高),假设定义任务堆栈数组Uint16 MyTaskStack[64] (该任务堆栈长度128byte)
所以任务堆栈的栈顶地址是 &MyTaskStack[0] ;
【tips】任务堆栈的栈顶指针指向的定义的堆栈数组元素对应的地址总是最低地址,不管增长方向;栈顶指针SP(cpu内部寄存器)指向人工栈数组的首元素(向上增长(递增栈))或最后一个元素(向下增长(递减栈));
在这里插入图片描述
向上:a[0] (sp栈顶指针指向) —> a[2] , 低—>高;
向下:a[0] —>a[2] (sp栈顶指针指向) , 高—>低;
Q4:关于高优先级抢占低优先级调用公共资源时的处理思想?
Answer:对于公共函数采用可重入函数(局部变量)和互斥调用的思想;在这里插入图片描述
Q5:任务控制块两条链表及创建任务时的链表处理,那么链表表头指针如何赋值,以及当前任务控制块指针OSTCBCur的值是多少? 任务调度中对任务控制块指针的处理?
Answer:由于已经定义了任务控制块结构体及所含该任务各个变量,那么在OS系统初始化时(调用OSInit()函数),按照事先配置设置的任务数量进行创建所有的任务控制块,方式是通过一条空白任务控制块链表(都还没分配给任务)和一条任务控制块链表(链表上的任务控制块都已分配任务)来处理;
【tips】1、以后每当系统调用创建任务OSTaskCreate函数创建任务时,系统就通过将空白任务控制块链表表头指针OSTCBFreeList指向的任务控制块分配给该任务,并给该任务控制块各个变量赋值后,再用任务控制块链表表头指针OSTCBList将其链入到任务控制块链表中;这两个链表表头指针指向的数据类型OS_TCB OSTCBFreeList,OSTCBList
2、然后定义个OS_TCB类型的数组OSTCBTbl[任务数+系统任务],然后将 OSTCBList /OSTCBFreeList = &OSTCBTbl[0] ; OSTCBTbl[0].next = &OSTCBTbl[1] ; OSTCBTbl[1].next = &OSTCBTbl[2] ; 。。。依次指向后一个任务控制块结构体类型的数组变量,实现整个链表的连接;
3、假设任务控制块类型定义的数组是:OS_TCB TCBHnb[任务数+系统任务];如果当前正在运行的任务优先级为0,也就是OSTCBCur = &TCBHnb[0] ,一般会直接这么操作;但是μC/OS采用定义另外一个OS_TCB指针类型的数组(该数组通过优先级顺序依次排好,例如该数组为OSTCBPrioTbl [Prio] ,当优先级prio为0时代入,OSTCBPrioTbl [0] = &TCBHnb[0]来获取对应优先级任务的任务控制块指针,也就是对应的定义任务控制块类型数组元素的地址;
4、任务调度需要前后两个任务的任务控制块指针:当前的任务控制块指针已存放于OSTCBCur指针变量中;获取待运行任务的任务控制块指针,可通过查任务就绪表获取任务最高优先级HighPrio(同时判断该优先级跟当前运行任务的优先级不相等,也就不是正在运行的任务,而是将要运行的任务);那么根据3可知OSTCBPrioTbl [HighPrio]中存放的就是待运行任务的任务控制块指针,再定义个指针变量OS_TCB * OSTCBRdy = OSTCBPrioTbl [HighPrio];那么就是OSTCBCur和OSTCBRdy这两个前后任务的任务控制块指针;
5、断点的保护(运行时中止的地方,此时的断点数据(例如CPU内部寄存器组中的控制寄存器PC程序计数器、通用寄存器SP堆栈指针等)要被保护起来;例如:任务1被中止,此时的断点数据保存到任务1的堆栈内;但是要注意的是这个堆栈是任务堆栈,不是内存堆栈(局部变量、函数调用、函数形参等开销),是一个全局变量的数组;
6、正确恢复断点数据的关键:cpu的SP堆栈指针的正确指向;例如:任务1被任务2中止,其中cpu的SP寄存器的值被保存到任务1的任务控制块结构图数组的内部变量OSTCBStkPtr,需要用汇编语句来实现,PC寄存器被保存到定义的任务堆栈内;
7、但是遗憾的是,对于一般处理器并没有对PC指针(PC寄存器)进行操作的出栈和入栈指令,而是靠产生中断,中断向量指向中断服务函数(跳转到ISR时) 时系统自动将断点处原PC指针内容保存到堆栈内,中断结束(中断返回指令)将之前保存原PC指针内容从堆栈内重新赋值给PC指针;
Q6: 任务在切换的时候,SP指针被保存(赋值)到任务的任务控制块中元素OSTCBStkPtr(通过汇编指令),那么PC指针被保存到任务堆栈还是内存堆栈?
【★★★】
Answer:借鉴简易OS设计,在创建任务的时候即调用OSTaskCreate()函数,已将人工定义的任务堆栈栈顶指针(即数组Uint16 MyTaskStack[64] (向上增长))&MyTaskStack[0]赋值给任务控制块元素OSTCBStkPtr;同样的,在进行任务切换时,此时cpu内部寄存器SP保存的堆栈栈顶地址会被保存到被中止的任务的任务控制块元素OSTCBStkPtr中;那么对于每个任务而言,其人工定义的任务堆栈和内存中的堆栈便是同一个堆栈;
1、在创建任务的时候,函数实参主要是三个:任务函数的入口地址、人工堆栈的栈顶地址、任务的优先级;调用后,系统执行到OSTaskStkInit()函数,会根据用户参数初始化任务堆栈;而对于任务在创建时的堆栈初始化内容可参考简易os设计; 当一个任务将要运行时,便通过取得已保存在其任务控制块变量的结构体元素OSTCBStkPtr(已被赋值人工任务堆栈的栈顶地址),将任务堆栈中对应位置赋值(出栈)给cpu内部寄存器组(R0~R12,PC/LR/CPSR),这一段的汇编代码处理可参考简易OS设计p17;
Q7:抢占式调度思想
【tips】最高优先级的任务一旦处于就绪态,立即抢占正在运行的低优先级任务的处理器资源;
1、高优先级任务通过主动挂起/延时,让位低优先级任务执行;
Q8:中断级任务调度思想
1、可剥夺型内核RTOS响应中断的过程,当收到中断请求时,cpu若处于中断允许状态(开中断)会响应中断,中止当前任务,按中断向量(一般为中断服务函数函数名)跳入中断服务函数ISR执行,当ISR执行完后,RTOS会判断情况进行一次任务调度运行优先级最高的就绪任务,不一定返回原断点继续执行被中断的任务;
2、中断嵌套:后产生的高优先级中断会打断低优先级的中断的ISR的执行;中断挂起:被高优先级中断打断的低优先级的ISR会被挂起,等待高优先级中断的ISR执行完继续执行;
3、中断级任务调度的条件:中断嵌套层数= 0、调度器上锁次数 = 0、就绪表最高优先级任务 ≠ 当前正在运行任务的优先级 才会进行任务切换;当仍有中断嵌套时,先执行完所有中断后才能返回到任务中,不满足条件不能进行任务切换,是通过OSIntExit()函数进行判断的,在该函数内满足条件时调用OSIntCtx()函数;
4、cpu的断点保护是放在中断的ISR里处理的;
5、中断的断点保护数据是保存在中断模式下的堆栈中;
6、示例任务1正在执行,被中断打断,这时的现场保护中的PC指针的值(断点地址)入栈到中断模式下的堆栈里,当退出中断后进行任务切换执行函数OSIntCtx(),如果有更高优先级的任务2,不返回原任务1断点处继续执行的话,那么PC指针的值肯定得等于函数名OSIntCtx,所以这时候要有个返回地址的更换(pc寄存器值的更换),这个通过汇编进行操作;当中断结束后,根据PC指针的值便开始执行OSIntCtx()函数进行任务的切换,然后将之前替下来的任务1的PC指针(断点地址),堆栈栈顶指针SP等断点数据入栈到任务1自己的堆栈里进行保存,然后将待执行当前已就绪的最高优先级任务2的SP堆栈栈顶指针赋值给SP寄存器,其他已经在创建任务2时初始换任务堆栈过的PC指针等内容赋值到cpu相关寄存器,进入任务2的运行环境;
【注意】进入中断时的堆栈跟任务里人工定义的堆栈不是一回事,必须等待中断ISR完全结束后才能进行任务切换以及堆栈的操作;刚退出中断时的运行环境的cpu寄存器的值已经由执行中断时的堆栈内容(退出中断其内容释放掉了)切换到任务1的任务堆栈保存的内容
Q9:任务挂起/恢复处理
1、挂起任务就是通过OSTaskSuspend()函数将需要被挂起的任务从任务就绪表中删除,然后再进行任务调度(切换);可以挂自己也可以挂别人;
2、通过OSTaskResume()函数可以将经过OSTaskSuspend()挂起或者OSTimeDly函数延时的任务立即从任务就绪表中置位恢复就绪,并将已被延时任务的延时变量OSTCBDly清0,即恢复任务,然后再进行任务调度(切换);

借用名言:“工程师在选用多任务操作系统前要先看看自己的项目是不是真的需要,如果你的任务可拆分性较差(耦合程度高,业界提倡高内聚低耦合),拆分后各任务间有N多在同步上的问题和复用资源问题,那么算了,还是不要用多任务操作系统;不要有事没事就觉得多任务好,多任务是通过降低实时性来换取软件开发的独立性,不要被实时多任务操作系统的实时给骗了,这个实时是相对于其他非实时性多任务操作系统来讲的,实时性最高的当然是你自己编写的单任务程序。

持续更新中

。。。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
RTOS(Real-Time Operating System)任务调度是实时操作系统中非常重要的一部分,它负责协调多个任务的执行,使得它们能够按照一定的优先级、时间片或其他算法在 CPU 上运行。RTOS 任务调度的实现方式可以分为两种:抢占式和非抢占式。 1. 抢占式任务调度 抢占式任务调度是指当一个优先级更高的任务就绪时,它可以抢占当前正在运行的任务并立即执行。在抢占式任务调度中,每个任务都有一个优先级,调度器会根据任务的优先级来决定其执行顺序。当一个高优先级任务就绪时,调度器会立即抢占当前正在执行的任务并执行高优先级任务。抢占式任务调度实现的关键在于中断处理,当一个中断发生时,调度器会暂停当前正在执行的任务并执行中断服务程序,然后在中断服务程序执行完毕后恢复原来的任务继续执行。 2. 非抢占式任务调度 非抢占式任务调度是指任务只有在自己执行完毕或主动放弃 CPU 时才会被其他任务抢占。在非抢占式任务调度中,每个任务也有一个优先级,但是任务的执行顺序只能由任务本身控制。当一个任务执行完毕或主动放弃 CPU 时,调度器会根据任务的优先级来选择下一个任务执行。非抢占式任务调度实现的关键在于任务的协作,每个任务需要在执行过程中主动让出 CPU,以便其他任务得到执行的机会。 无论是抢占式还是非抢占式任务调度,都涉及到任务的上下文切换,即保存当前任务的上下文,以便在任务再次执行时恢复其状态。上下文切换需要保存任务的 CPU 寄存器、堆栈指针等信息,并修改当前任务的状态(如就绪、阻塞等),然后切换到下一个任务的上下文。任务的上下文切换是一个比较耗时的操作,因此在任务调度的实现中需要尽量减少上下文切换的次数,提高任务的执行效率。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUR0608

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值