若有错误,还望大佬指出。
任务之间的通信是通过事件联系。这样说可能有些拗口,即当系统运行完任务1之后,会释放信息(也称事件),同时让任务2获取到这个信息之后,任务2也能执行了。所以两个任务通过信息相互联系。
①信号的创立
OS_EVENT *OSSemCreate (INT16U cnt)
{
OS_EVENT *pevent; //事件结构体指针,某个事件的地址
#if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
OS_CPU_SR cpu_sr = 0u;
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508
if (OSSafetyCriticalStartFlag == OS_TRUE) {
OS_SAFETY_CRITICAL_EXCEPTION();
}
#endif
if (OSIntNesting > 0u) { /* 判断中断嵌套是否还在进行. */
return ((OS_EVENT *)0); /* 如果还在中断说明没有完全关闭,返回错误信号 ,中断过程中不允许信号的产生 */
}
OS_ENTER_CRITICAL();
pevent = OSEventFreeList; /* OSEventFreeList链表表头挂载在事件的pevent事件结构体指针里,即该事件当表头 */
if (OSEventFreeList != (OS_EVENT *)0) {
/* 如果空闲链表的地址不是0的话,说明空闲链表地址即pevent地址不为0,那么就不是处于链表的最后一个 */
OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr;//此外,已知该空闲链表给了pevent,那么空闲链表就移动向下一位。
}
OS_EXIT_CRITICAL();
if (pevent != (OS_EVENT *)0) { /* 如果事件地址不为零 ,说明不在链表尾部 */
pevent->OSEventType = OS_EVENT_TYPE_SEM; /* 设置信号类型 */
pevent->OSEventCnt = cnt; /* 参数cnt赋值信号变化值 */
pevent->OSEventPtr = (void *)0; /* 断开与原来链表的链接 */
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
OS_EventWaitListInit(pevent); /* 等待列表初始化,将该事件的等待列表清空 */
}
return (pevent);
}
这看起来有点多,我们拨开现象看本质,1,该函数最主要的目的就是将事件链表中的一个空闲地址划分给我们建立的事件(通过地址的形式给该事件pevent赋值),2,然后将属性赋予给该事件。3,最后将该事件的等待列表初始化
所以事件和事件链表的关系:事件的地址可以挂载在事件链表上,创造或删除事件时,只需要添加地址或删除地址即可。
②删除信号量
呐呐呐,删除某个信号量。那就是将某个事件从事件链表给删除。
OS_EVENT *OSSemDel (OS_EVENT *pevent, //结构体指针
INT8U opt, //选项
INT8U *perr) //错误类型指针
{
BOOLEAN tasks_waiting; //等待任务变量
OS_EVENT *pevent_return; //结构体指针类型
.......
//switch判断选项:
/* OS_DEL_NO_PEND 0: 当事件里没有任务时,删除该信号
OS_DEL_ALWAYS 1:直接删除信号
*/
switch (opt) {
case OS_DEL_NO_PEND: /* Delete semaphore only if no task waiting */
if (tasks_waiting == OS_FALSE) { //没有任务的话
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
//大家看这里,接下来事件的地址就是在这个switch函数里面被删除的
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;//1,将事件类型为未使用
pevent->OSEventPtr = OSEventFreeList; /* 2,令该事件的地址链表下一位指向空闲表 */
pevent->OSEventCnt = 0u;
OSEventFreeList = pevent; /* 3,令空闲表的地址等于pevent事件地址,相对于直接将事件地址抹去了 */
OS_EXIT_CRITICAL(); /* 退出临界区 */
*perr = OS_ERR_NONE; /* 返回无错误 */
pevent_return = (OS_EVENT *)0; /* 返回信号地址为0,说明信号被删除了 */
} else {
OS_EXIT_CRITICAL();
*perr = OS_ERR_TASK_WAITING;//说明有任务挂载,那就不擅长
pevent_return = pevent; /* 返回信号地址*/
}
break;
case OS_DEL_ALWAYS: /* 直接删除,管你有没有任务 */
while (pevent->OSEventGrp != 0u) { /* 当任务挂载事件时,进入循环 ,为什么要循环?因为该事件可能挂载了多个任务 */
(void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK);//令挂载在事件上的任务使能,并进入就绪状态
}
#if OS_EVENT_NAME_EN > 0u
pevent->OSEventName = (INT8U *)(void *)"?";
#endif
pevent->OSEventType = OS_EVENT_TYPE_UNUSED;//事件类型为未使用
pevent->OSEventPtr = OSEventFreeList; /* 返回空闲链表,不占用链表 */
pevent->OSEventCnt = 0u;
OSEventFreeList = pevent; /* Get next free event control block */
OS_EXIT_CRITICAL();
if (tasks_waiting == OS_TRUE) { /* 任务还有挂载任务的话 */
OS_Sched(); /* 进行任务调度 */
}
*perr = OS_ERR_NONE; /* 返回无错误 */
pevent_return = (OS_EVENT *)0; /* 返回信号地址为0,说明信号被删除了 */
break;
default: //当选项非0非1
OS_EXIT_CRITICAL(); /* 退出临界区 */
*perr = OS_ERR_INVALID_OPT; /* 错误类型为选项错误 */
pevent_return = pevent; /* 直接返回地址 */
break;
}
return (pevent_return);
}
如下图说是:
③获取信号量
信号量是由任务来获取,所以是和任务相关,会涉及到当前任务的属性操作。即,什么任务获取什么信号
void OSSemPend (OS_EVENT *pevent, //事件结构体变量
INT32U timeout,//超时参数
INT8U *perr) //错误类型指针
{
.......
OS_ENTER_CRITICAL();//进入临界区,关中断
//1如果信号量可用
if (pevent->OSEventCnt > 0u) { /* 信号量可用 */
pevent->OSEventCnt--; /* 信号量减一 */
OS_EXIT_CRITICAL(); /* 退出临界区*/
*perr = OS_ERR_NONE; /* 返回无错误 */
return;
}
/*2 else,信号量不可用,那就必须等待事件发生 */
OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* 当前任务状态接收信号状态 */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* 当前任务获取信号 */
OSTCBCur->OSTCBDly = timeout; /* 当前任务的等待时间 */
OS_EventTaskWait(pevent); /* 当前任务退出就绪列表,并挂载在等待列表上,任务在等待列表中了,说明获取信息了 */
OS_EXIT_CRITICAL();
//然后调度函数。
OS_Sched(); /* 任务调度 ,重新找最高优先级任务,最高优先级任务为当前任务 */
OS_ENTER_CRITICAL();
switch (OSTCBCur->OSTCBStatPend) { /* 查看当前任务状态 */
case OS_STAT_PEND_OK: //接收消息成功
*perr = OS_ERR_NONE;
break;
case OS_STAT_PEND_ABORT://接收失败
*perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */
break;
case OS_STAT_PEND_TO: //接收超时
default:
OS_EventTaskRemove(OSTCBCur, pevent); /* 将当前任务从事件列表中删除*/
*perr = OS_ERR_TIMEOUT; /* 返回超时错误 */
break;
}
//此时,任务已经获取信息了
OSTCBCur->OSTCBStat = OS_STAT_RDY; /* 当前任务状态为就绪状态 */
OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* 获取信息成功 */
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* 指向链表底部 ,即事件列表清零 */
#if (OS_EVENT_MULTI_EN > 0u)
OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0;
#endif
OS_EXIT_CRITICAL();
}
④释放信号量
可以这样解释一波,获取信号量和释放信号量都会涉及任务。
释放信号量:1任务等待列表转成就绪列表。当执行完一个任务时,2事件的属性(信号量次数加一),这是就释放出一个信号量。
获取信号量:2信号次数还存在,通过次数减一,此时就获取信号量,1任务就绪列表转成等待列表。
这两个函数刚好完成一次循环。
那现在就明了了:只要涉及pevent->OSEventCnt–那就是相对于pend函数,信号获取。pevent->OSEventCnt++就是信号释放post函数。