多优先级任务

1 多优先级任务

1.1 设计目标

问题引入:

  • 任务数量无限,而资源有限。当多个任务在等待同一资源/事件时,如果资源/事件不能在任务间共享,应该有哪个任务来处理?
    在这里插入图片描述

1.2 优先级原理

在此我们只讨论CPU占用的优先级:

  • RTOS维护一个就绪表,每个表项对应一个任务,对应一种优先级。就绪表指明哪些优先级的任务等待占用CPU运行。
  • 为便于快速找到优先级更高的任务运行,使用了就绪位置标记就绪,快速查找。
    在这里插入图片描述
    在这里插入图片描述

1.3 设计实现

添加优先级字段:
在这里插入图片描述
添加优先级位图表:
在这里插入图片描述
添加获取当前最高优先级任务的函数:

/**********************************************************************************************************
** Function name        :   tTaskHighestReady
** Descriptions         :   获取当前最高优先级且可运行的任务
** parameters           :   无
** Returned value       :   优先级最高的且可运行的任务
***********************************************************************************************************/
tTask * tTaskHighestReady (void) 
{
    uint32_t highestPrio = tBitmapGetFirstSet(&taskPrioBitmap);
    return taskTable[highestPrio];
}

修改任务初始化函数:

/**********************************************************************************************************
** Function name        :   tTaskInit
** Descriptions         :   初始化任务结构
** parameters           :   task        要初始化的任务结构
** parameters           :   entry       任务的入口函数
** parameters           :   param       传递给任务的运行参数
** Returned value       :   无
***********************************************************************************************************/
void tTaskInit (tTask * task, void (*entry)(void *), void *param, uint32_t prio, uint32_t * stack)
{
    // 为了简化代码,tinyOS无论是在启动时切换至第一个任务,还是在运行过程中在不同间任务切换
    // 所执行的操作都是先保存当前任务的运行环境参数(CPU寄存器值)的堆栈中(如果已经运行运行起来的话),然后再
    // 取出从下一个任务的堆栈中取出之前的运行环境参数,然后恢复到CPU寄存器
    // 对于切换至之前从没有运行过的任务,我们为它配置一个“虚假的”保存现场,然后使用该现场恢复。

    // 注意以下两点:
    // 1、不需要用到的寄存器,直接填了寄存器号,方便在IDE调试时查看效果;
    // 2、顺序不能变,要结合PendSV_Handler以及CPU对异常的处理流程来理解
    *(--stack) = (unsigned long)(1<<24);                // XPSR, 设置了Thumb模式,恢复到Thumb状态而非ARM状态运行
    *(--stack) = (unsigned long)entry;                  // 程序的入口地址
    *(--stack) = (unsigned long)0x14;                   // R14(LR), 任务不会通过return xxx结束自己,所以未用
    *(--stack) = (unsigned long)0x12;                   // R12, 未用
    *(--stack) = (unsigned long)0x3;                    // R3, 未用
    *(--stack) = (unsigned long)0x2;                    // R2, 未用
    *(--stack) = (unsigned long)0x1;                    // R1, 未用
    *(--stack) = (unsigned long)param;                  // R0 = param, 传给任务的入口函数
    *(--stack) = (unsigned long)0x11;                   // R11, 未用
    *(--stack) = (unsigned long)0x10;                   // R10, 未用
    *(--stack) = (unsigned long)0x9;                    // R9, 未用
    *(--stack) = (unsigned long)0x8;                    // R8, 未用
    *(--stack) = (unsigned long)0x7;                    // R7, 未用
    *(--stack) = (unsigned long)0x6;                    // R6, 未用
    *(--stack) = (unsigned long)0x5;                    // R5, 未用
    *(--stack) = (unsigned long)0x4;                    // R4, 未用

    task->stack = stack;                                // 保存最终的值
    task->delayTicks = 0;
    task->prio = prio;                                  // 设置任务的优先级

    taskTable[prio] = task;                             // 填入任务优先级表
    tBitmapSet(&taskPrioBitmap, prio);                  // 标记优先级位置中的相应位
}

修改tTaskDelay函数:

