1.1 创建信号量
当事件控制块OS_EVENT中的OSEventType=OS_EVENT_TYPE_SEM时,则表示此处创建的事件为信号量。
上面为计数信号量的创建函数,创建函数不能在中断中调用,在全局的事件控制块列表中取出一个事件控制块pevent,对pevent进行初始化操作。设置此事件的类型为;OSEventType为OS_EVENT_TYPE_SEM。同时调用函数来初始化OS_EventWaitListInit()事件控制块使事件等待列表中没有等待的任务。
1.2 获取信号量
当某个任务获取计数信号量时,根据当前信号量的值判断此任务是继续执行还是挂起。函数
void OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *perr)来实现对信号量的获取。当前运行的任务不一定是系统中优先级最高的任务,只能说是在任务调度之前,就绪队列中优先级最高的任务。
上面的代码设置TCB的当前状态为:OS_STAT_SEM,定义如下:表示当前的任务正在等待信号量
#define OS_STAT_SEM 0x01u /* Pending on semaphore
之后调用函数OS_EventTaskWait()使当前的任务添加到等待事件表中并使任务从任务就绪表中删除。
之后进行任务的调度 OS_Sched(); 任务调度之后,系统将CPU的使用权限人给就绪表中最高优先级的任务。之前看到在系统tick处理函数中进行中断级任务的调度。其有如下代码:
上面的代码是在函数OSTimeTick()中,在任务的等待时间不为0时if (ptcb->OSTCBDly != 0),更新任务的扥带时间及任务的就绪状态。
同时在TCB数据结构的第一次字段是:
OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
在无论是任务级还是中断及的任务切换,在切换的时候都会当前函数的指针保存在任务的堆栈中。当等待事件的任务满足等待条件或者等待超时的时候,就绪表中最高优先级的任务从自己的堆栈PC指针处继续执行。
1.3 信号量的释放
当对共享资源访问结束指数之后,需要对信号量进行释放操作。在对信号量计数值进行+1操作之前必须对是否还要任务等待此信号量事件进行判断。当有任务等在此信号量事件时,调用函数OS_EventTaskRdy()是任务从等待事件表中移除并使此任务进入就绪状态。
1.4 信号量的删除
OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr)
上面的函数为计数信号量的删除函数,其中opt的取值如下:
opt == OS_DEL_NO_PEND Delete semaphore ONLY if no task pending
opt == OS_DEL_ALWAYS Deletes the semaphore even if tasks are waiting.In this case, all the tasks pending will be readied.
函数中首先判断任务等待组是否为0即可以确定是否有等待此事件的任务存在。
if (pevent->OSEventGrp != 0) { /* See if any tasks waiting on semaphore */
tasks_waiting = OS_TRUE; /* Yes */
} else {
tasks_waiting = OS_FALSE; /* No */
}
之后根据opt的取值分别进行处理
当opt=OS_DEL_NO_PEND 时
case OS_DEL_NO_PEND: /* Delete semaphore only if no task waiting */
if (tasks_waiting == OS_FALSE) {
#if OS_EVENT_NAME_SIZE > 1
pevent->OSEventName[0] = '?'; /* Unknown name */
pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */
} else {
OS_EXIT_CRITICAL();
*perr = OS_ERR_TASK_WAITING;
pevent_return = pevent;
}
break;
在此opt之下,之后当没有等待任务的时候才能进行此操作,将事件类型改为:
OSEventType = OS_EVENT_TYPE_UNUSED;即此事件还没有进行赋值操作。同时将此事件控制块添加到全局的空闲控制块OSEventFreeList 链表中。
当opt=OS_DEL_ALWAYS 时,无论是否有任务等待此事件,将等待此事件的任务从等待表中删除。
case OS_DEL_ALWAYS: /* Always delete the semaphore */
while (pevent->OSEventGrp != 0) { /* Ready ALL tasks waiting for semaphore */
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);
}
#if OS_EVENT_NAME_SIZE > 1
pevent->OSEventName[0] = '?'; /* Unknown name */
pevent->OSEventName[1] = OS_ASCII_NUL;
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;
pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */
pevent->OSEventCnt = 0;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
if (tasks_waiting == OS_TRUE) { /* Reschedule only if task(s) were waiting */
OS_Sched(); /* Find highest priority task ready to run */
}
*perr = OS_ERR_NONE;
pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */
break;
同时当删除的事件有任务等待时,在进行一个任务调度。