uCOS-II实时操作系统:任务


本文的主要目的是学习uCOS II操作系统,俗话说好记性不如烂笔头,这里主要学习“任务”“中断和时钟”“任务的同步和通信”“动态内存管理”我认为比较重要的几块内容,这块学习内容会持续更新,直到完成~

2024-07-31 2024-08-01 2024-08-03 2024-08-05 2024-08-07 2024-08-09 2024-08-11 2024-08-13 已完成 进行中 计划中 现有任务 Adding GANTT diagram functionality to mermaid

第三章 任务

任务的基础知识

操作系统调度的最小资源就是任务,可以看到一个任务的核心是任务控制块,其相当于人的身份证,身份证包含人的个人信息,则任务控制块也包含任务的所有信息,任务的组成如下所示:
在这里插入图片描述
上面是一个任务,操作系统要对多个任务进行管理,则登记造册得到任务链表:
在这里插入图片描述
注意,这里和任务代码一样,任务堆栈也需要进行申请(任务堆栈的创建):

typedef  unsigned int  OS_STK  //4字节无符号整型

#define  TASK_STK_SIZE  32
OS_STK  TaskStk[TASK_STK_SIZE  ]

以上给出任务组成和任务链表,下面给出实际的任务控制块结构体。另外,为了提高任务控制块的申请效率,实际上是系统给出了两条任务控制块链表:一条是提前申请好的空白任务控制块链表,还有一条是任务控制块链表。

任务控制块结构
typedef struct os_tcb {
    OS_STK          *OSTCBStkPtr;           /* 定义一个无符号整型指向stk的栈顶指针                        */
#if OS_TASK_CREATE_EXT_EN > 0u  //如果延申创建标志位=1,那句创建一些延申参数
    void            *OSTCBExtPtr;           /* 用户自定义的数据指针                                     */
    OS_STK          *OSTCBStkBottom;        /* 指向栈底的站在这                                         */
    INT32U           OSTCBStkSize;          /* 栈大小   */
    INT16U           OSTCBOpt;              /*任务选项  */
    INT16U           OSTCBId;               /* 任务ID  */
#endif

    struct os_tcb   *OSTCBNext;             /* 结构体指针OSTCBNext:相对于OS_TCB{OSTCBNext{...}},结构体里的结构体,构成链表                 */
    struct os_tcb   *OSTCBPrev;             /*OS_TCB{OSTCBPrev{...}},结构体里的结构体,构成链表                      */

#if (OS_EVENT_EN)
    OS_EVENT        *OSTCBEventPtr;         /* 指向事件控制块的指针                */
#endif
......
#if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)
    void            *OSTCBMsg;              /* Message received from OSMboxPost() or OSQPost()         */
#endif
......
    INT32U           OSTCBDly;              /* 任务延时时间片   */
    INT8U            OSTCBStat;             /* 任务状态                                             */
    INT8U            OSTCBStatPend;         /* Task PEND status                                        */
    INT8U            OSTCBPrio;             /* 任务的优先级                           */

    INT8U            OSTCBX;                /* 优先级位图法用到的结构    */
    INT8U            OSTCBY;                /* 优先级位图法用到的结构    */
    OS_PRIO          OSTCBBitX;             /* 优先级位图法用到的结构          */
    OS_PRIO          OSTCBBitY;             /* 优先级位图法用到的结构          */

#if OS_TASK_DEL_EN > 0u
    INT8U            OSTCBDelReq;           /*删除任务请求,是否需要删除自己       */
#endif

......//具体全部可以查看系统源码
} OS_TCB;

在这里插入图片描述

系统初始化时创建一个空任务控制块链表

在这里插入图片描述
这里注意,为了方便快速访问任务,系统创建了一个数组OSTCBPrioTb1[],下标对应任务的优先级(为任务的唯一标识ID),数组内存放指向各个任务控制块的指针;其中,系统定义了一个变量OSTCBCur存放当前任务控制块指针

任务的调度

任务的调度是操作系统的核心。调度器需要完成两项任务,一是判断哪些任务处于就绪状态,这部分对应就绪表的知识;二是进行实际的任务调度。
就绪表就是一个位图,表的下标对应任务优先级,每一个任务占据表中的一位,这里系统给出了一个类型为INT8U的OSRdyTb1[]数组。
为了便于对就绪表进行查找,系统还定义了一个数据类型为INT8U的变量OSRdyGrp,其中每一位对应OSRdyTb1[]的一个元素(即一个INT8U的元素)。
在这里插入图片描述
注意对于就绪表的操作主要有三个,即为登记、注销以及得到就绪的最高优先级

下面即为实际的任务调度,主要是获得就绪的优先级任务的任务控制块指针(操作就绪表得到)和当前任务的任务控制块指针(在OSTCBCur变量中)。下面给出任务级调度器OSSched的源码:

void  OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3u                           /* Allocate storage for CPU status register     */
    OS_CPU_SR  cpu_sr = 0u;
