uc/OS III任务的理解

时间:2024-04-12 22:47:31

任务的概念

在实际应用中一般是将工作拆分为多个任务的,并且每个任务都是可靠的。在使用uC/OS就可以很好的解决这个问题,任务又叫线程,在对于单个CPU来说,在任何时刻,都是只能有有一个任务被执行。

uC/OS-III 支持多任务且对任务数量没有限制,任务数仅取决于处理器内存的大小(RAM)。多任务调度是任务间占用CPU 的过程。CPU 有根据算法切换任务。多任务调度让人感觉到是有多个CPU在运行,并最大化利用CPU。多任务调用有助于模块化应用,是最重要的功能之一,能帮助程序员管理复杂的实时性应用。它也使程序易于设计和维护。
任务用于监控输入、更新输出、计算、循环控制、显示、读按钮和键盘、与其它系统交流等。有些应用中可能只包含少数任务,有些应用中也可能包含上百个任务。任务数多并不意味这设计有多好或者有多有效,这依赖于应用的需要。任务的功能也要根据应用设计。一个任务可能只需要工作几微秒,然而有些任务可能就需要工作几十毫秒了。
在大多数嵌入式系统中,任务通常是无限循环的。任务不能像C 函数那样,它是不能return 的。

当任务第一次执行时,会传入一个变量"p_arg"。这是一个指向void的指针。用于变量的地址、结构体地址、或者函数的地址等。只运行一次的任务结束时必须通过调用OSTaskDel()删除自己。这样可以使系统中的任务数减少。在任务体中,任务可以调用uC/OS-III提供的大部分函数帮助完成其所需要完成的功能。

uC/OS-III 需要通过调用函数OSTaskCreate() 创建任务。OSTaskCreate()函数的原型如下所示

uc/OS III任务的理解

关于创建任务的详细参数,前面也有说过了创建一个任务时必须为其分配一个TCB(Task Control Block,会存放任务优先级、任务名、任务状态、内部消息队列、内部信号量等),一个堆栈,一个优先级和其它一些参数。

接下来, 会调用一些定义在OS_CPU_C.C 中的函数如OSTaskCreateHook(),TCB 中有指针指向这个函数,用于扩展应用。例如,可以打印最新创建的TCB 内容到某终端(利于调试)。然后该任务被放到就绪列表(详见第六章"就绪列表")。uC/OS-III调用调度器,并切换到优先级最高的任务。

 一.任务优先级的设置

有些时候任务的优先级是显而易见的,多数系统中,不是所有的任务都是重要的,不重要的任务应该被设置为低优先级。

二.堆栈空间大小的确定

堆栈的大小取决于该任务的需求。设置堆栈大小时,就需要考虑:所有可能被堆栈调用的函数及其函数的嵌套层数,相关局部变量的大小,中断服务程序所需要的空间。另外,堆栈还需要存入CPU寄存器,如果处理浮点数单元FPU寄存器的话就还需要存入FPU寄存器。嵌入式系统的潜规则,避免写递归函数。在产品的开发和测试阶段,通常在运行时用调试器测量堆栈的使用情况。

三.检测任务堆栈的溢出

比较复杂,后文再写,因为不是很懂

四.任务管理服务

uC/OS-III 提供了很多与任务相关的函数。这些函数可以在OS_TASK.C 中找到,它们都以以OSTask***()形式命名的。如下
分组 函数
普通的 OSTaskCreate():创建人物
OSTaskDel():删除任务
OSTaskChangePrio():修改任务优先级
OSTaskRegSet():任务寄存器设置
OSTaskRegGet():获取当前寄存器的值
OSTaskSuspend():取消任务
OSTaskResume():恢复任务
OSTaskTimeQuantaSet():更改任务时间片
OS_TaskInit():任务初始化
OS_TaskInitTCB():初始化堆栈的默认值
标记任务 OSTaskSemPend():等待接收任务信号量
OSTaskSemPost():表示等待任务的信号
OSTaskSemPendAbort():终止等待任务信号
OSTaskSemSet():设置清除信号计数器
OSTaskStkChk():被用来检测堆栈后剩余内存量
给任务发送消息 OSTaskQPend():等待接收一个消息
OSTaskQPost():发送信息给任务
OSTaskQPendAbort():中止等待消息
OSTaskQFlush():刷新内部任务消息队列
OS_TaskResume():恢复一个已经移除的任务
在源代码中,针对每个函数都有很详细的解说是什么,该怎么用,这边不再说明

五.内部任务管理

1任务状态

从用户的观点来看,任务可以是有5 种状态,如下。展示了任务状态间的转换关系。{休眠状态,就绪状态,运行状态,挂起状态,中断状态}
uc/OS III任务的理解

