计数信号量
计数信号量的原理与创建
计数信号量原理


typedef struct _tSem
{
// 事件控制块
// 该结构被特意放到起始处,以实现tSem同时是一个tEvent的目的
tEvent event;
// 当前的计数
uint32_t count;
// 最大计数
uint32_t maxCount;
}tSem;
计数信号量是一个结构体,包含计数部分(当前计数和最大计数)和事件控制块
计数信号量创建
void tSemInit (tSem * sem, uint32_t startCount, uint32_t maxCount)
{
tEventInit(&sem->event, tEventTypeSem);
sem->maxCount = maxCount;
if (maxCount == 0)
{
sem->count = startCount;
}
else
{
sem->count = (startCount > maxCount) ? maxCount : startCount;
}
}
先对事件控制块初始化,然后根据maxCount限制startCount
计数信号量的获取和释放
uint32_t tSemWait (tSem * sem, uint32_t waitTicks)
{
uint32_t status = tTaskEnterCritical();
// 首先检查信号量计数是否大于0
if (sem->count > 0)
{
// 如果大于0的话,消耗掉一个,然后正常退出
--sem->count;
tTaskExitCritical(status);
return tErrorNoError;
}
else
{
// 然后将任务插入事件队列中
tEventWait(&sem->event, currentTask, (void *)0, tEventTypeSem, waitTicks);
tTaskExitCritical(status);
// 最后再执行一次事件调度,以便于切换到其它任务
tTaskSched();
// 当由于等待超时或者计数可用时,执行会返回到这里,然后取出等待结构
return currentTask->waitEventResult;
}
}
uint32_t tSemNoWaitGet (tSem * sem)
{
uint32_t status = tTaskEnterCritical();
// 首先检查信号量计数是否大于0
if (sem->count > 0)
{
// 如果大于0的话,消耗掉一个,然后正常退出
--sem->count;
tTaskExitCritical(status);
return tErrorNoError;
} else {
// 否则,返回资源不可用
tTaskExitCritical(status);
return tErrorResourceUnavaliable;
}
}
void tSemNotify (tSem * sem)
{
uint32_t status = tTaskEnterCritical();
// 检查是否有任务等待
if (tEventWaitCount(&sem->event) > 0)
{
// 如果有的话,则直接唤醒位于队列首部(最先等待)的任务
tTask * task = tEventWakeUp(&sem->event, (void *)0, tErrorNoError );
// 如果这个任务的优先级更高,就执行调度,切换过去
if (task->prio < currentTask->prio)
{
tTaskSched();
}
}
else
{
// 如果没有任务等待的话,增加计数
++sem->count;
// 如果这个计数超过了最大允许的计数,则递减
if ((sem->maxCount != 0) && (sem->count > sem->maxCount))
{
sem->count = sem->maxCount;
}
}
tTaskExitCritical(status);
}
tSemWait中,waitTicks 当信号量计数为0时,等待的ticks数,为0时表示永远等待,这里的永远等待具体可以查看事件控制块中的tEventWait部分,总体思路是先判断计数是否大于0,是就减一退出,否则把当前任务插入事件队列中,也就是计数量队列
tSemNoWaitGet是获取信号量-不阻塞,能获取返回,也就是计数大于0返回tErrorNoError,否则返回tErrorResourceUnavaliable
tSemNotify是通知信号量,有任务因为信号量的原因等待,则唤醒,也就是加入到就绪列表中,如果没有任务等待,那么计数加一
计数信号量的删除和查询
信号量删除
uint32_t tSemDestroy (tSem * sem)
{
uint32_t status = tTaskEnterCritical();
// 清空事件控制块中的任务
uint32_t count = tEventRemoveAll(&sem->event, (void *)0, tErrorDel);
sem->count = 0;
tTaskExitCritical(status);
// 清空过程中可能有任务就绪,执行一次调度
if (count > 0)
{
tTaskSched();
}
return count;
}
由于还没有涉及到内存管理,所以这里的删除比较简单,跟初始化一样,只是比初始化多了清空任务和开启调度而已
信号量查询
typedef struct _tSemInfo
{
// 当前信号量的计数
uint32_t count;
// 信号量允许的最大计数
uint32_t maxCount;
// 当前等待的任务计数
uint32_t taskCount;
}tSemInfo;
void tSemGetInfo (tSem * sem, tSemInfo * info)
{
uint32_t status = tTaskEnterCritical();
// 拷贝需要的信息
info->count = sem->count;
info->maxCount = sem->maxCount;
info->taskCount = tEventWaitCount(&sem->event);
tTaskExitCritical(status);
}
新建tSemInfo结构体,用作查询,通过tSemGetInfo函数传入参数即可