#endif

    OS_ENTER_CRITICAL();
    if (OSIntNesting == 0u) {                          /* Schedule only if all ISRs done and ...       */
        if (OSLockNesting == 0u) {                     /* ... scheduler is not locked                  */
            OS_SchedNew();
            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//得到任务控制块指针
            if (OSPrioHighRdy != OSPrioCur) {          /* No Ctx Sw if current task is highest rdy     */
#if OS_TASK_PROFILE_EN > 0u
                OSTCBHighRdy->OSTCBCtxSwCtr++;         /* Inc. # of context switches to this task      */
#endif
                OSCtxSwCtr++;                          /* 统计切换次数的计数器加1             */
                OS_TASK_SW();                          /* Perform a context switch                     */
            }
        }
    }
    OS_EXIT_CRITICAL();
}
#define  OS_TASK_SW()         OSCtxSw()

任务的切换工作是靠OSCtxSw()实现的。这给出任务切换的示意图和具体代码:
在这里插入图片描述
①和②的部分为:把被终止任务的断点指针(这里理解为PC指针)保存到任务堆栈中;把通用寄存器中的数据保存到任务堆栈中;还需要把被终止任务堆栈指针保存到任务的任务控制块的OSTCBStkPtr指针中。
③和④的部分为:获得待运行任务控制块,CPU通过该任务控制块获得待运行任务的任务堆栈指针;将任务堆栈中的通用寄存器内容恢复到处理器中;CPU获得待运行任务的断点指针

;OSCtxSw()具体代码实现:
OSCtxSw
		PUSH    {R4, R5}
        LDR     R4, =NVIC_INT_CTRL  	;触发PendSV异常 (causes context switch)
        LDR     R5, =NVIC_PENDSVSET
        STR     R5, [R4]
		POP     {R4, R5}
        BX      LR

任务的创建

创建任务的实质就是创建任务控制块,并通过任务控制块将任务代码和任务堆栈关联起来,形成一个任务。应用程序通过调用函数OSTaskCreate()来创建一个任务,下面给出其具体代码:

INT8U  OSTaskCreate (void   (*task)(void *p_arg), //指向任务的指针
                     void    *p_arg,  //传递给任务的参数
                     OS_STK  *ptos,  //指向任务堆栈栈顶的指针
                     INT8U    prio)  //任务的优先级
{
    OS_STK    *psp;
    INT8U      err;
#if OS_CRITICAL_METHOD == 3u                 /* Allocate storage for CPU status register               */
    OS_CPU_SR  cpu_sr = 0u;
#endif



#ifdef OS_SAFETY_CRITICAL_IEC61508
    if (OSSafetyCriticalStartFlag == OS_TRUE) {
        OS_SAFETY_CRITICAL_EXCEPTION();
    }
#endif

#if OS_ARG_CHK_EN > 0u
    if (prio > OS_LOWEST_PRIO) {             /* 检测任务的优先级是否合法           */
        return (OS_ERR_PRIO_INVALID);
    }
#endif
    OS_ENTER_CRITICAL();
    if (OSIntNesting > 0u) {                 /* 操作系统不允许在终端服务函数中创建任务  */
        OS_EXIT_CRITICAL();
        return (OS_ERR_TASK_CREATE_ISR);
    }
    if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority  */
        OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ...  */
                                             /* ... the same thing until task is created.              */
        OS_EXIT_CRITICAL();
        psp = OSTaskStkInit(task, p_arg, ptos, 0u);             /* 初始化任务堆栈         */
        err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u);  /* 获得并初始化任务控制块*/
        if (err == OS_ERR_NONE) {
            if (OSRunning == OS_TRUE) {      /* Find highest priority task if multitasking has started */
                OS_Sched();  //任务调度
            }
        } else {
            OS_ENTER_CRITICAL();
            OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others                 */
            OS_EXIT_CRITICAL();
        }
        return (err);
    }
    OS_EXIT_CRITICAL();
    return (OS_ERR_PRIO_EXIST);
}

以上就是操作系统任务的比较核心的内容,下面是对应的任务的挂起、恢复和删除的内容。

任务的挂起和恢复和删除

任务的挂起和恢复都是针对自身和除空闲任务之外的其它任务。 函数OSTaskSuspend()用于挂起任务,使其进入等待状态,然后只能在其他任务中调用恢复函数OSTaskResume()恢复其为就绪状态。其函数原型如下:

挂起任务函数原型:
INT8U OSTaskSuspend(INT8U prio)
恢复任务函数原型
INT8U OSTaskResume(INT8U prio)

这里prio是任务的优先级别,如果任务要挂起自身,则函数参数必须为常量OS_PRIO_SELF,该常量定义在uCOS_II.H中

任务的删除就是将任务控制块从任务控制块链表中删除,归还到空任务控制块链表,同时将任务就绪表置零。函数OSTaskDel()用来删除自身或除空闲任务之外的其它任务。

删除任务函数原型
INT8U OSTaskDel(INT8U prio)

务必注意,考虑到任务会动态分配内存和信号量等资源,直接删除任务而不释放这部分内存,可能会造成内存泄漏,因此应当谨慎删除一个占用资源的任务,比较可行办法是,提出删除任务请求的任务只负责提出删除任务请求(使用INT8U OSTaskDelReq(INT8U prio)函数提出请求),而删除工作则由被删除任务自己(1)释放资源(2)调用OSTaskDel()删除自己。

参考:https://blog.csdn.net/weixin_43491077/article/details/115802619

第四章 中断和时钟

第五章 任务的同步和通信

第七章 动态内存管理

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值