uC/OS-II的任务
在小型应用中,通常一个嵌入式系统中的所有程序模块同属于一个统一的任务——对宿主对象进行控制。因此,通常认为在嵌入式系统中只有一个进程,而把这个进程进行分解之后的那些小程序模块,由于他们没有自己的内存空间,所以他们实质上就是所谓的线程。在uC/OS-II中,习惯把这样的线程叫做任务,或者叫做实时任务。
实时系统中的任务的执行大多数都是由外部事件触发的。也就是说,实时操作系统的主要工作就是响应并处理各种外部事件。
uC/OS-II任务的存储结构
从应用程序设计的角度来看,uC/OS-II的任务就是一个线程,是一个用来解决用户问题的C函数和与之相关联的一些数据结构而构成的一个实体。
从任务的存储结构来看,任务有3个组成部分:任务程序代码,任务堆栈和任务控制块。其中,任务控制块用来保存任务属性;任务堆栈用来保存任务工作环境;任务程序代码是任务的执行部分。
uC/OS-II任务的优先级别
uC/OS-II把任务的优先权分为64个优先级别,每个级别都用一个数字来表示。数字0表示任务的优先级别最高,数字越大则表示任务的优先级别越低。
uC/OS-II规定,一个应用程序的任务数最多可以有64个。但在实际应用中,为了节省内存,用户可根据需要,在配置文件OS_CFG.H中通过对常数OS_LOWEST_PRIO赋值的方法来说明应用程序中任务优先级别的数目。通常,常数OS_LOWEST_PRIO还有另外一个意义,它还用来表示系统中任务的最低级别。该常数一旦被定义,就意味着系统可供使用的优先级别一共有OS_LOWEST_PRIO+1个,它们分别是:0,1,2,……OS_LOWEST_PRIO。
任务控制块
uC/OS-II用来记录任务的堆栈指针,任务的当前状态,任务的优先级别等一些与任务管理有关的表就叫做任务控制块。即进程控制块。
为了管理系统中的多个任务,uC/OS-II把系统所有任务的控制块链接为两条链表。
在uC/OS-II中,当用户应用程序调用系统函数OSTaskCreate()创建一个用户任务时,这个函数会把该任务的所有相关数据赋予任务控制块中的对应成员,并驻留在RAM中。
任务控制块结构的定义为如下:(在uC/OS-II.H中定义)
typedef struct os_tcb {
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
#if OS_TASK_CREATE_EXT_EN
void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
INT32U OSTCBStkSize; /* Size of task stack (in bytes) */
INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
INT16U OSTCBId; /* Task ID (0..65535) */
#endif
struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN || OS_SEM_EN
OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
#endif
#if (OS_Q_EN && (OS_MAX_QS >= 2)) || OS_MBOX_EN
void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
#endif
INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
INT8U OSTCBStat; /* Task status */
INT8U OSTCBPrio; /* Task priority (0 == highest, 63 == lowest) */
INT8U OSTCBX; /* Bit position in group corresponding to task priority (0..7) */
INT8U OSTCBY; /* Index into ready table corresponding to task priority */
INT8U OSTCBBitX; /* Bit mask to access bit position in ready table */
INT8U OSTCBBitY; /* Bit mask to access bit position in ready group */
#if OS_TASK_DEL_EN
BOOLEAN OSTCBDelReq; /* Indicates whether a task needs to delete itself */
#endif
} OS_TCB;
OSTCBStat用来存放任务的当前状态:
/* TASK STATUS (Bit definition for OSTCBStat) */
#define OS_STAT_RDY 0x00 /* Ready to run */
#define OS_STAT_SEM 0x01 /* Pending on semaphore */
#define OS_STAT_MBOX 0x02 /* Pending on mailbox */
#define OS_STAT_Q 0x04 /* Pending on queue */
#define OS_STAT_SUSPEND 0x08 /* Task is suspended */
任务控制块链表(学习实时性的链表处理方法)
uC/OS-II用两条链表管理任务控制块:一条是空任务控制块链表(其中所有任务控制块还没有分配给任务);另一条是任务控制块链表(其中所有任务控制块已经分配给任务了)。
众所周知,在这种链表例查找一个元素有时是很费时的,而且查找时间还与目标元素在链表中的位置有关,有某种不确定性,这两种情况都是实时系统所忌讳的。所以,为了加快对任务控制块的访问速度,除了任务控制块链表被创建为双向链表之外,uC/OS-II在uC/OS-II.H中还定义了一个数据类型为0S_TCB* DE 数组OSTCBTbl[],专门用来存放指向各任务控制块的指针,并按任务的优先级别把这些指针存放数组的各元素里。这样,在访问某个任务的任务控制块时,就可以按照任务的优先级别直接从数组OSTCBTbl[]的对应元素中获得该任务控制块的指针,并通过它直接找到该任务控制块了。
任务堆栈
在文件OS_CUP.H中专门定义了一个数据类型OS_STK:
typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */
在应用程序中定义一个OS_STK类型的一个数组即可。
#define TASK_STK_SIZE 512 /* Size of each task's stacks (# of WORDs) */
#define N_TASKS 10 /* Number of identical tasks */
OS_STK TaskStk[N_TASKS][TASK_STK_SIZE]; /* Tasks stacks */
OS_STK TaskStartStk[TASK_STK_SIZE];
需要注意的是,堆栈的增长方向是随系统使用的处理器的不同而不同的,利用OS_CFG.H文件中的常数OS_STK_GROWTH作为选择开关,选择不同的增长方向。
任务堆栈的初始化
应用程序在创建一个新任务时,就必须把在系统启动这个任务时处理器个寄存器所需的初始化数据(任务指针,任务堆栈指针程序状态字等)事先存放在这个任务的堆栈中。这个工作是在创建任务函数OSTaskCreate()中通过调用任务堆栈初始化函数OSTaskStkInit()来完成的。
void *OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
该函数需要用户在进行uC/OS-II的移植时按所使用的处理器由用户来编写。
系统任务
uC/OS-II预定义了两个为应用程序服务的系统任务:空闲任务(OSTaskIdle())和统计任务(OSTaskStat())。
临界区
uC/OS-II中的临界区,值得就是一个特殊代码段。如果在程序中有一段代码在执行期间不允许中断,那么它就是具有原子性的代码段。这种代码段在uC/OS-II中叫做临界区。
在uC/OS-II中用宏OS_ENTER_CRITICAL()关闭中断,而用OS_EXIT_CRITICAL()打开中断。(在OS_CPU.H中)
OS_ENTER_CRITICAL();
……//临界区
OS_EXIT_CRITICAL();