(1)处于休眠状态的任务驻留于内存但未被uC/OS-III 使能。通过调用OSTaskCreate()函数uC/OS-III 创建任务。任务代码是存在于ROM 的。但需要用OSTaskCreate()函数通知uC/OS-III 关于任务的相关信息。如果任务的使命完成了,就要调用OSTaskDel()删除该任务。OSTaskDel()实际上不是删除任务的代码,只是让任务不再具有使用CPU 的资格而已。
(2)就绪状态的任务根据优先级有序地排列于就绪列表中。就绪列表中对就绪任务的个数没有限制。
(3)正在运行的任务被置为运行状态。在单CPU 中,任何时刻只能有一个任务被运行。当应用程序调用OSStart() 或者调用OSIntExit() 或者调用OS_TASK_SW()时uC/OS-III 从就绪队列中选择优先级最高的任务去运行。正如前面所提到的,有些时候任务必须等待某些事件发生,若事件还未发生时,任务就会被设置为挂起状态。
(4)挂起状态的任务被放置在挂起列表中以表明任务在等待某些事件的发生。等待的时候,任务是不会占用CPU 的。事件发生时,该任务会被放到就绪队列中。在这种情况下,正在运行的任务可能会被抢占(被放回就绪列表),并由uC/OS-III 选择优先级最高的任务去运行。换句话说,如果新的任务优先级最高,那么它就会被立即运行。请注意,调用OSTaskSuspend()会任务无条件地停止运行。有些时候调用OSTaskSuspend()不是为了等待某个事件的发生,而是等待另一个任务调用OSTaskResume()函数恢复这个任务。
(5)若中断发生,中断会挂起正在执行的任务并去处理ISR。ISR中可能有某些任务等待的事件。一般来说,中断用来通知任务某些事件的发生,并让在任务级处理实际的响应操作。ISR 程序越短越好,实际响应中断的操作应该被设置在任务级以便能让uC/OS-III 管理这些操作。ISR 中只允许调用一些提交函数(OSFlagPost(),OSQPost()OSSemPost() , OSTaskQPost() , OSTaskSemPost()) , 除了OSMutexPost()。因为mutex 只允许在任务级被修改。大多数处理器支持中断嵌套。然而,如果管理不当,中断
嵌套是很容易引起堆栈溢出的。


uC/OS-III 一直追踪着任务的状态如下图。事实上,这些都是以一个变量的形式保存在每个任务的TCB 中。图小括号中的数值表示着任务的状态,每个任务都可以有8 种状态。
uc/OS III任务的理解
(0)状态0 表示任务已经就绪。每个任务在被运行之前都必须处于就绪状态。
(1)任务可以通过调用OSTimeDly()或者OSTimeDlyHMSM()等待期满。当期满或者延时删除时(通过调用OSTimeDlyResume()),任务会转为就绪状态。
//////这是一天的进程,但为了章节比较好看,只能列为一个章节
(2)任务可以通过调用挂起函数(OSFlagPend(),OSMutexPend(),OSQPend,OSSemPend,OSTaskQPend(),OSTaskSemPend())等待某事件的发生。当事件发生时、该任务被删除、或者被另一个任务取消等待时,等待停止。
(3)如前面所说,任务可以等待事件发生。但任务也可以被设置等待多少时间。如果在这段时间内事件没有发生,任务也会被设为就绪状态,并通知这个任务是等待超时而被挂起的。{挂起函数都有一个关于函数执行结果错误代号,可以查看这个代号知道任务是因何被就绪的}
(4) 任务暂停自己或者被其他任务暂停( 通过调用OSTaskSuspend())。暂停中的任务只能通过调用OSTaskResume()被恢复。
(5)一个延时中的任务也可以被其它任务设置为停止。在这种情况下,效果会被叠加。换句话说,延时需被执行、停止状态需被解除。该任务才会被执行。
(6)一个挂起状态中的任务也可能被其它任务设置为停止。同样的,效果会被叠加。事件发生且停止状态被移除后,任务才会被执行。
(7)任务可以等待事件的发生,但可以给它设定一个期限。同样的,它也可能被设为停止,效果是叠加的。除非移除停止状态并事件发生或等待事件超时,任务才会被执行。

2 任务控制块 TCB

任务控制块是被uC/OS-III 用于维护任务的一个结构体。每个任务都必须有自己的TCB。uC/OS-III 在RAM 中分配TCB。当调用uC/OS-III 提供的与任务相关的函数(以OSTask???()形式命名)时,任务的TCB 地址需会被提供给该函数。TCB 的结构定义于OS.H 中,由于结构体太长,这边不做截图了,可以参见OS.H中struct os_tcb {}结构体,里面有相对详细的介绍。TCB 中的一些变量可以根据具体应用进行裁剪。用户程序不应该访问这些变量(尤其不能更改它们)。换句话说,TCB 中的变量只能被uC/OS-III 访问
这边要是解释的话,需要太长的字段,实在是意义不大,源码OS.H 文件写的还比较好理解。