20 KiB
任务调度策略
CosyOS的任务调度类型包括 抢占式调度 、 时间片轮转调度 。不同优先级的任务实行抢占式调度,
相同优先级的任务按先入先出实行时间片轮转调度,“入”指的是加入到任务队列,即启动任务。
CosyOS的任务调度时机分为 定时调度 与 临时调度 。
- 定时调度
在每个系统滴答周期,SysTick_Handler/定时器0中断,都会触发一次任务调度。 - 临时调度
1. 当前任务被阻塞、挂起、超时、停止、删除时,会立即触发任务调度。
2. 恢复任务时,如果恢复后该任务可以运行并且它的优先级高于当前任务的优先级,会立即触发任务调度。
3. 设置任务优先级时,如果该任务可以运行并且它的新优先级高于当前任务的优先级,会立即触发任务调度。
4. 设置阻塞时间为零或清除阻塞时,该任务会转为就绪状态,如果它的优先级高于当前任务的优先级,
会立即触发任务调度。
5. 归还互斥信号量完成时,如果发生过优先级继承(该任务的优先级被提升),会恢复该任务的原优先级,
并立即触发任务调度。
6. 用户通过调用服务,直接触发的任务调度,包括任务级任务调度和中断级任务调度。
CosyOS的任务调度策略可最大程度的实现尽可能实时高效的任务切换,使CosyOS的实时性更为优异。
任务优先级分组
您可在系统配置文件中配置任务优先级分组。
共有六个分组可供选择,8级、16级、32级、64级、128级、256级,您也可自定义级数。
如您配置的分组为8级,则最高优先级为7级,最低优先级为0级。
时间片模式
CosyOS有三种时间片的定义模式, 全局时间片 、 算法时间片 、 自定义时间片 ,需在系统配置文件中定义。
1. 全局时间片 所有任务采用统一的时间片。
2. 算法时间片 不同优先级的任务时间片不同,采用一个算法公式来描述。
3. 自定义时间片 不同优先级的任务时间片不同,用户自定义各优先级的时间片。
全局时间片简单高效,时间片轮转调度性能为最佳;算法时间片、自定义时间片可实现不同优先级任务精细的时间片控制。
时间片的取值范围:1~65535,单位为滴答周期。
安全运行时
安全运行时是CosyOS的安全关键技术之一,是为了防止某任务长期独占或超时使用处理器。当任务累计运行时间超过安全运行时,仍不能被比它优先级低的任务所切换,则进入超时状态并放弃处理器的使用权,直到系统空闲任务运行时才能恢复。
安全运行时的取值范围:0~65535,单位为时间片,0为无限长。
所有空闲任务无法应用安全运行时,创建任务时,安全运行时输入0即可。
安全运行时的应用技巧:在开发初期,所有任务的安全运行时可设置为0,待开发测试阶段中后期,再根据任务各自的特点及测试经验合理的调整为有效值。
软件定时器
CosyOS的软件定时器分为三类,延时定时器、定时中断定时器、定时查询定时器。
- 延时定时器 每个任务各有一个,用来在此任务中实现阻塞时间计数。
- 定时中断定时器 每个定时中断任务/钩子绑定一个定时中断定时器,至多可以有64个,定时器ID从0开始,用户通过调用API进行定时操作后,定时器将自动开始计数。
- 定时查询定时器 每个定时查询任务/钩子绑定一个定时查询定时器,至多可以有64个,定时器ID从0开始,用户通过调用API进行定时操作后,定时器将自动开始计数。
支持任务/钩子
一、系统任务
由操作系统自主创建并自动启动的任务。
1. 任务管理器(Taskmgr) 优先级固定为最高优先级。
2. 调试任务(Debugger) 优先级固定为最高优先级,用做命令行和任务管理器的串口接收解析。
3. 启动任务(Starter) 优先级固定为比最高优先级低一级,通过调用启动钩子来启动用户任务,而后删除自身。
4. 系统空闲任务(Sysidle) 优先级固定为最低优先级0级,是系统级的空闲任务。
二、系统钩子
由操作系统自主创建并自动调用的函数,并由用户来写代码。
1. 初始化钩子(init_hook) 在主函数中首先被调用,适用于初始化时钟、GPIO、寄存器等工作。
2. 启动钩子(start_hook) 在启动任务中被调用,用于启动用户任务。用户可在启动钩子内调用API来启动用户任务。
3. 滴答钩子(tick_hook) 每个系统滴答周期,在SysTick_Handler/定时器0中断中都会被调用一次,适用于每滴答周期/秒/分/时/...做一次的工作。
4. 空闲钩子(idle_hook) 在系统空闲任务中被调用,用户可添加自己的代码做一些没有实时性要求的工作。
5. 中断服务钩子(pendsv_hook)
6. 全局变量钩子(gvar_hook) 用于更新全局变量的副本。
三、定时任务/钩子
包括定时中断任务/钩子和定时查询任务/钩子,其实质都是软件定时器中断。
1. 定时中断任务/钩子 由用户设定定时时间,当定时器溢出时,系统将自动恢复/调用与其绑定的任务/钩子并自动重复定时(如果您已开启定时器的自动重装载)。
2. 定时查询任务/钩子 由用户设定定时时间,当定时器溢出后,系统在每个滴答周期都会查询用户定义的事件,若事件为真,系统将自动恢复/调用与其绑定的任务/钩子并自动重复定时(如果您已开启定时器的自动重装载)。系统初始化后,所有定时查询定时器的值均为零,相当于已经溢出,系统已经开始查询用户定义的事件了。
3. 任务与钩子的区别 由于定时中断钩子、定时查询钩子都是在SysTick_Handler/定时器0中断中被调用,所以只有相对精简的代码才适合创建钩子,耗时长的代码应创建为任务。再有,钩子相当于比任务具有更高的优先级,可更及时的被执行。
四、一般用户任务
对于一般用户任务,只要优先级为0级,就是空闲任务。用户空闲任务内都要有阻塞事件,以保证所有空闲任务都有机会被运行。
最高优先级固定分配给任务管理器和调试任务,用户任务不要使用。
任务状态
CosyOS的任务状态较传统RTOS划分更为详细,以便用户在使用任务管理器监控任务时,能够更加精准的判断各任务的运行情况。
具体划分如下:
注:[]为在任务管理器中显示的任务状态
- 运行状态[RDY] 任务正在运行。
- 就绪状态[RDY] 任务已经准备就绪,可以参与任务调度并运行了。
- 延时阻塞状态[DLY] 由阻塞延时而导致任务进入阻塞状态,直到延时时间结束转为就绪状态。
- 超时阻塞状态 :
1. 二值信号量阻塞状态[BIN] 由二值信号量等待或获取而导致任务进入阻塞状态,直到成功或超时转为就绪状态。
2. 互斥信号量阻塞状态[MUT] 由互斥信号量获取而导致任务进入阻塞状态,直到成功或超时转为就绪状态。
3. 计数信号量阻塞状态[SEM] 由计数信号量获取而导致任务进入阻塞状态,直到成功或超时转为就绪状态。
4. 等待标志组阻塞状态[GRP] 由事件标志组等待而导致任务进入阻塞状态,直到成功或超时转为就绪状态。
5. 接收私信阻塞状态[ TM] 由接收私信而导致任务进入阻塞状态,直到收到私信或超时转为就绪状态。
6. 接收飞信阻塞状态[FET] 由接收飞信而导致任务进入阻塞状态,直到收到飞信或超时转为就绪状态。
7. 接收邮件阻塞状态[MAL] 由接收邮件而导致任务进入阻塞状态,直到收到邮件或超时转为就绪状态。
8. 接收消息阻塞状态[MSG] 由接收消息而导致任务进入阻塞状态,直到收到消息或超时转为就绪状态。 - 浮动状态[RDY]
浮动状态是临时的就绪状态。
超时阻塞的任务,当阻塞条件解除时,会转为浮动状态;
浮动状态的任务,当阻塞条件具备时,会转回阻塞状态。
浮动状态在任务管理器中显示为就绪状态。 - 超时状态[OSR]
当任务累计运行时间超过安全运行时,仍不能被比它优先级低的任务所切换,则进入超时状态并放弃处理器的使用权,直到系统空闲任务运行时才能恢复。 - 挂起状态[SPD]
任务暂停运行,除任务状态变为挂起外,任务的其它相关信息和数据均保持不变,当恢复任务后,可继续挂起时的断点运行。
运行状态的任务被挂起,恢复后为就绪状态;其它状态的任务被挂起,恢复后为原状态。 - 停止状态
任务停止运行,不可恢复。
1. 任务栈溢出停止[!OF] 由任务栈溢出而导致任务停止运行。
2. 任务栈重分配失败停止[!RF] 由任务栈重分配失败而导致任务停止运行。 - 删除/未启动状态(Deleted)
任务被删除或未启动,若想再次运行该任务,必须重新启动该任务。

