任务管理
任务的唤醒与挂起
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);
}
做状态查询的目的是避免过多使用全局变量,这种思路在大多项目都采用