Files
cosyos/说明文档/CosyOS原理与应用.md
零中断延迟的RTOS 8ad02f2006 update 说明文档/CosyOS原理与应用.md.
Signed-off-by: 零中断延迟的RTOS <cosyos@139.com>
2024-01-23 14:12:00 +00:00

41 KiB
Raw Blame History

第一章、基础篇

任务优先级分组

可在系统配置文件中配置任务优先级分组。
共有六个分组可供选择8级、16级、32级、64级、128级、256级您也可自定义分组。
CosyOS的任务优先级数值越大优先级越高数值越小优先级越低最低优先级固定为0级。
如您配置的分组为8级则最高优先级为7级最低优先级为0级。

时间片模式

CosyOS有三种时间片的定义模式全局时间片算法时间片自定义时间片,需在系统配置文件中定义。

  • 全局时间片 所有任务采用相同的时间片。
  • 算法时间片 不同优先级的任务时间片不同,采用一个算法公式来描述。
  • 自定义时间片 不同优先级的任务时间片不同,用户自定义各优先级的时间片。

全局时间片,简单高效,时间片轮转调度性能为最佳;
算法时间片、自定义时间片,可实现不同优先级的、精细的时间片控制。
时间片的取值范围1~65535单位为滴答周期。

安全运行时

安全运行时是CosyOS的安全关键技术之一是为了防止某任务长期独占或超时使用处理器。
当任务累计运行时间超过安全运行时,仍不能被比它优先级低的任务所切换,则进入超时状态并放弃处理器的使用权,直到系统空闲任务运行时才能恢复。
可在系统配置文件中设置安全运行时的开启或关闭。
安全运行时的取值范围0~65535单位为时间片0为无限长。
所有空闲任务无法应用安全运行时创建任务时安全运行时输入0即可。
安全运行时的应用技巧开发初期所有任务的安全运行时可设置为0待开发测试阶段中后期再根据任务各自的特点及测试经验合理的调整为有效值。

支持任务/钩子

  • 系统任务
    由CosyOS自主创建并自动启动的任务。
    1、任务管理器Taskmgr 优先级固定为最高优先级。
    2、调试任务Debugger 优先级固定为最高优先级,用做命令行和任务管理器的串口接收解析。
    3、启动任务Starter 优先级固定为比最高优先级低一级,通过调用启动钩子来启动用户任务,而后删除自身。
    4、系统空闲任务Sysidle 优先级固定为最低优先级0级是系统级的空闲任务。

  • 系统钩子
    由CosyOS自主创建并自动调用的函数并由用户来写代码。
    1、初始化钩子init_hook 在主函数中首先被调用适用于初始化时钟、GPIO、寄存器等工作。
    2、启动钩子start_hook 在启动任务中被调用用于启动用户任务。用户可在启动钩子内调用API来启动用户任务。
    3、空闲钩子idle_hook 在系统空闲任务中被调用,用户可添加自己的代码做一些没有实时性要求的工作。
    4、滴答钩子tick_hook 每个系统滴答周期,在系统滴答中断中都会被调用一次,适用于每滴答周期/秒/分/时/...做一次的工作。
    5、挂起服务钩子pendsv_hook 详情见全局变量访问相关介绍及说明。
    6、全局变量钩子gvar_hook 用于更新全局变量的副本,详情见全局变量访问相关介绍及说明。

  • 定时任务/钩子
    包括定时中断任务/钩子和定时查询任务/钩子,其实质都是软件定时器中断。
    1、定时中断任务/钩子 由用户设定定时时间,当定时器溢出时,系统将自动恢复/调用与其绑定的任务/钩子并自动重复定时(如果您已开启定时器的自动重装载)。
    2、定时查询任务/钩子 由用户设定定时时间,当定时器溢出后,系统在每个滴答周期都会查询用户定义的事件,若事件为真,系统将自动恢复/调用与其绑定的任务/钩子并自动重复定时(如果您已开启定时器的自动重装载)。系统初始化后,所有定时查询定时器的值均为零,相当于已经溢出,系统已经开始查询用户定义的事件了。
    任务与钩子的区别 由于定时中断钩子、定时查询钩子都是在系统滴答中断中被调用,所以只有相对精简的代码才适合创建钩子,耗时长的代码应创建为任务。再有,钩子相当于比任务具有更高的优先级,可更及时的被执行。

  • 一般用户任务
    对于一般用户任务只要优先级为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-任务状态图 ——————————

