1.信号量更多的被用于实现任务间的同步以及任务和ISR间的同步。
2.信号量的数据结构类型为OS_SEM,它的定义位于os.h中。
typedef struct os_sem OS_SEM; // 631行
struct os_sem { // 858行 - 869行
OS_OBJ_TYPE Type; // 类型,必须被设置成OS_OBJ_TYPE_SEM
CPU_CHAR *NamePtr; // 名称
OS_PEND_LIST PendList; // 任务挂起表
#if OS_CFG_DBG_EN > 0u // 与调试相关
OS_SEM *DbgPrevPtr;
OS_SEM *DbgNextPtr;
CPU_CHAR *DbgNamePtr;
#endif
OS_SEM_CTR Ctr; // 信号量计数,它的最大值取决于它的类型OS_SEM_CTRO
CPU_TS TS; // 上次被释放的时间
};
3.创建信号量使用函数OSSemCreate(),它的定义位于os_sem.c中。
void OSSemCreate (OS_SEM *p_sem, // 72行 - 123行,声明的信号量的地址
CPU_CHAR *p_name, // 名称
OS_SEM_CTR cnt, // 计数初始值
OS_ERR *p_err) // 错误代码
{
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器
#ifdef OS_SAFETY_CRITICAL // 安全检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return;
}
#endif
#ifdef OS_SAFETY_CRITICAL_IEC61508 // 安全检查-IEC61508协议
if (OSSafetyCriticalStartFlag == DEF_TRUE) {
*p_err = OS_ERR_ILLEGAL_CREATE_RUN_TIME;
return;
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 不允许在ISR中调用该函数
*p_err = OS_ERR_CREATE_ISR;
return;
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_sem == (OS_SEM *)0) { // 无效的参数
*p_err = OS_ERR_OBJ_PTR_NULL;
return;
}
#endif
CPU_CRITICAL_ENTER(); // 进入临界区
p_sem->Type = OS_OBJ_TYPE_SEM; // 该结构体的类型为信号量
p_sem->Ctr = cnt; // 设置计数初值
p_sem->TS = (CPU_TS)0;
p_sem->NamePtr = p_name; // 保存信号量的名称
OS_PendListInit(&p_sem->PendList); // 初始化挂起表
#if OS_CFG_DBG_EN > 0u // 允许进行调试
OS_SemDbgListAdd(p_sem);
#endif
OSSemQty++; // 信号量的数目加1
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
}
4.等待信号量使用函数OSSemPend(),它的定义位于os_sem.c中。
OS_SEM_CTR OSSemPend (OS_SEM *p_sem, // 301行 - 427行
OS_TICK timeout, // 超时时间:0表示任务将一直等待下去
OS_OPT opt, // 选项
CPU_TS *p_ts,
OS_ERR *p_err)
{
OS_SEM_CTR ctr;
OS_PEND_DATA pend_data;
CPU_SR_ALLOC(); // 声明变量cpu_sr,用来临时存储CPU的状态寄存器
#ifdef OS_SAFETY_CRITICAL // 系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_CALLED_FROM_ISR_CHK_EN > 0u // 允许进行ISR调用检查
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 不允许在ISR中调用该函数
*p_err = OS_ERR_PEND_ISR;
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_sem == (OS_SEM *)0) { // 信号量无效
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_SEM_CTR)0);
}
switch (opt) {
case OS_OPT_PEND_BLOCKING:
case OS_OPT_PEND_NON_BLOCKING:
break;
default: // 选项无效
*p_err = OS_ERR_OPT_INVALID;
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u // 允许进行内核对象类型检查
if (p_sem->Type != OS_OBJ_TYPE_SEM) { // 保证信号量已被创建
*p_err = OS_ERR_OBJ_TYPE;
return ((OS_SEM_CTR)0);
}
#endif
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS)0; // 初始化返回时间戳
}
CPU_CRITICAL_ENTER(); // 进入临界区
if (p_sem->Ctr > (OS_SEM_CTR)0) { // 计数值>0,信号量可用
p_sem->Ctr--; // 计数值减1
if (p_ts != (CPU_TS *)0) {
*p_ts = p_sem->TS; // 获取最近一次信号量的释放时间
}
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_NONE;
return (ctr);
}
if ((opt & OS_OPT_PEND_NON_BLOCKING) != (OS_OPT)0) { // 信号量不可用,用户不想阻塞
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_PEND_WOULD_BLOCK;
return (ctr);
} else { // 信号量不可用,用户想阻塞
if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) { // 若调度器锁定,不能挂起
CPU_CRITICAL_EXIT(); // 退出临界区
*p_err = OS_ERR_SCHED_LOCKED;
return ((OS_SEM_CTR)0);
}
}
OS_CRITICAL_ENTER_CPU_CRITICAL_EXIT(); // 锁定调度器,使能中断
OS_Pend(&pend_data, // 把该任务放入到挂起表中
(OS_PEND_OBJ *)((void *)p_sem),
OS_TASK_PEND_ON_SEM,
timeout);
OS_CRITICAL_EXIT_NO_SCHED(); // 退出临界区,不调度
OSSched(); // 执行调度程序
// 在任务执行OSSched()函数后,该任务被挂起,其他任务开始执行;当某一个时刻,切换到该任务后才执行下面的代码。
CPU_CRITICAL_ENTER(); // 进入临界区
switch (OSTCBCurPtr->PendStatus) { // 判断当前任务的状态
case OS_STATUS_PEND_OK: // 信号量被发布了
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_NONE;
break;
case OS_STATUS_PEND_ABORT: // 等待操作被其他任务取消
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_PEND_ABORT;
break;
case OS_STATUS_PEND_TIMEOUT: // 在特定的超时时间内,信号量没有被发布
if (p_ts != (CPU_TS *)0) {
*p_ts = (CPU_TS )0;
}
*p_err = OS_ERR_TIMEOUT;
break;
case OS_STATUS_PEND_DEL: // 信号量被删除
if (p_ts != (CPU_TS *)0) {
*p_ts = OSTCBCurPtr->TS;
}
*p_err = OS_ERR_OBJ_DEL;
break;
default: // 其他情况
*p_err = OS_ERR_STATUS_INVALID;
CPU_CRITICAL_EXIT();
return ((OS_SEM_CTR)0);
}
ctr = p_sem->Ctr;
CPU_CRITICAL_EXIT(); // 退出临界区
return (ctr);
}
5.发布信号量使用OSSemPost(),它的定义位于os_sem.c中。
OS_SEM_CTR OSSemPost (OS_SEM *p_sem, // 573行 - 625行,信号量的地址
OS_OPT opt, // 发布信号量的方式
OS_ERR *p_err) // 返回的错误码
{
OS_SEM_CTR ctr;
CPU_TS ts;
#ifdef OS_SAFETY_CRITICAL // 允许进行系统安全性检查
if (p_err == (OS_ERR *)0) {
OS_SAFETY_CRITICAL_EXCEPTION();
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_ARG_CHK_EN > 0u // 允许进行参数检查
if (p_sem == (OS_SEM *)0) { // 无效的信号量
*p_err = OS_ERR_OBJ_PTR_NULL;
return ((OS_SEM_CTR)0);
}
#endif
#if OS_CFG_OBJ_TYPE_CHK_EN > 0u // 允许进行内核对象类型检查
if (p_sem->Type != OS_OBJ_TYPE_SEM) { // 保证信号量已被创建
*p_err = OS_ERR_OBJ_TYPE;
return ((OS_SEM_CTR)0);
}
#endif
ts = OS_TS_GET(); // 获取时间戳
#if OS_CFG_ISR_POST_DEFERRED_EN > 0u // 延时发布模式
if (OSIntNestingCtr > (OS_NESTING_CTR)0) { // 如果在ISR中调用
OS_IntQPost((OS_OBJ_TYPE)OS_OBJ_TYPE_SEM, // 发布到中断服务队列中
(void *)p_sem,
(void *)0,
(OS_MSG_SIZE)0,
(OS_FLAGS )0,
(OS_OPT )opt,
(CPU_TS )ts,
(OS_ERR *)p_err);
return ((OS_SEM_CTR)0);
}
#endif
ctr = OS_SemPost(p_sem, // 发送信号量
opt,
ts,
p_err);
return (ctr);
}
发布信号量的方式有以下几种选项:
OS_OPT_POST_1表明只给一个任务发布信号;
OS_OPT_POST_ALL表明信号会发布给所有等待该信号量的任务;
OS_OPT_POST_NO_SCHED表明在发布信号量后不会进行任务调度,可以添加到前两种选项的后面。