/**********************************************************************************************************
** Function name        :   tTaskDelay
** Descriptions         :   使当前任务进入延时状态。
** parameters           :   delay 延时多少个ticks
** Returned value       :   无
***********************************************************************************************************/
void tTaskDelay (uint32_t delay) {
   // 配置好当前要延时的ticks数
    uint32_t status = tTaskEnterCritical();
    currentTask->delayTicks = delay;
    tBitmapClear(&taskPrioBitmap, currentTask->prio);
    tTaskExitCritical(status);

    // 然后进行任务切换,切换至另一个任务,或者空闲任务
    // delayTikcs会在时钟中断中自动减1.当减至0时,会切换回来继续运行。
    tTaskSched();
}

修改tTaskSystemTickHandler函数:

/**********************************************************************************************************
** Function name        :   tTaskSystemTickHandler
** Descriptions         :   系统时钟节拍处理。
** parameters           :   无
** Returned value       :   无
***********************************************************************************************************/
void tTaskSystemTickHandler () 
{
    // 检查所有任务的delayTicks数,如果不0的话,减1。
    int i;   
    uint32_t status = tTaskEnterCritical();

    for (i = 0; i < TINYOS_PRO_COUNT; i++) 
    {
        if (taskTable[i]->delayTicks > 0)
        {
            taskTable[i]->delayTicks--;
        }
        else 
        {
            tBitmapSet(&taskPrioBitmap, i);
        }
    }
    tTaskExitCritical(status);

    // 这个过程中可能有任务延时完毕(delayTicks = 0),进行一次调度。
    tTaskSched();
}

修改调度算法:

/**********************************************************************************************************
** Function name        :   tTaskSched
** Descriptions         :   任务调度接口。tinyOS通过它来选择下一个具体的任务,然后切换至该任务运行。
** parameters           :   无
** Returned value       :   无
***********************************************************************************************************/
void tTaskSched (void) 
{   
    tTask * tempTask;

    // 进入临界区,以保护在整个任务调度与切换期间,不会因为发生中断导致currentTask和nextTask可能更改
    uint32_t status = tTaskEnterCritical();

    // 如何调度器已经被上锁,则不进行调度,直接退bm
    if (schedLockCount > 0) 
    {
        tTaskExitCritical(status);
        return;
    }

    // 找到优先级最高的任务,如果其优先级比当前任务的还高,那么就切换到这个任务
    tempTask = tTaskHighestReady();
    if (tempTask != currentTask) 
    {
        nextTask = tempTask;
        tTaskSwitch();   
    }

    // 退出临界区
    tTaskExitCritical(status); 
}

参考资料:

  1. 【李述铜】从0到1自己动手写嵌入式操作系统
任务多线程管理模块,任务优先级,一个任务执行完毕,按照优先级高低执行另一个任务 1)如何使用: 1. 声明一个HashTaskList,或在栈上动态获取; 2. 调用InitTaskList初始化上一步的HashTaskList 3. 调用StartTaskManager开始启动任务管理 ***** 对于一个HashTaskList而言,以上2个函数只调用一次 4. 调用ApplyTask向第一步得到的结构对象添加任务,每调用一次添一个任务 2)如何终止任务管理功能: 1. 程序结束,终止程序既停止管理并且释放所有资源,请看2).2括号的内容 2. 调用某个函数,这个功能未写(考虑到模块作为服务运行,不需要终止任务管理的函数) 需要取消所有1v1管理线程,在此需要一个列表保存这些线程的ID 需要清理表当中的互斥锁和信号变量,如果1).1.是动态分配,要释放 3)关于改进: 1. 回收资源的线程和TaskList reclaim[LEVEL_ARRAY_LIMIT];是可以省掉的 但考虑到模块功能的延伸: 如果任务执行失败,先移动到某个地方,过段时间在放回来重试,等等... 其实还有很多可能用到它的地方...所以,依旧保留 2. 有多少个任务可以并发管理目前是固定的 其实可添加一个对StartTaskManager所生成的线程管理的数据结构 这样就能动态的增加或减少任务管理的1v1线程 ***** 可事实上我并不想做这个管理的东西在里面 ***** 没有必要,对提供服务的程序而言这东西一般是固定不变的 ********************************************* 1) How to use: 1. Declare a HashTaskList, or dynamic access stack; 2. Call the initialization of step HashTaskList InitTaskList
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值