任务队列

CosyOS全局只有一个任务队列为双向循环链表所有已启动且未删除的任务都会在队列上排队。

  • 排队原则
    1、 在队列上从头至尾,按任务优先级由大到小排列。
    2、 如果任务优先级相同,则按任务启动顺序排列(先启动的任务排在前面)。
    3、 如果启用了任务管理器,那它一定是任务队列上的第一个任务(头节点)。
    4、 唯一的例外,系统空闲任务是任务队列上的最后一个任务(尾节点),其它所有任务都会排在它的前面。

CosyOS任务管理器 真实的反应了整个任务队列的当前形态。
其中任务IDTID是按任务的启动顺序动态分配的从1开始++),真实的反应了任务的启动顺序。
下方示例中消失的任务TID2为系统启动任务Starter任务启动完成后该任务默认被自动删除。
任务管理器 启动钩子
—————————— CosyOS-任务管理器 ——————————-———————— CosyOS-启动钩子 ————————

任务调度

CosyOS的 任务调度方式 包括 抢占式调度时间片轮转调度
不同优先级的任务实行抢占式调度,相同优先级的任务按任务队列中的排列顺序实行时间片轮转调度。

CosyOS的 任务调度时机 分为 定时调度临时调度

  • 定时调度
    在每个系统滴答周期,系统滴答中断都会触发一次任务调度。
  • 临时调度
    一、自动调度
    当满足特定条件时,由系统自动触发的临时性的任务调度。
    1、当前任务被阻塞、挂起、删除时会立即触发任务调度。
    2、恢复任务时如果该任务被恢复后可以运行并且它的优先级高于当前任务的优先级会立即触发任务调度。
    3、设置任务优先级时如果该任务可以运行并且它的新优先级高于当前任务的优先级会立即触发任务调度。
    4、设置阻塞时间为零或清除阻塞时该任务会转为就绪状态如果它的优先级高于当前任务的优先级会立即触发任务调度。
    5、归还互斥信号量完成时如果发生了优先级继承任务优先级被提升会恢复该任务的原优先级并立即触发任务调度。
    6、归还二值信号量时如果有任务因获取该信号量而阻塞会立即触发任务调度。
    7、归还计数信号量时如果有任务因获取该信号量而阻塞会立即触发任务调度。
    8、给予二值信号量时如果有任务因获取该信号量而阻塞如果该任务的优先级高于当前任务的优先级会立即触发任务调度。
    9、给予计数信号量时如果有任务因获取该信号量而阻塞如果该任务的优先级高于当前任务的优先级会立即触发任务调度。
    A、发送私信至某任务时如果该任务因接收私信而阻塞如果该任务的优先级高于当前任务的优先级会立即触发任务调度。
    B、发送飞信时如果有任务因接收该信箱的飞信而阻塞如果该任务的优先级高于当前任务的优先级会立即触发任务调度。
    C、发送邮件时如果有任务因接收该邮箱的邮件而阻塞如果该任务的优先级高于当前任务的优先级会立即触发任务调度。
    D、发送消息时如果有任务因接收该队列的消息而阻塞如果该任务的优先级高于当前任务的优先级会立即触发任务调度。
    二、手动调度
    由用户手动触发的临时性的任务调度,包括任务级任务调度和中断级任务调度。
    当无法满足自动调度条件,用户又期望特定线程能够更及时的处理事件,可采用手动调度。

CosyOS的任务调度策略可最大程度的实现尽可能实时高效的任务切换使CosyOS的实时性更为优异。

软件定时器

CosyOS的软件定时器分为三类延时定时器、定时中断定时器、定时查询定时器。

  1. 延时定时器 每个任务各有一个,用来在此任务中实现阻塞时间计数。
  2. 定时中断定时器 每个定时中断任务/钩子绑定一个定时中断定时器至多可以有64个定时器ID从0开始用户通过调用API进行定时操作后定时器将自动开始计数。
  3. 定时查询定时器 每个定时查询任务/钩子绑定一个定时查询定时器至多可以有64个定时器ID从0开始用户通过调用API进行定时操作后定时器将自动开始计数。

