首页 > 其他分享 >优先级

优先级

时间:2022-11-22 13:00:34浏览次数:44  
标签:优先级 链表 任务 pxNewTCB 延时 TCB

rtos中,对任务的调度是按最高优先级的顺序进行的,所以需要对每个任务进行优先级的定义,而有了优先级之后,其他的代码:如结构体,创建任务,切换任务,阻塞延时等都需要相应修改

 

1. 任务TCB结构体:TCB结构体添加优先级属性成员

typedef struct tasTaskControlerBlock
{
    volatile StackType_t *pxTopOfStack;                        //栈顶
    ListItem_t xStateListItem;                                        //任务节点(链表项)
    StackType_t *pxStack;                                                    //任务栈起始地址
    char pcTaskName[configMAX_TASK_NAME_LEN];            //任务名称
    TickType_t xTicksToDelay;                                            //任务延时计数值
    UBaseType_t uxPriority;                                                //优先级
}tskTCB;    
typedef tskTCB    TCB_t;

 

2. 创建任务→初始化任务:初始化任务优先级

static void prvInitialiseNewTask(TaskFunction_t pxTaskCode, const char * const pcName, const uint32_t ulStackDepth, void * pvParameters, UBaseType_t uxPriority, TaskHandle_t * const pxCreatedTask, TCB_t *pxNewTCB)
{
    /*    
        brief:初始化新任务    
        param:
            pxTaskCode:任务函数
            pcName:任务名称
            ulStackDepth:任务栈深
            pvParameters:任务函数传参
            pxCreatedTask:任务句柄,指向TCB
            pxNewTCB:TCB
        return:NULL
    
    */
    
    StackType_t *pxTopOfStack;
    UBaseType_t x;
    
    //计算栈顶
    pxTopOfStack = pxNewTCB->pxStack + (ulStackDepth - (uint32_t)1);
    pxTopOfStack = (StackType_t *)((uint32_t)pxTopOfStack & ~((uint32_t)0x0007));    //向下对齐八个字节
    
    //初始化任务栈(伪造现场)
    pxNewTCB->pxTopOfStack = pxPortInitialiseStack(pxTopOfStack, pxTaskCode, pvParameters);
    
    //初始化任务名称
    for(x = (UBaseType_t)0;x < (UBaseType_t)configMAX_TASK_NAME_LEN;x++)
    {
        pxNewTCB->pcTaskName[x] = pcName[x];
        if(pcName[x] == 0x00)
        {
            break;
        }
    }
    pxNewTCB->pcTaskName[configMAX_TASK_NAME_LEN -1] = '\0';    //最后一个字符为结束符?
    
    //初始化任务节点
    vListInitialiseItem(&(pxNewTCB->xStateListItem));
    
    //设置节点owner
    listSET_LIST_ITEM_OWNER((pxNewTCB->xStateListItem),pxNewTCB);    //关联owner,令该任务节点挂载TCB
    
    //初始化优先级
    if(uxPriority >= (UBaseType_t) configMAX_PRIORITIES)
    {
        uxPriority = (UBaseType_t) configMAX_PRIORITIES - (UBaseType_t) 1U;
    }
    pxNewTCB->uxPriority = uxPriority;
    
    //任务句柄指向TCB
    if((void *) pxCreatedTask != NULL)
    {
        *pxCreatedTask = (TaskHandle_t) pxNewTCB;
    }
}

 

3. 添加任务到就绪链表:需要做三件事:1. 当前任务指向最高任务;2. 更新优先级(保证最高优先级能被记录);3. 插入相应优先级链表尾部

  首先定义添加函数prvAddNewTaskToReadyList。其完成了任务计数加1,当前任务指向

static void prvAddNewTaskToReadyList(TCB_t *pxNewTCB)
{
    taskENTER_CRITICAL();                                    //进入临界区
    
    uxCurrentNumberOfTasks++;                            //添加了新任务,计数加一
    
    if(pxCurrentTCB == NULL)                            //如果当前任务为空,则指向新创建的任务
    {
        pxCurrentTCB = pxNewTCB;
        
        if(uxCurrentNumberOfTasks == (UBaseType_t) 1)        //第一次创建任务则初始化任务列表
        {
            prvInitialiseTaskLists();
        }
    }
    else
    {
        if(pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority)    //保持当前任务始终为最高优先级任务
        {
            pxCurrentTCB = pxNewTCB;
        }
    }
    prvAddTaskToReadyList(pxNewTCB);            //添加到就绪链表

    taskEXIT_CRITICAL();                                    //退出临界区
}

    函数中使用了添加就绪链表的宏定义prvAddTaskToReadyList。其完成了更新优先级,插入相应链表尾部的操作

