leonling
发表于 2023-7-18 10:15:33
测试了一下CosyOS-STC8-TEST-V1.1.1-20230716,将其taskmagr打开,即设SYSCFG_DEBUGGING 1,得到如下测试结果:
接收←Co3954
syOS Taskmgr 100.00%
Name TID TPL STA CPU RAM
Taskmgr 1 7 RDY 0.26% 7B/m23B
Debugger 4 7 SPD 0% 7B/m23B
task_C 7 4 DLY 0.08% 14B/m128B
task_A 5 3 BIN 55.57% 9B/m128B
task_B 6 2 RDY 56.07% 9B/m128B
Sysidle 3 0 RDY 0% 7B/m23B
Task-PC: 54C7.
SysTick: 21.31us.
接收←Co3954
syOS Taskmgr 100.00%
Name TID TPL STA CPU RAM
Taskmgr 1 7 RDY 0.26% 7B/m23B
Debugger 4 7 SPD 0% 7B/m23B
task_C 7 4 DLY 0.08% 14B/m128B
task_A 5 3 RDY 57.57% 9B/m128B
task_B 6 2 BIN 60.07% 9B/m128B
Sysidle 3 0 RDY 0% 7B/m23B
Task-PC: 5745.
SysTick: 21.34us.
发现一是性能数据从1万多降到了3954(接收←Co3954),二是TaskA和B的CPU占用率加起来超过100%。
请问这两个现象是正常的吗?
CosyOS
发表于 2023-7-18 13:32:41
leonling 发表于 2023-7-18 10:15
测试了一下CosyOS-STC8-TEST-V1.1.1-20230716,将其taskmagr打开,即设SYSCFG_DEBUGGING 1,得到如 ...
开启任务管理器后,测试分数急剧下降是正常现象,因为任务管理器虽是经过特别的优化处理非常高效,但相较如此简单的测试任务,还是会占用更多的时间。
再者,开启任务管理器后,会增加两个任务,Taskgmr、Debugger,使得每次任务调度时,会从Taskgmr开始查询,而未开启任务管理器时,会从task_C开始查询。
还有CPU利用率的统计、任务PC、系统滴答时间统计等,都会占用不少时间,最终导致测试分数下降。
CPU利用率超过100%的问题,在正常使用时是不会出现的,只有在这种极限测试下才会出现。
但这是否是因为算法上存在bug还不好说,下一步我会针对这个问题做进一步的分析和测试,并在未来公布结论。
感谢你的反馈,希望大家都能多多测试,多提异议!
CosyOS
发表于 2023-7-19 22:14:12
本帖最后由 CosyOS 于 2023-9-10 12:18 编辑
CosyOS 的三大技术特点
今天,我们从实际出发,来谈一谈CosyOS的技术特点,而不是采用“假大空”、“高大上”的方式,如“某某风格”、“面向某某”,等华丽的词藻。
一:内核对象静态创建
CosyOS的内核对象,一般采用静态创建方式,这样可以一步完成创建,简单易用。
调用API操作内核对象时,一般均输入内核对象的名称,而非句柄指针,具体请参考API相关说明。
二:所有内核全局不关总中断(零中断延迟)
此前已做过多次论述,此处不再详述,只再重复一句话:
★★★只要您的用户中断不是最低优先级就可实现零中断延迟★★★
三:独家技术实现系统服务的可重入,使51彻底摆脱可重入栈全面提速
系统服务分为滴答服务、任务服务、中断同步服务、中断异步服务等,各类服务分别调用互不干涉。
首先您需知道,系统滴答、PendSV、任务临界区中,三者之间是互斥访问的。详情请参阅《CosyOS-所有内核全局不关中断原理.md》。
滴答服务:仅在系统滴答/定时器0中断中调用,所以不会重入。
中断异步服务:服务的结构体指针入中断服务栈后在PendSV中执行,所以不会重入。
中断同步服务:采用特别的优化,实现了服务函数的可重入(形参和局部变量均为寄存器变量),
再配合互斥访问机制,实现了全局资源(全局变量、消息邮箱、消息队列)的互斥访问。
详情请参阅源码,关于互斥访问机制,请参阅《CosyOS-所有内核全局不关中断原理.md》。
任务服务:
任务服务大体上可分为三类,需重点讲解一下:
1:本地代码
do{ \
进入任务临界区; \
本地执行服务; \
退出任务临界区; \
}while(false)
由于是本地独立的代码,并非调用函数,所以不存在重入的问题。
CosyOS仅在两种情况下才会采用本地代码:
(1):对于51单片机来说,调用函数的代码量(仅指调用过程,即传参)比本地代码的代码量还要大(如定时中断、定时查询);
(2):如果不采用本地代码将无法实现服务功能(如发送私信)。
2:调用服务函数(无返回值)
do{ \
进入任务临界区; \
调用服务函数(); \
}while(false)
由于是在进入任务临界区之后才调用的服务函数,所以服务函数是不会重入的,也不需要可重入。
服务函数在返回时会自动退出任务临界区。
3:调用服务函数(有返回值)
( \
__enter_critical() || true ? 调用服务函数() : 返回一个其它值 \
)
采用三目运算,巧妙的实现了在进入任务临界区之后再调用服务函数并返回值。三目运算的优势在于可进行常量优化。
__enter_critical()返回后,|| true,其结果必然为真,编译后将不再做条件判断,而是直接调用服务函数并返回值。“为假时返回一个其它值”也会被优化掉。
下面以启动任务为例加以说明:
/* API */
#define /*|ecode|*/uStartTask(task, state)sUSV_StartTask(&vTaskHandle_##task, state)
/* 服务 */
#define sUSV_StartTask(thdl, state) \
( \
__enter_critical() || true ? __start_task(thdl, !state ? __READY__ : __READY__ | __SUSPENDED__) : false \
)
C51应用示例:
uStartTask(sensor_task, __READY__);
编译后:
C:0xC026 12DA0B LCALL enter_critical(C:DA0B)
C:0xC029 7E14 MOV R6,#0x14
C:0xC02B 7F06 MOV R7,#0x06
C:0xC02D E4 CLR A
C:0xC02E FD MOV R5,A
C:0xC02F 12D4E8 LCALL _start_task(C:D4E8)
可见,编译后为三步:
1、调用enter_critical();
2、传参(R6、R7为任务句柄的指针,R5为任务状态);
3、调用__start_task()。
优化掉了其它无用的环节,厉害了吧?{:lol:}
对于这一类服务又分为两种情况:
一、在服务函数内会一气呵成,最后退出任务临界区并返回常量或寄存器变量。
。。。。。。
进入任务临界区 → 本地执行服务 → 退出任务临界区并返回
。。。。。。
这一类服务函数,在退出任务临界区之前是不会重入的,在退出之后是可重入的。
由于这一类服务比较简单,不再举例说明。
二、会中途退出任务临界区并触发任务调度,当再次回到当前位置时(任务切换回来时),会再次进入任务临界区并继续执行服务,而后再退出任务临界区并返回常量或寄存器变量。
。。。。。。
第一阶段:进入任务临界区 → 服务入 → 退出任务临界区并触发任务调度
PendSV中切换任务(可能)
第二阶段:再次进入任务临界区 → 继续执行服务 → 退出任务临界区并返回
。。。。。。
这一类服务是含有超时机制的服务,共包括八大服务:等待或获取二值信号量、获取互斥信号量、获取计数信号量、等待标志组、任务中接收飞信、任务中接收私信、任务中接收邮件、任务中接收消息。
具体过程:(vTASKING 为当前任务节点的指针)
1、在第一阶段,已将内核对象指针存入vTASKING->ptr;
2、而后在PendSV中,会访问非延时阻塞(超时阻塞)状态的各任务节点->ptr,以调整任务状态、查找最高优先级的就绪任务;
3、在第二阶段,再次进入任务临界区后,将vTASKING->ptr赋值给内核对象指针变量,继续执行服务。
这一类服务函数,在第一阶段是不会重入的,第二阶段也是不会重入的,但服务函数可能已经重入了(因为可能切换过其它任务)。
对于51来说,内核对象指针可能已经改变(因为内核对象指针变量有可能是内存变量,服务重入后指针将改变),
所以需要将vTASKING->ptr再次赋值给内核对象指针变量以恢复原值,然后再执行服务。
下面以获取二值信号量为例加以说明:
/* API */
#define /*|res|*/uTakeBin(bin, tc)sUSV_TakeBin(bin, tc)
/* 服务 */
#define sUSV_TakeBin(bin, tc) \
( \
__enter_critical()||true?__take_bin((bool *)&bin, tc):false \
)
/* 服务函数 */
bool __take_bin(bool _XDATA_MEM_ *p, tDelay tc) // p为二值信号量指针变量,对于51来说,_XDATA_MEM_为xdata
{
if(*p) goto __RET_TRUE;
if(!tc) goto __RET_FALSE;
vDELAY_STMR = tc; // 赋值超时时间给延时定时器
vTASKING->state = __BLOCKED__; // 赋值当前任务状态为阻塞
vTASKING->blocktype = __BINARY__ | sizeof(bool); // 赋值阻塞类型为二值信号量阻塞,指针为单字节数据类型
vTASKING->ptr = p; // 将二值信号量指针存入vTASKING->ptr
suTaskScheduler; // 退出任务临界区并触发任务调度,当再次回到当前位置时,服务函数可能已经重入,对于51来说,内核对象指针可能已经改变
p = (bool *)vTASKING->ptr; // 所以需要将vTASKING->ptr重新赋值给二值信号量指针变量
if(*p) goto __RET_TRUE; // 再次访问二值信号量指针变量
__RET_FALSE:
mExitCritical;
return false;
__RET_TRUE:
*p = false; // 再次访问二值信号量指针变量
mExitCritical;
return true;
}
可见,通过上述方法,可完美实现这一类服务的可重入,使51彻底摆脱可重入栈全面提速。
上述方法虽然会导致251、Arm等内核执行服务的性能稍有下降,但对于51来说,性能却有质的提升。
而且此法还适用于任何MCU和编译器,都能确保安全可靠和卓越性能。
对于51来说,在类型定义(os_redef.h)和服务函数中,内核对象指针变量一律指定指针域为xdata,这样操作不仅可显著的减少内存占用和代码量,还可提速。
对于51来说,用户需注意,内存模型一定要选择Large。
以上就是CosyOS的三大技术特点。(完)
神农鼎
发表于 2023-7-19 23:13:47
感谢{:4_196:},32位8051世界又向前跨了一大步
leonling
发表于 2023-7-21 10:41:06
假设一个任务是发送报文的,然后阻塞延时等待回应,根据回应的信息,这个任务可能会被立即激活发送下一个报文,也可能被设置等待更长的时间来让系统完成一些工作。这就要求可以激活阻塞延时任务的API和更改指定任务延时的API,但我没有在手册中看到。请问有这样的API吗?
CosyOS
发表于 2023-7-21 13:52:40
本帖最后由 CosyOS 于 2023-7-21 14:42 编辑
leonling 发表于 2023-7-21 10:41
假设一个任务是发送报文的,然后阻塞延时等待回应,根据回应的信息,这个任务可能会被立即激活发送下一个报 ...
哦,我明白你的意思了,就是操作指定任务,让它立即结束阻塞延时状态并转为就绪状态,或更改它的阻塞延时时间,这些服务现在确实没有。
你的提议非常好,我考虑在下一版中,增加这些服务。
对于“结束阻塞延时”的功能,可采用另一方法来实现。创建一个二值信号量,初值为false,发送报文后获取它(进入超时阻塞状态),解析完成后再释放它。
CosyOS
发表于 2023-7-26 21:23:56
本帖最后由 CosyOS 于 2023-7-26 21:39 编辑
CosyOS 最新版 V3.3.3-beta 发布了!
版本更新记录
V3.3.1-beta
取消了报警信息:rts(任务栈重分配发生),因为任务栈重分配机制被定义为一种标准功能、常规手段。
V3.3.2-beta
调整了互斥信号量,使其均支持递归,最大嵌套深度:255。
也就是说,从V3.3.2-beta版开始,互斥信号量就是递归互斥信号量。
释放互斥信号量,由返回结果调整为返回错误码。
优化并调整了系统配置文件和MCU配置文件。
V3.3.3-beta
新增服务:“设置延时”、“清除延时”,支持在任务、滴答、中断中调用。
设置延时:设置指定任务的阻塞延时时间,前提是该任务当前为阻塞延时状态。
清除延时:清除指定任务的阻塞延时状态,并使其就绪。
CosyOS最新版下载(内核版本 V3.3.3-beta,附带所有说明文档):
所有说明文档均已更新!!!
CosyOS的套路有别于传统的RTOS,花一些时间来了解这些是非常必要的,《技术要点.md》有助于用户对CosyOS的了解。
《开发流程.md》可使读者详尽的了解CosyOS的开发流程及注意事项,是非常重要的。
leonling
发表于 2023-7-27 07:47:16
CosyOS 发表于 2023-7-26 21:23
CosyOS 最新版 V3.3.3-beta 发布了!
非常棒,这样我的更改任务延时值的问题就解决了。{:4_250:}
leonling
发表于 2023-7-27 09:08:16
假设一个任务是处理报文的,处理好了检测下有没有新报文,没有就uSuspendTasking。报文到了有中断,所以在中断中iResumeTask(task, svid)。由于CosyOS全局不关中断,如果在uSuspendTasking的过程中新报文的中断来了,会不会造成这个任务最终被挂起而错过了新报文?
另外,手册中关于svid的解释很少,调用时该如何设置这个值呢?
CosyOS
发表于 2023-7-27 13:31:35
本帖最后由 CosyOS 于 2023-7-27 14:00 编辑
leonling 发表于 2023-7-27 09:08
假设一个任务是处理报文的,处理好了检测下有没有新报文,没有就uSuspendTasking。报文到了有中断,所以在 ...
uSuspendTasking是在任务临界区中执行的,iResumeTask(task, svid)是在PendSV中执行的,SysTick、PendSV、任务临界区中,三者是互斥访问的。
如果uSuspendTasking的过程中中断来了,在中断中恢复这个任务iResumeTask,只是服务的结构体指针入中断服务栈并触发PendSV;当中断返回后,继续执行uSuspendTasking,而后会退出任务临界区,再进入PendSV执行服务(iResumeTask),任务即可恢复。所以,当这种情况发生时,是挂起后再恢复,任务还是会被恢复的。
但要注意一点,在中断要直接恢复任务,不要判断任务的状态是否为挂起,如果为挂起再恢复,否则当这种情况发生时,有时会漏掉恢复。
关于svid:
svid是中断异步服务的用户id。首先,在MCU配置文件中(MCUCFG_8051.h、MCUCFG_80251.h),PendSV_Handler/异步服务总数,是与其相关的配置。
异步服务总数:
对于51/251单片机,您在中断中每调用一次异步服务,都要输入一个唯一的服务ID,此项用于定义您在中断中调用异步服务的总次数。
例如,如果异步服务总数设置为10,那么您在中断中最多只能调用10次异步服务,服务ID为0~9。
至于每个服务的svid是几,是随意的。只要每个服务的svid不同,并且小于“异步服务总数”即可。