邮箱
邮箱原理和创建
邮箱原理

以上是邮箱的大致思路,使用了两个队列,一个用于作为信息缓存—当然是有长度的,一个用于存放等待任务,我们还需要理解这两个队列运行逻辑,两个任务任何一个为空的时候,另外一个必定为非空

对于消息的缓存,采用了读和写索引,有消息进入的时候,通过写索引写入,有消息读出的时候,通过读索引读出
可以看到是一个环形缓冲区,但如下我们创建的是一个数组,我们加入写索引和读索引来实现环形缓冲的功能,除此以外,我们还想在缓冲区中有优先级高消息写入,那实现方法大体是读索引往前移动,写入到读索引上,而写索引往后移动
邮箱创建
typedef struct _tMbox
{
// 事件控制块
// 该结构被特意放到起始处,以实现tSem同时是一个tEvent的目的
tEvent event;
// 当前的消息数量
uint32_t count;
// 读取消息的索引
uint32_t read;
// 写消息的索引
uint32_t write;
// 最大允许容纳的消息数量
uint32_t maxCount;
// 消息存储缓冲区
void ** msgBuffer;
}tMbox;
这里的重点是msgBuffer,他是一个指向指针的指针,可以理解为数组指针
- void * 可以表示任意类型的指针
- void ** 则是一个指向指针的指针,表示一个可以存储多个指针的数组
void tMboxInit (tMbox * mbox, void ** msgBuffer, uint32_t maxCount)
{
tEventInit(&mbox->event, tEventTypeMbox);
mbox->msgBuffer = msgBuffer;
mbox->maxCount = maxCount;
mbox->read = 0;
mbox->write = 0;
mbox->count = 0;
}
由于邮箱在功能上也有等待,所以底层也有计数信号量,其余无需赘述
邮箱的获取和释放
uint32_t tMboxWait (tMbox * mbox, void **msg, uint32_t waitTicks)
{
uint32_t status = tTaskEnterCritical();
// 首先检查消息计数是否大于0
if (mbox->count > 0)
{
// 如果大于0的话,取出一个
--mbox->count;
// 取出消息
*msg = mbox->msgBuffer[mbox->read++];
// 同时读取索引前移,如果超出边界则回绕
if (mbox->read >= mbox->maxCount)
{
mbox->read = 0;
}
tTaskExitCritical(status);
return tErrorNoError;
}
else
{
// 然后将任务插入事件队列中
tEventWait(&mbox->event, currentTask, (void *)0, tEventTypeMbox, waitTicks);
tTaskExitCritical(status);
// 最后再执行一次事件调度,以便于切换到其它任务
tTaskSched();
// 当切换回来时,从tTask中取出获得的消息
*msg = currentTask->eventMsg;
// 取出等待结果
return currentTask->waitEventResult;
}
}
uint32_t tMboxNoWaitGet (tMbox * mbox, void **msg)
{
uint32_t status = tTaskEnterCritical();
// 首先检查消息计数是否大于0
if (mbox->count > 0)
{
// 如果大于0的话,取出一个
--mbox->count;
*msg = mbox->msgBuffer[mbox->read++];
// 同时读取索引前移,如果超出边界则回绕
if (mbox->read >= mbox->maxCount)
{
mbox->read = 0;
}
tTaskExitCritical(status);
return tErrorNoError;
}
else
{
// 否则,返回资源不可用
tTaskExitCritical(status);
return tErrorResourceUnavaliable;
}
}
uint32_t tMboxNotify (tMbox * mbox, void * msg, uint32_t notifyOption)
{
uint32_t status = tTaskEnterCritical();
// 检查是否有任务等待
if (tEventWaitCount(&mbox->event) > 0)
{
// 如果有的话,则直接唤醒位于队列首部(最先等待)的任务
tTask * task = tEventWakeUp(&mbox->event, (void *)msg, tErrorNoError )
// 如果这个任务的优先级更高,就执行调度,切换过去
if (task->prio < currentTask->prio)
{
tTaskSched();
}
}
else
{
// 如果没有任务等待的话,将消息插入到缓冲区中
if (mbox->count >= mbox->maxCount)
{
tTaskExitCritical(status);
return tErrorResourceFull;
}
// 可以选择将消息插入到头,这样后面任务获取的消息的时候,优先获取该消息
if (notifyOption & tMBOXSendFront)
{
if (mbox->read <= 0)
{
mbox->read = mbox->maxCount - 1;
}
else
{
--mbox->read;
}
mbox->msgBuffer[mbox->read] = msg;
}
else
{
mbox->msgBuffer[mbox->write++] = msg;
if (mbox->write >= mbox->maxCount)
{
mbox->write = 0;
}
}
// 增加消息计数
mbox->count++;
}
tTaskExitCritical(status);
return tErrorNoError;
}
tMboxWait中,首先检查消息计数是否大于0—邮箱里面是否有邮件,有的话就将消息取出来—赋值给msg,并对计数减减;否则将任务插入事件队列中,但也会对msg操作
tMboxNoWaitGet与tMboxWait大同小异,唯一区别就是不需要对任务进行操作
tMboxNotify中,对判断的对象就改为了任务,有的话就唤醒并调度;否则将消息插入缓冲区中
但是在tMboxNotify不同的是,消息可以选择插入到头还是尾,方便优先获得该消息
邮箱的清空和删除
void tMboxFlush (tMbox * mbox)
{
uint32_t status = tTaskEnterCritical();
// 如果队列中有任务等待,说明邮箱已经是空的了,不需要再清空
if (tEventWaitCount(&mbox->event) == 0)
{
mbox->read = 0;
mbox->write = 0;
mbox->count = 0;
}
tTaskExitCritical(status);
}
uint32_t tMboxDestroy (tMbox * mbox)
{
uint32_t status = tTaskEnterCritical();
// 清空事件控制块中的任务
uint32_t count = tEventRemoveAll(&mbox->event, (void *)0, tErrorDel);
tTaskExitCritical(status);
// 清空过程中可能有任务就绪,执行一次调度
if (count > 0)
{
tTaskSched();
}
return count;
}
tMboxFlush中,对于清空,我们只需要把邮箱中的消息清空
tMboxDestroy中,对于销毁,我们需要把邮箱等待队列中的任务取出,并调度
邮箱的状态查询
typedef struct _tMboxInfo {
// 当前的消息数量
uint32_t count;
// 最大允许容纳的消息数量
uint32_t maxCount;
// 当前等待的任务计数
uint32_t taskCount;
}tMboxInfo;
void tMboxGetInfo (tMbox * mbox, tMboxInfo * info)
{
uint32_t status = tTaskEnterCritical();
// 拷贝需要的信息
info->count = mbox->count;
info->maxCount = mbox->maxCount;
info->taskCount = tEventWaitCount(&mbox->event);
tTaskExitCritical(status);
}
新增tMboxInfo结构体,以及tMboxGetInfo ,用于状态获取