#define prvAddTaskToReadyList(pxTCB) \
    { /* 更新最高优先级 */ \
        taskRECORD_READY_PRIORITY((pxTCB)->uxPriority);    \
        /* 插入相应优先级尾部 */ \
        vListInsertEnd(&(pxReadyTasksLists[(pxTCB)->uxPriority]),&((pxTCB)->xStateListItem));  \
    }

    最后在创建任务xTaskCreateStatic函数中,完成初始化任务后调用prvAddNewTaskToReadyList函数,由于代码过长不演示

 

4. 任务调度函数:调度函数内核自动创建空闲任务后,再无需将空闲任务手动插入就绪链表和指定当前任务,由3可知,创建任务函数内部已自动将任务加入就绪链表,同时指定当前任务位最高优先级任务,所以把两个相关语句注释

void vTaskStartScheduler(void)
{
    /* 启动任务调度(含有空闲任务创建) */
    TaskHandle_t xIdleTaskHandle;                                            //空闲任务句柄
    TCB_t *pxIdleTaskTCBBuffer = NULL;                                //空闲任务TCB的指针
    StackType_t *pxIdleTaskStackBuffer = NULL;                //空闲任务栈指针
    uint32_t ulIdleTaskStackSize;                                            //空闲任务栈大小
    
    vApplicationGetIdleTaskMemory(&pxIdleTaskTCBBuffer,&pxIdleTaskStackBuffer,&ulIdleTaskStackSize);        //获取空闲任务信息,注意用二重指针
    
    //创建空闲任务
    xIdleTaskHandle = xTaskCreateStatic((TaskFunction_t)prvIdleTask,(char *)"IDLE", (uint32_t)ulIdleTaskStackSize,(void *)NULL, (UBaseType_t)tskIDLE_PRIORITY, (StackType_t *)pxIdleTaskStackBuffer,(TCB_t *)pxIdleTaskTCBBuffer);
    
    //vListInsertEnd(&(pxReadyTasksLists[0]),&(((TCB_t *)pxIdleTaskTCBBuffer)->xStateListItem));  //换成(*xIdleTaskHandle)->xStateListItem呢?

    /* 原本调度器的调度部分 */
    
    //pxCurrentTCB = &Task1TCB;    //指定第一个任务
    
    if(xPortStartScheduler() != pdFALSE)
    {
        //调度器启动失败则进入这里
    }
}

 

5. 阻塞延时:由前面文章可知,在没有优先级的情况下,阻塞延时函数的作用是给任务赋予延时计数值,当有了优先级,就需要将任务从就绪链表移出,放入延时链表(但本章未实现延时链表,所以仅仅是将这一优先级从优先级位图表置零)

void vTaskDelay(const TickType_t xTicksToDelay)
{
    TCB_t *pxTCB = NULL;
    
    //设置当前任务的延时计数
    pxTCB = pxCurrentTCB;
    pxTCB->xTicksToDelay = xTicksToDelay;
    
    // 任务从就绪列表中删除进入延时列表 
    //uxListRemove(&(pxTCB->xStateListItem));
    
    //就绪优先级位置0    
    taskRESET_READY_PRIORITY(pxTCB->uxPriority);

    //任务切换    
    taskYIELD();        
}

 

 

 

6. 切换任务(切换上下文):由前面的文章可知,在没有优先级情况下,仅仅依靠判断任务的延时计数值进行切换,但是,当任务多的时候,其切换顺序或者说判断顺序乱套了(因为是人工决定的),所以,当有优先级概念时,按优先级顺序进行切换

  由下面代码可知,定义一个选择最高优先级任务的宏定义,其完成两件事:获取当前最高优先级,然后按着最高优先级去找到链表的入口链表项(任务),然后将其作为当前任务,这样就实现了任务切换

/* 选择最高优先级的任务(TCB) */
#define taskSELECT_HIGHEST_PRIORITY_TASK() \
{ \
    UBaseType_t uxTopPriority; \
    /* 获得最高优先级 */ \
    portGET_HIGHEST_PRIORITY(uxTopPriority, uxTopReadyPriority); \
    /* 如果是表头则下一项就是第一个链表项,如果是某个已经执行的链表项则是下一个链表项 */ \
    listGET_OWNER_OF_NEXT_ENTRY(pxCurrentTCB, &(pxReadyTasksLists[uxTopPriority])); \
}