第二章、中断篇

什么是零中断延迟?

零中断延迟并非是中断响应时间为零而是指当引入了RTOS以后中断响应时间仍然能够达到MCU内核特性的响应时间即只要中断发生就能按中断优先级立即抢占不存在指令级延误。也就是说中断响应时间不受RTOS影响与裸机编程是一样的。

零中断延迟的意义

MCU的核心价值在于中断的使用实现对紧急事件的及时响应并处理。如果RTOS内核以关闭总中断的方式来处理内核服务、保护临界段则意味着会丢失对高速并发事件的响应并导致处理延误。而 “丢失响应” 可能是致命的,“处理延误” 可能会引发不良后果。

  • 误区
    即使关闭了总中断,用户中断被触发后标志位仍可置位,当开启总中断后仍可响应中断。
    首先,这已经导致了“处理延误”,如果延误时间超出了允许范围,危害是可想而知的。
    其次,如果关闭总中断期间,某个用户中断发生了两次或多次(并发),当开启总中断后,也仅能响应并处理一次,即“丢失响应”。而有的事件,发生一次就得处理一次,如果有遗漏将导致致命的错误。
  • 示例
    高速通讯(接收丢帧)
    高速捕获(丢失脉冲)
    高速PWM
    高速ADC

事实上RTOS以关闭总中断的方式来保护临界段是最为直接有效的可实现最少的指令。CosyOS通过特殊的方法来保护临界段必然是走了一条弯路需要执行更多的指令牺牲了整体的运行效率但同时确换取了零中断延迟这一点与RTOS通过牺牲整体的运行效率来换取实时性是相同的道理。

零中断延迟是CosyOS的宗旨是原则和底线必须坚决贯彻落实。

零中断延迟基本原理

  • CosyOS-实时运行模型

  • 中断层 【用户中断按中断优先级实时抢占、零中断延迟】

    • 用户中断
      -> 中断本地服务的执行
      -> 中断挂起服务的装载
  • 服务层 【内核服务】

    • SysTick [minpri]
      -> 软件RTC/定时器计数
      -> 恢复定时任务
      -> 调用定时钩子/滴答钩子(滴答服务的执行)
    • PendSV [minpri]
      -> 中断挂起服务的执行
      -> 任务调度/切换
    • 任务临界区 [关闭SysTick/PendSV]
      -> 任务服务的执行
  • 任务层 【不同优先级的任务抢占式调度,相同优先级的任务时间片轮转调度】

    • Taskmgr[maxpri]
    • Debugger[maxpri]
    • Starter[maxpri-1]
    • 一般用户任务[maxpri-1 ~ minpri+1:1]
    • 用户空闲任务[minpri:0]
    • 系统空闲任务[minpri:0]
  • 服务层中SysTick、PendSV、任务临界区三者间是互斥访问的。换言之整个服务层是一个大临界区服务层临界区

  • 所有内核服务(中断本地服务除外),均在 “服务层临界区” 执行,从而保证服务的 “操作流” 不会被打断。

  • 中断本地服务采用互斥访问机制。

服务详解

  • 任务服务 是指在任务中调用的服务,并在任务临界区中执行。
  • 滴答服务 是指在滴答中调用的服务,并在滴答中执行。 用户可在滴答钩子、定时中断钩子、定时查询钩子中调用滴答服务。
  • 中断服务 是指在用户中断中调用的服务,分为中断本地服务和中断挂起服务。
  • 中断本地服务 是指在用户中断中调用并在本地直接执行的服务,需要互斥访问机制相配合。
  • 中断挂起服务 是指在用户中断中调用的服务不在本地直接执行,而是把服务的相关内容存入局部的结构体中, 再把结构体指针存入中断挂起服务缓存队列PendSV_FIFO再触发PendSV而后在PendSV中执行。

