信号量(Semaphore
)是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数。以同步为目的的信号量和以互斥为目的的信号量在使用上存在差异。本文通过分析鸿蒙轻内核信号量模块的源码,掌握信号量使用上的差异。本文中所涉及的源码,以OpenHarmony LiteOS-M
内核为例,均可以在开源站点https://gitee.com/openharmony/kernel_liteos_m 获取。
接下来,我们看下信号量的结构体,信号量初始化,信号量常用操作的源代码。
1、信号量结构体定义和常用宏定义
1.1 信号量结构体定义
在文件kernel\include\los_sem.h
定义的信号量控制块结构体为LosSemCB
,结构体源代码如下。信号量状态.semStat
取值OS_SEM_UNUSED
、OS_SEM_USED
,其他成员变量的注释见注释部分。
typedef struct {
UINT16 semStat; /**< 信号量状态 */
UINT16 semCount; /**< 可用的信号量数量 */
UINT16 maxSemCount; /**< 可用的信号量最大数量 */
UINT16 semID; /**< 信号量Id */
LOS_DL_LIST semList; /**< 阻塞在该信号量的任务链表 */
} LosSemCB;
1.2 信号量常用宏定义
系统支持创建多少信号量是根据开发板情况使用宏LOSCFG_BASE_IPC_SEM_LIMIT
定义的,每一个信号量semId
是UINT32
类型的,取值为[0,LOSCFG_BASE_IPC_SEM_LIMIT)
,表示信号量池中各个的信号量的编号。
⑴处的宏表示二值信号量的最大值为1,⑵处、⑶处的宏表示信号量未使用、使用状态值。⑷处根据信号量阻塞任务双向链表中的链表节点指针ptr
获取信号量控制块结构体指针。⑸处从信号量池中获取指定信号量semId
对应的信号量控制块。
⑴ #define OS_SEM_BINARY_MAX_COUNT 1
⑵ #define OS_SEM_UNUSED 0
⑶ #define OS_SEM_USED 1
⑷ #define GET_SEM_LIST(ptr) LOS_DL_LIST_ENTRY(ptr, LosSemCB, semList)
⑸ #define GET_SEM(semid) (((LosSemCB *)g_allSem) + (semid))
2、信号量初始化
信号量在内核中默认开启,用户可以通过宏LOSCFG_BASE_IPC_SEM
进行关闭。开启信号量的情况下,在系统启动时,在kernel\src\los_init.c
中调用OsSemInit()
进行信号量模块初始化。
下面,我们分析下信号量初始化的代码。
⑴初始化双向循环链表g_unusedSemList
,维护未使用的信号量池。⑵为信号量池申请内存,如果申请失败,则返回错误。⑶循环每一个信号量进行初始化,为每一个信号量节点指定索引semID
,把.semStat
设置为未使用OS_SEM_UNUSED
,并执行⑷把信号量节点插入未使用信号量双向链表g_unusedSemList
。
LITE_OS_SEC_TEXT_INIT UINT32 OsSemInit(VOID)
{
LosSemCB *semNode = NULL;
UINT16 index;
⑴ LOS_ListInit(&g_unusedSemList);
if (LOSCFG_BASE_IPC_SEM_LIMIT == 0) {
return LOS_ERRNO_SEM_MAXNUM_ZERO;
}
⑵ g_allSem = (LosSemCB *)LOS_MemAlloc(m_aucSysMem0, (LOSCFG_BASE_IPC_SEM_LIMIT * sizeof(LosSemCB)));
if (g_allSem == NULL) {
return LOS_ERRNO_SEM_NO_MEMORY;
}
⑶ for (index = 0; index < LOSCFG_BASE_IPC_SEM_LIMIT; index++) {
semNode = ((LosSemCB *)g_allSem) + index;
semNode->semID = index;
semNode->semStat = OS_SEM_UNUSED;
⑷ LOS_ListTailInsert(&g_unusedSemList, &semNode->semList);
}
return LOS_OK;
}