void vTaskSwitchContext(void)
{
    /* 上下文切换(任务切换) */
    
    taskSELECT_HIGHEST_PRIORITY_TASK();    //选择优先性最高的任务切换
}

 

7. 计数器递减:由5知道,任务进入阻塞时,将任务从就绪链表移出,放入延时链表;那当任务就绪时要回到就绪链表怎么做?答案就是在计数器递减函数中实现。

  计数器递减函数是对每个优先级任务延时计数值进行减1,同时,如果有任务的延时计数值已经为0,表示延时结束,则将其移出延时链表,加入就绪链表(但这里没有实现延时链表,所以只有实现优先级位图相应优先级置1)

void xTaskIncrementTick(void)
{
    /* 进入计数器递减,并在每一次递减后判断是否有超时任务,进行切换 */
    TCB_t *pxTCB = NULL;
    BaseType_t i = 0;
    
    extern TickType_t xTickCount;
        
    const TickType_t xConstTickCount = xTickCount + 1;
    xTickCount = xConstTickCount;
    
    
    /*    扫描就绪列表中所有优先级链表的第一个任务的xTicksToDelay,如不为0,则减1    */
    for(i=0;i<configMAX_PRIORITIES;i++)
    {
        pxTCB = (TCB_t *)listGET_OWNER_OF_HEAD_ENTRY((&pxReadyTasksLists[i]));
        if(pxTCB->xTicksToDelay > 0)
        {
            pxTCB->xTicksToDelay--;
            /* 如果该任务计数值为0,则放入就绪链表 */
            if(pxTCB->xTicksToDelay == 0)
            {
                taskRECORD_READY_PRIORITY(pxTCB->uxPriority);   //暂时先实现优先级位图置1
            }
        }
    }    
    portYIELD();
}

 

  

标签:优先级,链表,任务,pxNewTCB,延时,TCB
From: https://www.cnblogs.com/toriyung/p/16911094.html

相关文章

  • 设置优先级
    【1】同优先级别的线程,采取的策略就是先到先服务,使用时间片策略【2】如果优先级别高,被cpu调度的概率就高【3】级别1-10 默认的级别为5  【4】代码packagecom.m......
  • vue为什么v-for的优先级比v-if的高?
    前言有时候有些面试中经常会问到v-for与v-if谁的优先级高,这里就通过分析源码去解答一下这个问题。下面的内容是在当我们谈及v-model,我们在讨论什么?的基础上分析的,所以......
  • RabbitMq队列优先级
    RabbitMq队列优先级使用场景在我们系统中有一个订单催付的场景,我们的客户在天猫下的订单,淘宝会及时将订单推送给我们,如果在用户设定的时间内未付款那么就会给用户推送一......
  • Linux系统编程·进程优先级
    你好,我是安然无虞。文章目录​​自学网站​​​​基本概念​​​​查看系统进程​​​​PRIvsNI​​​​其他概念​​​​竞争性​​​​独立性​​​​并行​​​​并发......
  • 49.var声明的函数和function声明的函数谁的优先级更高
    function声明函数的优先级更高;因为function声明函数的时候,是在代码解析之前赋值给变量,此时就已经可以调用了;但是var声明的函数,要在代码运行阶段才会赋值给......
  • HDMI和DP双屏幕连接,哪个优先级高——DP线优先级高于HDMI
    最近被博导忽悠了,说是实验室的国家项目结项了,有几十万的资金没有花掉,于是每个人都有了1W的报销金额,由于是结项所用因此只能报销耗材。我这人呢,平时是绝对不占小便宜的,但这......
  • 运算符优先级
     1#include<stdio.h>2#defineproduct(x)((x)*(x))3intmain(intargc,char**argv){4printf("Hello,World!\n");56inta=5;7......
  • Vue中v-if和v-for一起使用时的优先级
     问题:Vue2.0中v-if和v-for一起使用时报错,怎么解决呢?代码和报错信息如下  原因和解决办法:  在处于同一节点的时候,v-for优先级比v-if高。这意味着v-if将分别......
  • 华为&思科设备默认的路由协议优先级
    华为&思科设备默认的路由协议优先级华为设备默认路由协议优先级在华为的设备中,路由器分别定义了外部优先级和内部优先级。外部优先级是指用户可以手工为各路由协议配......
  • day 22- 线程的礼让,优先级,守护线程
    线程的礼让利用Thread.yield()使线程进行礼让礼让的概念:礼让线程,让当前正在执行的线程暂停,但并不是阻塞将线程从运行状态转化为就绪状态线程礼让是由cpu调度,并......