互斥访问机制

  • 中断中读全局变量
    重入访问:如果变量正在被写入,仍可成功读取变量。
    首先,用户需定义全局变量的副本,并按照指定方式来更新副本。中断中读全局变量时,
    需调用 iWhichGVarToRead 来询问应该读哪一个全局变量返回0读正本返回1读副本。

  • 中断中接收邮件
    重入访问:如果邮箱正在被写入,仍可成功接收邮件(将读取写入过程所读取的局部邮箱)。
    互斥访问如果邮箱正在被写入将返回失败false

  • 中断中接收消息
    互斥访问如果队列正在被访问将返回失败NULL

中断挂起服务缓存

CosyOS-II 中断挂起服务缓存队列PendSV_FIFO已实现所有内核的MCU均为先入先出队列FIFO
中断挂起服务缓存队列,有必要先入先出吗?答案是确定的,原因在于当中断并发时,服务的执行顺序要符合“不同中断间可能存在的”和“同一中断中必然存在的”提请服务的时序关系。再有,“入队列”与“出队列”的过程必须是原子操作,“操作流”不能被打断。

中断使用注意事项

  • 在CosyOS中中断是系统脱管的用户对中断的使用与裸机编程是一样的。
  • 只要用户中断不是最低优先级,就可实现零中断延迟,用户中断应尽量避免使用最低优先级。
    其实即使是最低优先级也可能是零中断延迟的未使用BASEPRI时只不过系统中断可能会与其抢占。
  • 用户中断应遵循快进快出的原则,对事件仅做必要的紧急处理,再以同步方式通知 中断服务任务 做善后处理。
  • 用户中断的最大执行时间应远小于系统滴答周期,这将促使整个系统更加良性的运行。

第三章、线程通信

互斥信号量

二值信号量

计数信号量

事件标志组

飞信

  • 典型特征:极简类型、极速通信。
  • 数据类型是极简的针对不同的MCU内核都要求数据类型必须是原子访问类型。
    1、8051内核飞信固定为1字节无符号类型uint8_t
    2、80251内核飞信固定为2字节无符号类型uint16_t
    3、Arm32内核飞信固定为4字节无符号类型uint32_t
  • 通信速率是极速的,仅使用一个变量(信箱),同时即是消息又是新消息标志。
    飞信为0时表示无消息飞信非0时表示有消息因此用户传输的有效消息必须为真值。

私信

  • 典型特征:随意定义、灵活多变。
  • 私信是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_三个未引用的参数。

消息邮箱

  • 典型特征:任意类型、数据拷贝。
  • 数据类型是任意的CosyOS的消息邮箱支持随意定义数据类型包括结构体、数组等。
  • 注意事项
    1、CosyOS的消息邮箱仅能保存一封邮件如果收件速度低于发件速度会导致邮件覆盖。
    2、消息邮箱虽支持随意定义数据类型但建议不要太大以免影响系统实时性。

消息队列

  • 典型特征:并发发送、指针引用。
  • 队列类型
    静态队列:收发消息的效率高,队列占用固定的内存;
    动态队列:收发消息的效率低,接收消息后,队列内存可被回收。
  • 注意事项
    1、每个消息队列用户应当自己明确消息的类型和size此事与CosyOS无关。
    2、每一处发送消息消息缓存都是独享的可静态创建定义静态数组或动态创建动态内存分配
    3、对于动态创建的消息缓存在消息使用完以后用户应当自己释放消息指针。

第四章、线程同步

事件同步

处理一个事件

  • 一次同步
    信号或标志 :【用户自己实现,无相关服务支持】;
    二值信号量 :【同步过程:给予 -> 获取】;
    恢复和挂起 :【同步过程:恢复 -> 挂起】; 最为高效,推荐使用;
  • 并发同步
    计数信号量 :【同步过程:给予 -> 获取】;
  • 无限同步
    二值信号量 :【同步过程:给予 -> 查询/等待】;【终止同步:上锁 -> 查询/等待】;

处理一类事件

  • 一次同步
    事件标志组 :【同步过程:设置 -> (查询 -> 清除)】;

消息同步

【同步过程:发送 -> 接收】;

  • 一次同步
    飞信 :极简类型、极速通信; 最为高效,推荐使用;
    私信 :随意定义、灵活多变;
    消息邮箱 :任意类型、数据拷贝;
  • 并发同步
    消息队列 :并发发送、指针引用;