—————————— CosyOS-任务状态图 ——————————
中断优先级
无论您的MCU是哪种内核,CosyOS都会占用两个中断做为系统中断:
- SysTick_Handler/定时器0中断(中断优先级固定为最低优先级)
- PendSV_Handler/其它替代中断(中断优先级固定为最低优先级)
CosyOS 是全局不关总中断和用户中断,在进入任务临界区时,会关闭上述两个系统中断或屏蔽最低优先级的中断,
★★★ 只要您的用户中断不是最低优先级就可实现零中断延迟 ★★★
注意事项:
- 为实现零中断延迟,用户中断应尽量避免使用最低优先级。
- 用户中断应遵循快进快出的原则,仅执行最为紧急的工作,而把可以稍缓的工作转为在任务中执行(中断服务任务)。
- 用户中断的最大执行时间应远小于系统滴答周期,这将促使整个系统更加良性的运行。
相关资料:
零中断延迟原理
临界区
临界区分为“任务临界区”与“全局临界区”。
任务临界区 任务级的临界区保护,仅关闭系统中断(SysTick、PendSV);
全局临界区 系统级的临界区保护,会关闭总中断。CosyOS内核中从来不会进入全局临界区,提供此项服务只是为了便于用户对全局公共资源和程序过程的保护。
两种临界区都支持嵌套功能,最大嵌套深度:255。
CosyOS-II 中断服务调用注意事项
凡事都是辩证的,有一利必有一弊。CosyOS-II 在实现零中断延迟的同时,在服务的调用上,也必然会存在一些限制条件。
- 一、用户中断中写全局变量
当用户在中断中调用iWriteGVar(gv, lv)、iWriteGAry(gp, lp, size)、iWriteGStr(gs, ls)写一个全局变量时,局部变量要做为专用。
1、当服务调用以后,该局部变量在本次中断中不应该被改变,原因是写全局变量为挂起服务;
2、如果某次进入中断以后,该局部变量被更新,必须再次调用相同的服务写同一个全局变量。 - 二、用户中断中发送邮件
当用户在中断中调用iSendMail(mbox, mail)发送邮件时,mail所指向的局部邮箱要做为专用。
1、当服务调用以后,该局部邮箱在本次中断中不应该被改变,原因是发送邮件为挂起服务;
2、如果某次进入中断以后,该局部邮箱被更新,必须再次调用相同的服务发送邮件至同一个邮箱。 - 三、用户中断中接收邮件
1、当用户在中断中调用iRecvMail(mail, mbox)接收邮件时,其它不同优先级的中断、任务、滴答,均不可再接收这一邮箱中的邮件。也就是说,所有任务和滴答可以接收来自同一个邮箱中的邮件;所有相同优先级的中断可以接收来自同一个邮箱中的邮件。
2、当邮箱的数据类型为非原子访问类型时,中断最好不要发送邮件给中断(除非这个中断的发生频率足够低,可确保在PendSV中最终完成发送以后,这个中断才会再次发生),否则会有出错的风险。 - 四、用户中断中接收飞信
1、当用户在中断中调用iRecvFetion(tbox)接收飞信时,其它不同优先级的中断、任务、滴答,均不可再接收这一信箱中的飞信。也就是说,所有任务和滴答可以接收来自同一个信箱中的飞信;所有相同优先级的中断可以接收来自同一个信箱中的飞信。 - 总结
1、条件一和条件二其实算不上什么条件,只要注意局部变量和邮箱做为专用就可以了。
2、条件三和条件四只需稍加留意即可。
CosyOS-II 在临界区中或关闭总中断时调用服务注意事项
一、在任务中,以下服务不支持在临界区中(包括任务临界区和全局临界区)或关闭总中断时调用:
启动任务:uStartTask(task, status)、uStartTask_Ready(task)、uStartTask_Suspend(task)。
互斥信号量获取:uTakeMut(mut, tc)。
二值信号量等待:uWaitBin(bin, tc)。
二值信号量获取:uTakeBin(bin, tc)。
计数信号量获取:uTakeSem(sem, tc)。
接收飞信:uRecvFetion(tbox, tc)。
接收私信:uRecvTaskMsg(tc)。
接收邮件:uRecvMail(mail, mbox, tc)。
接收消息:uRecvMsg(que, tc)。
等待标志组:uWaitFlagGroup(group, tc)。
二、其它会导致任务调度/切换的服务,虽支持在临界区中或关闭总中断时调用,但也必须等到最终退出临界区或开启总中断后才能切换任务。
任务管理器
在您配置好相关硬件和软件后,任务管理器便可以使用了。您可使用某一款串口调试助手,作为任务管理器的输出窗口。

