任务的唤醒与挂起

typedef struct _tTask {
    tTaskStack * stack;
    // 连接结点
    tNode linkNode;
    // 任务延时计数器
    uint32_t delayTicks;
    // 延时结点:通过delayNode就可以将tTask放置到延时队列中
    tNode delayNode;
    // 任务的优先级
    uint32_t prio;
    // 任务当前状态
    uint32_t state;
    // 当前剩余的时间片
    uint32_t slice;
    // 被挂起的次数
    uint32_t suspendCount;
 }tTask;

在tTask中新增挂起次数,用于合理控制挂起和唤醒,具体思路可以见调度锁处

void tTaskSuspend (tTask * task) 
{
    // 进入临界区
    uint32_t status = tTaskEnterCritical();
    // 不允许对已经进入延时状态的任务挂起
    if (!(task->state & TINYOS_TASK_STATE_DELAYED)) 
    {
        // 增加挂起计数,仅当该任务被执行第一次挂起操作时,才考虑是否
        // 要执行任务切换操作
        if (++task->suspendCount <= 1)
        {
            // 设置挂起标志
            task->state |= TINYOS_TASK_STATE_SUSPEND;
            // 挂起方式很简单,就是将其从就绪队列中移除,这样调度器就不会发现他
            // 也就没法切换到该任务运行
            tTaskSchedUnRdy(task);
            // 当然,这个任务可能是自己,那么就切换到其它任务
            if (task == currentTask)
            {
                tTaskSched();
            }
        }
    }
    // 退出临界区
    tTaskExitCritical(status); 
}
void tTaskWakeUp (tTask * task)
{
    // 进入临界区
    uint32_t status = tTaskEnterCritical();
     // 检查任务是否处于挂起状态
    if (task->state & TINYOS_TASK_STATE_SUSPEND)
    {
        // 递减挂起计数,如果为0了,则清除挂起标志,同时设置进入就绪状态
        if (--task->suspendCount == 0) 
        {
            // 清除挂起标志
            task->state &= ~TINYOS_TASK_STATE_SUSPEND;
            // 同时将任务放回就绪队列中
            tTaskSchedRdy(task);
            // 唤醒过程中,可能有更高优先级的任务就绪,执行一次任务调度
            tTaskSched();
        }
    }
    // 退出临界区
    tTaskExitCritical(status); 
}

tTaskSuspend中,先对任务进行判断是否处于延时,然后对挂起任务的计数加加,第一次挂起的时候会判断当前任务是否是自己,是则切换任务

tTaskWakeUp中,这个函数比tTaskSuspend思路简单一点,只需要对挂起任务的计数减减,当减到0的时候,将任务加入到任务队列中,再开启调度

任务删除

typedef struct _tTask {
    tTaskStack * stack;
    // 连接结点
    tNode linkNode;
    // 任务延时计数器
    uint32_t delayTicks;
    // 延时结点:通过delayNode就可以将tTask放置到延时队列中
    tNode delayNode;
    // 任务的优先级
    uint32_t prio;
    // 任务当前状态
    uint32_t state;
    // 当前剩余的时间片
    uint32_t slice;
    // 被挂起的次数
    uint32_t suspendCount;
    // 任务被删除时调用的清理函数
    void (*clean) (void * param);
    // 传递给清理函数的参数
    void * cleanParam;
    // 请求删除标志,非0表示请求删除
    uint8_t requestDeleteFlag;
 }tTask;
 void tTaskSetCleanCallFunc (tTask * task, void (*clean)(void * param), void * param) 
{
    task->clean = clean;
    task->cleanParam = param;
}

tTask新增clean清理函数,清理函数的参数和requestDeleteFlag删除标志

tTaskSetCleanCallFunc中,是设置任务删除调用的清理函数,具体用法如下,其中task1DestroyFunc就是清理函数

tTaskSetCleanCallFunc(currentTask, task1DestroyFunc, (void *)0);