补充说明

  • 一次同步 :事件的发生不存在并发,发生一次即处理一次。
  • 并发同步 :事件的发生存在瞬时并发,导致“事件处理线程”来不急处理。
  • 无限同步 :仅给予一次,“事件处理线程”便可无限次的、周期性的处理事件;通过上锁,可终止同步。
  • 有的事件,发生一次就得处理一次,如果有遗漏将导致致命的错误。对于这种情况,如果存在并发,就必须采用并发同步工具;
  • 有的事件,即使累计发生了多次未处理(并发),仍然仅处理一次即可。对于这种情况,虽存在并发,仍可采用一次同步工具。

第五章、资源与访问

资源

  • 分类一
    1、局部资源
    2、公共资源任务级公共资源、全局公共资源
  • 分类二
    1、不会重入资源
    2、会重入资源可重入资源、不可重入资源

访问

  • 分类一
    只读访问、只写访问、读写访问(自运算);
  • 分类二
    可重入访问、互斥访问;

临界区

CosyOS的临界区可分为 任务临界区服务层临界区全局临界区

  • 任务临界区
    任务级的临界区保护仅关闭系统中断SysTick、PendSV
    任务临界区不会破坏零中断延迟,当需要任务级的临界区保护时,可以考虑。

  • 服务层临界区
    【任务临界区 + 系统滴答 + 挂起服务调用/钩子】即CosyOS实时运行模型中的服务层是系统级的临界区保护。
    具体过程:
    1、任务中在任务临界区中访问
    2、滴答中直接访问即可
    3、中断中采用挂起服务调用/钩子来访问;
    服务层临界区不会破坏零中断延迟,当需要系统级的临界区保护时,应首先予以考虑。

  • 全局临界区
    系统级的临界区保护,一般会关闭总中断。
    具体方式:
    8051/80251操作EA会关闭总中断。
    Arm
    1、操作PRIMASK会关闭总中断。
    2、操作FAULTMASK会关闭总中断。
    3、操作BASEPRI可实现不同掩蔽范围的全局临界区保护不会关闭总中断。
    全局临界区会破坏零中断延迟,应做为最后的选项,慎重使用。
    CosyOS内核中从来不会进入全局临界区提供此项服务只是为了便于用户对全局公共资源或程序过程的保护。

任务临界区、全局临界区都支持嵌套功能最大嵌套深度255。

  • 何时应用临界区
    1、对于CosyOS已经提供服务支持的功能用户直接调用API实现即可无需考虑临界区问题。
    2、只有在任务中访问“事件标志组”和“非原子全局变量”时才需要用户自行进入“任务临界区”参见《API用户参考手册》。
    3、用户对其它公共资源或程序过程的保护才需要考虑采用“临界区”或其它互斥访问方式来实现。

  • 临界区应用原则
    临界区应遵循快进快出的原则,临界段代码的执行时间应远小于系统滴答周期,这将促使整个系统更加良性的运行。

互斥访问

各访问者对同一个目标资源的访问过程是互相排斥的,即每次只允许一个访问者访问目标资源,目标资源是不可重入资源。
下述为CosyOS推荐方案

  • 任务级公共资源
    1、互斥信号量访问过程耗时的、实时性要求不高的应尽量采用互斥信号量。
    2、任务临界区访问过程迅速的、实时性要求较高的可考虑任务临界区。
  • 全局公共资源
    1、服务层临界区即可实现全局成功的互斥访问又不会破坏零中断延迟是最优方案应首先予以考虑。
    2、二值信号量弊端是如果获取失败将导致访问失败而从概率上来说获取失败是必然会发生的。
    3、全局临界区弊端是全局临界区会破坏零中断延迟应做为最后的选项慎重使用。

第六章、定时服务

我们知道MCU都有硬件定时器和定时器中断当定时器溢出时CPU可以响应中断并调用定时器中断服务程序。
然而,硬件定时器的数量十分有限,在较复杂的应用中可能会不够用。
因此CosyOS提供了软件定时器以弥补硬件定时器数量的不足同时也支持“定时服务”功能来模拟定时器中断。
下面简单对比一下MCU、FreeRTOS、CosyOS三者间“定时服务”的区别

  • MCU
    -----------调用
    中断响应 ——> 定时器中断服务程序

  • FreeRTOS
    -----------调用
    守护任务 ——> 定时器回调函数

  • CosyOS
    -----------调用
    系统滴答 ——> 定时中断/查询钩子
    -----------恢复
    系统滴答 ——> 定时中断/查询任务