—————————— CosyOS-任务管理器 ——————————
相关名词解释
Name:任务名称
TID:任务ID
PRI:任务优先级
STA:任务状态
CPU:CPU使用率
RAM:任务栈大小(字节数)
其中,RAM项需着重说明一下。
示例1:32B/s64B,系统采用每调度监控(假定入栈)计算出的在概率上必然存在的最大任务栈占用为32Bytes,任务栈已“静态创建”了64Bytes。
示例2:32B/m64B,系统采用每调度监控(假定入栈)计算出的在概率上必然存在的最大任务栈占用为32Bytes,任务栈已“malloc”分配了64Bytes。
示例3:32B/r40B,系统采用每调度监控(假定入栈)计算出的在概率上必然存在的最大任务栈占用为32Bytes,任务栈已“realloc”分配了40Bytes。
每调度监控:是每次任务调度时都会监控,并假定本次会切换任务、需要现场保护,计算当前任务需要多大的任务栈。这种假定在概率上必然早晚都会发生,但不一定是什么时候(也许是即将发生、也许是一万年以后、也许是时间的尽头),从而提前预判任务栈溢出的风险。
PSVFIFO:中断挂起服务缓存(PendSV_FIFO)的深度,如果您已开启PendSV_FIFO监控,会显示此项,历史最大值/设置值。
Task-PC:任务PC监控,如果您已开启任务PC监控,会显示此项。
当任务管理器被调度运行时,上一个被切换的任务入栈的PC值。当有任务出现代码运行卡死或死循环时,此功能可用来帮助用户锁定具体位置,以便查找BUG。注意,PC值为下一条汇编指令的地址。
某任务出现代码运行卡死或死循环的表现:任务始终为就绪状态,且占用较高的CPU使用率,比它优先级低的任务一直都没有机会运行。
SysTick:系统滴答时间统计,如果您已开启系统滴答时间统计,会显示此项。
注意:系统滴答时间是SysTick_Handler运行的平均时间,并非是任务切换时间。
Alarm:报警信息,如果有报警,会显示此项。
omq:消息队列溢出。
otq:任务队列溢出;
osr:安全运行时超时。
otp:任务优先级超出范围。
rts:任务栈重分配发生,虽然成功了,但仍要提醒用户任务栈定义小了。
任务栈重分配机制被设计用来在正式的产品中抵御任务栈溢出的风险,用户在开发测试阶段应避免这种情况的发生。
ots:在概率上、在未来必然会发生的任务栈溢出(虽然现在可能并未发生)。
Fault:故障信息,如果有故障,会显示此项。
mmn:消息节点内存分配失败;
mtn:任务节点内存分配失败;
mts:任务栈内存分配失败;
rts:任务栈内存重分配失败;
ots:任务栈溢出;
fst:启动任务失败;
erm:中断中接收消息错误(中断中不可接收来自动态消息队列的消息)。
opd:中断挂起服务缓存溢出。
任务管理器现支持如下命令:
- taskmgr,启动任务管理器。
- exit,退出任务管理器。
- taskmgr /s=...,任务管理器更新速度,取值范围:[50~5000]ms。
注意:命令仅支持小写字母,结尾必须加回车换行(\r\n)。
私信
私信是CosyOS独创的一种任务间通讯方式,可用来实现信号、事件、消息等功能。
私信的本质其实就是任务线程的形参,功能上相当于消息邮箱。它的主要优势在于易用性,私信参数(数量、名称、类型)可随意定义,较传统的消息邮箱应用更为便利。缺点是私信只能发给任务,中断中不能发送私信。
私信报警
当您使用私信时,编译后会有报警提示。具体表现为,您定义的每个私信参数都对应一个未引用的局部变量(参数),名称为私信参数的名称后面加_。如下方示例,某个任务创建了私信,共有三个参数p、a、b,则相应的会有报警p_、a_、b_,三个未引用的局部变量(参数),但这并不会影响私信功能的正常使用,用户可不必理会或在C51、C251、C/C++标签页屏蔽掉相应的报警。

8051内核,报警为warning C280;
80251内核,报警为warning C47;
Cortex-M内核,报警为warning: #177-D。
当私信的创建模式为智能创建模式时:
8051内核还另有报警R3_、R5_、R7_,三个未引用的局部变量;
80251内核还另有报警DR0_、DR4_、R11_,三个未引用的参数。