计数信号量的原理与创建

计数信号量原理

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函数传入参数即可