事件控制块
我们需要了解事件控制块一般是不需要直接使用的,是作为信号量,消息队列等的底层
事件控制块的原理和创建
typedef enum _tEventType {
tEventTypeUnknown = 0, // 未知类型
}tEventType;
// Event控制结构
typedef struct _tEvent {
tEventType type; // Event类型
tList waitList; // 任务等待列表
}tEvent;
增加tEvent结构体,目前存放等待列表和Event类型
// 任务正在等待的事件类型
struct _tEvent * waitEvent;
// 等待事件的消息存储位置
void * eventMsg;
// 等待事件的结果
uint32_t waitEventResult;
在tTask加入以上内容,用于保存事件内容状态
void tEventInit (tEvent * event, tEventType type)
{
event->type = type;
tListInit(&event->waitList);
}
初始化主要是对类型初始化,以及等待列表
事件等待与通知


这里相比之前,又引入了新的任务状态,也就是等待状态,由事件控制块管理
void tEventWait (tEvent * event, tTask * task, void * msg, uint32_t state, uint32_t timeout)
{
// 进入临界区
uint32_t status = tTaskEnterCritical();
task->state |= state; // 标记任务处于等待某种事件的状态
task->waitEvent = event; // 设置任务等待的事件结构
task->eventMsg = msg; // 设置任务等待事件的消息存储位置
// 因有时候需要接受消息,所以需要接受区
task->waitEventResult = tErrorNoError; // 清空事件的等待结果
// 将任务从就绪队列中移除
tTaskSchedUnRdy(task);
// 将任务插入到等待队列中
tListAddLast(&event->waitList, &task->linkNode);
// 如果发现有设置超时,在同时插入到延时队列中
// 当时间到达时,由延时处理机制负责将任务从延时列表中移除,同时从事件列表中移除
if (timeout)
{
tTimeTaskWait(task, timeout);
}
// 退出临界区
tTaskExitCritical(status);
}
tTask * tEventWakeUp (tEvent * event, void * msg, uint32_t result)
{
tNode * node;
tTask * task = (tTask * )0;
// 进入临界区
uint32_t status = tTaskEnterCritical();
// 取出等待队列中的第一个结点
if((node = tListRemoveFirst(&event->waitList)) != (tNode *)0)
{
// 转换为相应的任务结构
task = (tTask *)tNodeParent(node, tTask, linkNode);
// 设置收到的消息、结构,清除相应的等待标志位
task->waitEvent = (tEvent *)0;
task->eventMsg = msg;
task->waitEventResult = result;
task->state &= ~TINYOS_TASK_WAIT_MASK;
// 任务申请了超时等待,这里检查下,将其从延时队列中移除
if (task->delayTicks != 0)
{
tTimeTaskWakeUp(task);
}
// 将任务加入就绪队列
tTaskSchedRdy(task);
}
// 退出临界区
tTaskExitCritical(status);
return task;
}
void tEventRemoveTask (tTask * task, void * msg, uint32_t result)
{
// 进入临界区
uint32_t status = tTaskEnterCritical();
// 将任务从所在的等待队列中移除
// 注意,这里没有检查waitEvent是否为空。既然是从事件中移除,那么认为就不可能为空
tListRemove(&task->waitEvent->waitList, &task->linkNode);
// 设置收到的消息、结构,清除相应的等待标志位
task->waitEvent = (tEvent *)0;
task->eventMsg = msg;
task->waitEventResult = result;
task->state &= ~TINYOS_TASK_WAIT_MASK;
// 退出临界区
tTaskExitCritical(status);
}
分为三个函数,可以通俗的理解为挂起事件控制块,唤醒事件控制块和删除事件
tEventWait主要内容是先设置状态和传给task参数,然后移除就绪队列,添加进入等待队列中,最后再查看是否有超时参数,有则加入延时队列,至于调度,由于这些是用于底层的,所以以上函数并不会出现调度函数,调度处理主要是在更细致的控制块中
tEventWakeUp中,首先取出等待列表中的任务,直接移出延时队列,加入就绪队列
tEventRemoveTask中,删除的是一个任务正在等待的事件,最后再设置标志位等参数
事件控制块的清空和状态查询
事件控制块清空
uint32_t tEventRemoveAll (tEvent * event, void * msg, uint32_t result)
{
tNode * node;
uint32_t count;
// 进入临界区
uint32_t status = tTaskEnterCritical();
// 获取等待中的任务数量
count = tListCount(&event->waitList);
// 遍历所有等待中的任务
while ((node = tListRemoveFirst(&event->waitList)) != (tNode *)0)
{
// 转换为相应的任务结构
tTask * task = (tTask *)tNodeParent(node, tTask, linkNode);
// 设置收到的消息、结构,清除相应的等待标志位
task->waitEvent = (tEvent *)0;
task->eventMsg = msg;
task->waitEventResult = result;
task->state &= ~TINYOS_TASK_WAIT_MASK;
// 任务申请了超时等待,这里检查下,将其从延时队列中移除
if (task->delayTicks != 0)
{
tTimeTaskWakeUp(task);
}
// 将任务加入就绪队列
tTaskSchedRdy(task);
}
// 退出临界区
tTaskExitCritical(status);
return count;
}
不难看出,tEventRemoveAll相比tEventRemoveTask多了一个while循环,直到event中等待列表删除干净,也就是tListRemoveFirst返回0即可
事件控制块状态查询
uint32_t tEventWaitCount (tEvent * event)
{
uint32_t count = 0;
// 进入临界区
uint32_t status = tTaskEnterCritical();
count = tListCount(&event->waitList);
// 退出临界区
tTaskExitCritical(status);
return count;
}
对于事件控制块所需要的只有等待列表中任务数量,所以只需要查询等待列表中任务数量