FreeRTOS 是在守护任务中统一调用所有的定时器回调函数,所有“定时服务”享有相同的优先级,即守护任务的优先级。

CosyOS则分为两种情况
1、在系统滴答中调用定时中断/查询钩子,“定时服务”享有系统滴答的优先级(高于任务);
2、在系统滴答中恢复定时中断/查询任务,“定时服务”的优先级即任务优先级(用户定义)。
注意:只有极为精简的代码才可创建为钩子,否则会对系统实时性造成不利影响。

可见,CosyOS的“定时服务”优先级都可由用户灵活配置。

关于CosyOS的定时任务/钩子的应用示例可参考CosyOS源码
1、任务管理器 Taskmgr为定时查询任务\System\taskmgr.c
2、调试任务 Debugger为定时中断任务\System\debug.c
3、调试钩子 debug_hook为定时查询钩子\System\debug.c。

第七章、任务管理器

可在系统配置文件中配置任务管理器的相关选项。
可使用超级终端或串口调试助手,作为任务管理器的输出窗口。
任务管理器
—————————— CosyOS-任务管理器 ——————————

  • Name:任务名称

  • TID任务ID

  • PRI:任务优先级

  • STA:任务状态

  • CPUCPU使用率

  • RAM:任务栈大小(字节数),下面举例说明:
    32B/s64B系统采用每调度监控计算出的在概率上必然存在的最大任务栈占用为32Bytes任务栈已 静态创建 了64Bytes。
    32B/m64B系统采用每调度监控计算出的在概率上必然存在的最大任务栈占用为32Bytes任务栈已 malloc 分配了64Bytes。
    32B/r40B系统采用每调度监控计算出的在概率上必然存在的最大任务栈占用为32Bytes任务栈已 realloc 分配了40Bytes。

  • PSVFIFO中断挂起服务缓存PendSV_FIFO的深度如果您已开启PendSV_FIFO监控会显示此项历史最大值/设置值。

  • Task-PC任务PC监控如果您已开启任务PC监控会显示此项。
    当任务管理器被调度运行时上一个被切换的任务入栈的PC值。
    当有任务出现代码运行卡死或死循环时此功能可用来帮助用户锁定具体位置以便查找BUG。注意PC值为下一条汇编指令的地址。
    某任务出现代码运行卡死或死循环的表现任务始终为就绪状态且占用较高的CPU使用率比它优先级低的任务一直都没有机会运行。

  • SysTick:系统滴答时间统计,如果您已开启系统滴答时间统计,会显示此项。
    注意:系统滴答时间是系统滴答中断运行的平均时间,并非是任务切换时间。

  • Alarm:报警信息,如果有报警,会显示此项。
    omq消息队列溢出。
    otq任务队列溢出
    osr安全运行时超时。
    otp任务优先级超出范围。
    rts任务栈重分配发生虽然成功了但仍要提醒用户任务栈定义小了。
    任务栈重分配机制被设计用来在正式的产品中抵御任务栈溢出的风险,用户在开发测试阶段应避免这种情况的发生。
    ots在概率上、在未来必然会发生的任务栈溢出虽然现在可能并未发生

  • Fault:故障信息,如果有故障,会显示此项。
    mmn消息节点内存分配失败
    mtn任务节点内存分配失败
    mts任务栈内存分配失败
    rts任务栈内存重分配失败
    ots任务栈溢出
    fst启动任务失败
    erm中断中接收消息错误中断中不可接收来自动态消息队列的消息
    opd中断挂起服务缓存溢出。

  • 任务管理器相关命令
    1、taskmgr启动任务管理器。
    2、exit退出任务管理器。
    3、taskmgr /s=...,任务管理器更新速度,取值范围:[50~5000]ms。
    注意:命令仅支持小写字母,结尾必须加回车换行(\r\n

第八章、任务栈

任务栈模式

CosyOS把任务栈的处理方式称之为任务栈模式共包括三种类型MSP模式PSP模式MSP+PSP模式

  • MSP模式 任务切换时,先拷贝主栈内容至当前任务栈(保存现场);再拷贝新任务栈内容至主栈(恢复现场)。
  • PSP模式 每个任务的任务栈都是主栈,哪个任务运行时,哪个任务的任务栈(当前任务栈)就是当前主栈。
  • MSP+PSP模式 中断时为Handler模式入栈入的是主栈任务时为Thread模式入栈入的是当前任务栈。

对于51来说CosyOS采用了搬栈这一传统方案也就是MSP模式
对于Arm来说CosyOS所采用的方法当然是主流的双栈指针了也就是MSP+PSP模式
对于251来说CosyOS支持MSP模式、PSP模式这两种栈模式供用户选择。

  • PSP模式与MSP+PSP模式的异同
    PSP模式中断入栈也入任务栈相比MSP+PSP模式需要更大的任务栈也就是需要更大的edata内存。

  • MSP模式的优势
    凡事都是辩证的,有一利必有一弊。
    MSP模式虽然在任务切换时会保存并恢复现场导致效率较低但只要主栈和内存池足够大就可确保所有任务栈永不溢出。
    当然前提是任务栈得是动态的CosyOS将自动启用任务栈重分配机制可有效抵御任务栈溢出的风险。
    这一点是PSP模式、MSP+PSP模式所不具备的。PSP模式、MSP+PSP模式一旦任务栈溢出既成事实数据覆盖可能已经发生无法挽回。

  • 任务栈重分配机制
    任务栈重分配机制,被设计用来在正式的产品中抵御任务栈溢出的风险。
    在概率上,也许测试一年也不会碰到一次任务栈溢出,但大批量产品投入使用后不一定哪天就会溢出。
    如果内存足够大问题也好解决但51/251的内存有限既要节约着用还不要栈溢出就得用点策略了。
    任务栈重分配机制的意义正在于此。
    重点是,任务栈重分配机制是做为一种补救手段,不建议用户过分依赖。
    开发调试时,还是要配置够用的任务栈,保证测试阶段不会溢出(不会发生重分配)。
    任务栈重分配次数多了会导致内存碎片,如果过分依赖的话,反而会导致内存更加不够用。
    任务栈重分配机制,就是为了当万一哪一天任务栈溢出了呢?能够满血复活。
    启用条件:
    1、对于8051当任务创建模式非静态创建时CosyOS将自动启用任务栈重分配机制。
    2、对于80251当任务栈模式为MSP模式、任务创建模式非静态创建时CosyOS将自动启用任务栈重分配机制。

任务栈监控

CosyOS的任务栈监控包括每调度监控、入栈监控、线程入口监控。

  • 每调度监控
    每次任务调度时都会假定本次会切换任务、需要现场保护,计算当前任务需要多大的任务栈。
    这种假定在概率上必然早晚都会发生,但不一定是什么时候(也许是即将发生、也许是一万年以后、也许是时间的尽头),从而提前预判任务栈溢出的风险。
    应用技巧:
    任务调度包括定时调度与临时调度,对于定时调度来说,就是系统滴答中断触发的,所以,用户在开发测试阶段可调整不同的 系统滴答周期,可提升“每调度监控”提前预判任务栈溢出风险的机率。
    启用条件:
    CosyOS启用“每调度监控”的充分必要条件是启用任务管理器即系统配置中“DEBUG接口设置”必须打勾。
  • 入栈监控
    当任务切换时,对当前任务栈进行计算,并做相关处理。
  • 线程入口监控
    线程入口监控现已取消,原因是虽然有效,但不实用。

任务栈定义

  • 任务栈初定义
    1、对于51及251的MSP模式当任务创建模式非静态创建时任务栈的初始定义可以小可为零因为有重分配机制而后再根据监控情况重新调整任务栈。
    2、对于251的PSP模式及Arm的MSP+PSP模式任务栈的初始定义一定要够大否则可能会运行不起来或死机而后再根据监控情况重新调整任务栈。

  • 任务栈调整
    1、当任务栈重分配发生时就需要重新调整任务栈的size了。
    32B/r40B此时就需要重新调整任务栈的大小为至少32个字节。
    当这种情况发生时还会有相应的报警提示用户rts任务栈重分配发生虽然成功了但仍要提醒用户任务栈定义小了。
    2、当 “前面的字节数” 大于 “后面的字节数” 时就需要重新调整任务栈的size了。
    56B/m32B此时就需要重新调整任务栈的大小为至少56个字节。
    当这种情况发生时还会有相应的报警提示用户ots在概率上、在未来必然会发生的任务栈溢出虽然现在可能并未发生
    3、对于251的PSP模式由于中断入栈也入任务栈所以需要用户自己根据中断的使用情况来计算一个中断嵌套入栈的最大size不必包括最低优先级的中断入栈累加到“前面的字节数”中看是否会大于 “后面的字节数”,再行调整。

  • 任务栈补偿
    1、在开发测试阶段用户应尽量设法模拟各种情况的发生最好是频繁发生包括中断的发生。只要你的产品支持的功能、情况都要设法模拟到。
    2、通过足够时间的测试统计出各任务在任务管理器当中的“RAM”项中系统采用每调度监控假定入栈计算出的在概率上必然存在的最大任务栈占用而后在此值的基础上再累加一个补偿增量再重新定义任务栈size。
    示例经过长期的测试以后某任务的任务栈为56B/m64B可重新定义任务栈的size为56 + XX为补偿增量。
    原因是无论 每调度监控 如何的假定入栈,也可能会存在着更大的任务栈需求,只是未能监测到,所以必须增加一定的补偿。
    而这个补偿量的大小,每个任务都可根据剩余内存的大小而灵活配置,最好能尽量大一些,尤其是对于没有任务栈重分配机制的情况来说。
    没有任务栈重分配机制的情况:
    1、任务创建模式为静态创建
    2、251的PSP模式
    3、ARM。
    这个补偿增量对于51来说可描述为2的整数倍2 x NN为每调度监控未能监测到的N次的函数嵌套调用。
    对于251、ARM来说则无法描述。

附 录、调用服务注意事项

CosyOS-II 在临界区中或关闭总中断时调用服务注意事项

  • 在任务中,以下服务不支持在临界区中(包括任务临界区和全局临界区)或关闭总中断时调用:
    1、启动任务uStartTask(task, status)、uStartTask_Ready(task)、uStartTask_Suspend(task)。
    2、互斥信号量获取uTakeMut(mut, tc)。
    3、二值信号量等待uWaitBin(bin, tc)。
    4、二值信号量获取uTakeBin(bin, tc)。
    5、计数信号量获取uTakeSem(sem, tc)。
    6、接收飞信uRecvFetion(tbox, tc)。
    7、接收私信uRecvTaskMsg(tc)。
    8、接收邮件uRecvMail(mail, mbox, tc)。
    9、接收消息uRecvMsg(que, tc)。
    A、等待标志组uWaitFlagGroup(group, tc)。
  • 其它会导致任务调度/切换的服务,虽支持在临界区中或关闭总中断时调用,但也必须等到最终退出临界区或开启总中断后才能切换任务。

CosyOS-II 在中断中调用服务注意事项

  • 中断中写全局变量
    一、当用户在中断中调用iWriteGVar(gv, lv)、iWriteGAry(gp, lp, size)、iWriteGStr(gs, ls)写一个全局变量时,局部变量要做为专用。
    1、如果局部变量被更新之后就必须调用服务写同一个全局变量。
    2、调用服务以后该局部变量在本次中断中不可被改变因为写全局变量为挂起服务。
  • 中断中发送邮件
    一、当用户在中断中调用iSendMail(mbox, mail)发送邮件时mail所指向的局部邮箱要做为专用。
    1、如果局部邮箱被更新之后就必须调用服务发送邮件至同一个邮箱。
    2、服务调用以后该局部邮箱在本次中断中不可被改变因为发送邮件为挂起服务。
    二、当邮箱的数据类型为非原子访问类型时中断最好不要发送邮件给中断除非这个中断的发生频率足够低可确保在PendSV中最终完成发送以后这个中断才会再次发生否则会有出错的风险。