tTaskinit中作出以下修改,clean置0的初始化的目的是我们可以不采用清理函数

    task->clean = (void(*)(void *))0;                   // 设置清理函数
    task->cleanParam = (void *)0;                       // 设置传递给清理函数的参数
    task->requestDeleteFlag = 0;                        // 请求删除标记

总共有两种删除方式,一种是强制删除,另外一种是设置删除标志

强制删除

void tTaskForceDelete (tTask * task) 
{
    // 进入临界区
    uint32_t status = tTaskEnterCritical();
     // 如果任务处于延时状态,则从延时队列中删除
    if (task->state & TINYOS_TASK_STATE_DELAYED) 
    {
        tTimeTaskRemove(task);
    }
		// 如果任务不处于挂起状态,那么就是就绪态,从就绪表中删除
    else if (!(task->state & TINYOS_TASK_STATE_SUSPEND))
    {
        tTaskSchedRemove(task);
    }
    // 删除时,如果有设置清理函数,则调用清理函数
    if (task->clean) 
    {
        task->clean(task->cleanParam);
    }
    // 如果删除的是自己,那么需要切换至另一个任务,所以执行一次任务调度
    if (currentTask == task) 
    {
        tTaskSched();
    }
    // 退出临界区
    tTaskExitCritical(status); 
}

tTaskForceDelete逻辑比较简单,在所有队列中移除,调用清理回调函数,最好再判断切换

设置删除标志——自主删除

void tTaskRequestDelete (tTask * task)
{
   // 进入临界区
    uint32_t status = tTaskEnterCritical();
    // 设置清除删除标记
    task->requestDeleteFlag = 1;
    // 退出临界区
    tTaskExitCritical(status); 
}
uint8_t tTaskIsRequestedDelete (void)
{
    uint8_t delete;
   // 进入临界区
    uint32_t status = tTaskEnterCritical();
    // 获取请求删除标记
    delete = currentTask->requestDeleteFlag;
    // 退出临界区
    tTaskExitCritical(status); 
    return delete;
}
void tTaskDeleteSelf (void)
{
    // 进入临界区
    uint32_t status = tTaskEnterCritical();
    // 任务在调用该函数时,必须是处于就绪状态,不可能处于延时或挂起等其它状态
    // 所以,只需要从就绪队列中移除即可
    tTaskSchedRemove(currentTask);
    // 删除时,如果有设置清理函数,则调用清理函数
    if (currentTask->clean)
    {
        currentTask->clean(currentTask->cleanParam);
    }
    // 接下来,肯定是切换到其它任务去运行
    tTaskSched();
    // 退出临界区
    tTaskExitCritical(status);
}

tTaskIsRequestedDelete是处于任务或者定时器之类的周期运行中;重点集中在tTaskDeleteSelf上,这个函数是放在任务中,这样状态处于就绪态,操作更为简单,任务也必定需要切换为其他任务

任务状态

typedef struct _tTaskInfo {
    // 任务延时计数器
    uint32_t delayTicks;
    // 任务的优先级
    uint32_t prio;
    // 任务当前状态
    uint32_t state;
    // 当前剩余的时间片
    uint32_t slice;
    // 被挂起的次数
    uint32_t suspendCount;
}tTaskInfo;

新增tTaskInfo结构体,用于储存任务状态

void tTaskGetInfo (tTask * task, tTaskInfo * info)
{
   // 进入临界区
    uint32_t status = tTaskEnterCritical();
    info->delayTicks = task->delayTicks;                // 延时信息
    info->prio = task->prio;                            // 任务优先级
    info->state = task->state;                          // 任务状态
    info->slice = task->slice;                          // 剩余时间片
    info->suspendCount = task->suspendCount;            // 被挂起的次数
    // 退出临界区
    tTaskExitCritical(status); 
}

做状态查询的目的是避免过多使用全局变量,这种思路在大多项目都采用