更新日期: 2022/06/01 来源: https://gitee.com/weharmony/kernel_liteos_a_note
los_sem.c 文件参考

http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-mini-basic-ipc-sem-basic.html 更多...

浏览源代码.

函数

LITE_OS_SEC_TEXT_INIT UINT32 OsSemInit (VOID)
 
LITE_OS_SEC_TEXT_INIT UINT32 OsSemCreate (UINT16 count, UINT16 maxCount, UINT32 *semHandle)
 
LITE_OS_SEC_TEXT_INIT UINT32 LOS_SemCreate (UINT16 count, UINT32 *semHandle)
 对外接口 创建信号量 更多...
 
LITE_OS_SEC_TEXT_INIT UINT32 LOS_BinarySemCreate (UINT16 count, UINT32 *semHandle)
 对外接口 创建二值信号量,其计数值最大为1,可以当互斥锁用 更多...
 
LITE_OS_SEC_TEXT_INIT UINT32 LOS_SemDelete (UINT32 semHandle)
 对外接口 删除指定的信号量,参数就是 semID 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_SemPend (UINT32 semHandle, UINT32 timeout)
 对外接口 申请指定的信号量,并设置超时时间 更多...
 
LITE_OS_SEC_TEXT UINT32 OsSemPostUnsafe (UINT32 semHandle, BOOL *needSched)
 以不安全的方式释放指定的信号量,所谓不安全指的是不用自旋锁 更多...
 
LITE_OS_SEC_TEXT UINT32 LOS_SemPost (UINT32 semHandle)
 对外接口 释放指定的信号量 更多...
 

变量

LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_unusedSemList
 可用的信号量列表,干嘛不用freeList? 可以看出这里是另一个人写的代码 更多...
 
LITE_OS_SEC_BSS LosSemCBg_allSem = NULL
 信号池,一次分配 LOSCFG_BASE_IPC_SEM_LIMIT 个信号量 更多...
 

详细描述

http://weharmonyos.com/openharmony/zh-cn/device-dev/kernel/kernel-mini-basic-ipc-sem-basic.html

    信号量(Semaphore)是一种实现任务间通信的机制,可以实现任务间同步或共享资源的互斥访问。
    
    一个信号量的数据结构中,通常有一个计数值,用于对有效资源数的计数,表示剩下的可被使用的共享资源数,其值的含义分两种情况:
    
        0,表示该信号量当前不可获取,因此可能存在正在等待该信号量的任务。
        正值,表示该信号量当前可被获取。
    以同步为目的的信号量和以互斥为目的的信号量在使用上有如下不同:
    
        用作互斥时,初始信号量计数值不为0,表示可用的共享资源个数。在需要使用共享资源前,先获取信号量,
        然后使用一个共享资源,使用完毕后释放信号量。这样在共享资源被取完,即信号量计数减至0时,其他需要获取信号量的任务将被阻塞,
        从而保证了共享资源的互斥访问。另外,当共享资源数为1时,建议使用二值信号量,一种类似于互斥锁的机制。
    用作同步时,初始信号量计数值为0。任务1获取信号量而阻塞,直到任务2或者某中断释放信号量,任务1才得以进入Ready或Running态,从而达到了任务间的同步。

    信号量运作原理
    信号量初始化,为配置的N个信号量申请内存(N值可以由用户自行配置,通过LOSCFG_BASE_IPC_SEM_LIMIT宏实现),并把所有信号量初始化成未使用,
    加入到未使用链表中供系统使用。

    信号量创建,从未使用的信号量链表中获取一个信号量,并设定初值。

    信号量申请,若其计数器值大于0,则直接减1返回成功。否则任务阻塞,等待其它任务释放该信号量,等待的超时时间可设定。
    当任务被一个信号量阻塞时,将该任务挂到信号量等待任务队列的队尾。

    信号量释放,若没有任务等待该信号量,则直接将计数器加1返回。否则唤醒该信号量等待任务队列上的第一个任务。

    信号量删除,将正在使用的信号量置为未使用信号量,并挂回到未使用链表。

    信号量允许多个任务在同一时刻访问共享资源,但会限制同一时刻访问此资源的最大任务数目。当访问资源的任务数达到该资源允许的最大数量时,
    会阻塞其他试图获取该资源的任务,直到有任务释放该信号量。

   开发流程
    创建信号量LOS_SemCreate,若要创建二值信号量则调用LOS_BinarySemCreate。
    申请信号量LOS_SemPend。
    释放信号量LOS_SemPost。
    删除信号量LOS_SemDelete。
注意
由于中断不能被阻塞,因此不能在中断中使用阻塞模式申请信号量。
版本
作者
weharmonyos.com | 鸿蒙研究站 | 每天死磕一点点
日期
2021-11-18

在文件 los_sem.c 中定义.

函数说明

◆ OsSemCreate()

LITE_OS_SEC_TEXT_INIT UINT32 OsSemCreate ( UINT16  count,
UINT16  maxCount,
UINT32 semHandle 
)

在文件 los_sem.c135 行定义.

136{
137 UINT32 intSave;
138 LosSemCB *semCreated = NULL;
139 LOS_DL_LIST *unusedSem = NULL;
140 UINT32 errNo;
141 UINT32 errLine;
142
143 if (semHandle == NULL) {
144 return LOS_ERRNO_SEM_PTR_NULL;
145 }
146
147 if (count > maxCount) {//信号量不能大于最大值,两参数都是外面给的
148 OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_OVERFLOW);
149 }
150
151 SCHEDULER_LOCK(intSave);//进入临界区,拿自旋锁
152
153 if (LOS_ListEmpty(&g_unusedSemList)) {//没有可分配的空闲信号提供
154 SCHEDULER_UNLOCK(intSave);
156 OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_ALL_BUSY);
157 }
158
159 unusedSem = LOS_DL_LIST_FIRST(&g_unusedSemList);//从未使用信号量池中取首个
160 LOS_ListDelete(unusedSem);//从空闲链表上摘除
161 SCHEDULER_UNLOCK(intSave);
162 semCreated = GET_SEM_LIST(unusedSem);//通过semList挂到链表上的,这里也要通过它把LosSemCB头查到. 进程,线程等结构体也都是这么干的.
163 semCreated->semCount = count;//设置数量
164 semCreated->semStat = OS_SEM_USED;//设置可用状态
165 semCreated->maxSemCount = maxCount;//设置最大信号数量
166 LOS_ListInit(&semCreated->semList);//初始化链表,后续阻塞任务通过task->pendList挂到semList链表上,就知道哪些任务在等它了.
167 *semHandle = semCreated->semID;//参数带走 semID
168 OsHookCall(LOS_HOOK_TYPE_SEM_CREATE, semCreated);
169 OsSemDbgUpdateHook(semCreated->semID, OsCurrTaskGet()->taskEntry, count);
170
171 return LOS_OK;
172
173ERR_HANDLER:
174 OS_RETURN_ERROR_P2(errLine, errNo);
175}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListInit(LOS_DL_LIST *list)
Definition: los_list.h:104
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListDelete(LOS_DL_LIST *node)
Definition: los_list.h:292
LITE_OS_SEC_ALW_INLINE STATIC INLINE BOOL LOS_ListEmpty(LOS_DL_LIST *list)
Identify whether a specified doubly linked list is empty. | 判断链表是否为空
Definition: los_list.h:321
STATIC INLINE LosTaskCB * OsCurrTaskGet(VOID)
LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_unusedSemList
可用的信号量列表,干嘛不用freeList? 可以看出这里是另一个人写的代码
Definition: los_sem.c:96
STATIC INLINE VOID OsSemDbgUpdateHook(UINT32 semID, TSK_ENTRY_FUNC creater, UINT16 count)
STATIC INLINE VOID OsSemInfoGetFullDataHook(VOID)
unsigned int UINT32
Definition: los_typedef.h:57
UINT16 semCount
Definition: los_sem_pri.h:49
UINT32 semID
Definition: los_sem_pri.h:51
UINT8 semStat
Definition: los_sem_pri.h:48
LOS_DL_LIST semList
Definition: los_sem_pri.h:52
UINT16 maxSemCount
Definition: los_sem_pri.h:50
函数调用图:
这是这个函数的调用关系图:

◆ OsSemInit()

LITE_OS_SEC_TEXT_INIT UINT32 OsSemInit ( VOID  )

在文件 los_sem.c103 行定义.

104{
105 LosSemCB *semNode = NULL;
106 UINT32 index;
107
108 LOS_ListInit(&g_unusedSemList);//初始化链表,链表上挂未使用的信号量,用于分配信号量,鸿蒙信号量的个数是有限的,默认1024个
109 /* system resident memory, don't free */
110 g_allSem = (LosSemCB *)LOS_MemAlloc(m_aucSysMem0, (LOSCFG_BASE_IPC_SEM_LIMIT * sizeof(LosSemCB)));//分配信号池
111 if (g_allSem == NULL) {
112 return LOS_ERRNO_SEM_NO_MEMORY;
113 }
114
115 for (index = 0; index < LOSCFG_BASE_IPC_SEM_LIMIT; index++) {
116 semNode = ((LosSemCB *)g_allSem) + index;//拿信号控制块, 可以直接g_allSem[index]来嘛
117 semNode->semID = SET_SEM_ID(0, index);//保存ID
118 semNode->semStat = OS_SEM_UNUSED;//标记未使用
119 LOS_ListTailInsert(&g_unusedSemList, &semNode->semList);//通过semList把 信号块挂到空闲链表上
120 }
121
122 if (OsSemDbgInitHook() != LOS_OK) {
123 return LOS_ERRNO_SEM_NO_MEMORY;
124 }
125 return LOS_OK;
126}
LITE_OS_SEC_ALW_INLINE STATIC INLINE VOID LOS_ListTailInsert(LOS_DL_LIST *list, LOS_DL_LIST *node)
Insert a node to the tail of a doubly linked list.
Definition: los_list.h:244
VOID * LOS_MemAlloc(VOID *pool, UINT32 size)
从指定内存池中申请size长度的内存,注意这可不是从内核堆空间中申请内存
Definition: los_memory.c:1123
UINT8 * m_aucSysMem0
异常交互动态内存池地址的起始地址,当不支持异常交互特性时,m_aucSysMem0等于m_aucSysMem1。
Definition: los_memory.c:107
LITE_OS_SEC_BSS LosSemCB * g_allSem
信号池,一次分配 LOSCFG_BASE_IPC_SEM_LIMIT 个信号量
Definition: los_sem.c:97
STATIC INLINE UINT32 OsSemDbgInitHook(VOID)
函数调用图:
这是这个函数的调用关系图:

◆ OsSemPostUnsafe()

LITE_OS_SEC_TEXT UINT32 OsSemPostUnsafe ( UINT32  semHandle,
BOOL needSched 
)

以不安全的方式释放指定的信号量,所谓不安全指的是不用自旋锁

在文件 los_sem.c287 行定义.

288{
289 LosTaskCB *resumedTask = NULL;
290 LosSemCB *semPosted = GET_SEM(semHandle);
291 if ((semPosted->semID != semHandle) || (semPosted->semStat == OS_SEM_UNUSED)) {
292 return LOS_ERRNO_SEM_INVALID;
293 }
294
295 /* Update the operate time, no matter the actual Post success or not */
296 OsSemDbgTimeUpdateHook(semHandle);
297
298 if (semPosted->semCount == OS_SEM_COUNT_MAX) {//当前信号资源不能大于最大资源量
299 return LOS_ERRNO_SEM_OVERFLOW;
300 }
301 if (!LOS_ListEmpty(&semPosted->semList)) {//当前有任务挂在semList上,要去唤醒任务
302 resumedTask = OS_TCB_FROM_PENDLIST(LOS_DL_LIST_FIRST(&(semPosted->semList)));//semList上面挂的都是task->pendlist节点,取第一个task下来唤醒
303 OsTaskWakeClearPendMask(resumedTask);
304 resumedTask->ops->wake(resumedTask);
305 if (needSched != NULL) {//参数不为空,就返回需要调度的标签
306 *needSched = TRUE;//TRUE代表需要调度
307 }
308 } else {//当前没有任务挂在semList上,
309 semPosted->semCount++;//信号资源多一个
310 }
311 OsHookCall(LOS_HOOK_TYPE_SEM_POST, semPosted, resumedTask);
312 return LOS_OK;
313}
STATIC INLINE VOID OsSemDbgTimeUpdateHook(UINT32 semID)
STATIC INLINE VOID OsTaskWakeClearPendMask(LosTaskCB *resumeTask)
清除事件阻塞掩码,即任务不再等待任何事件.
Definition: los_task_pri.h:298
VOID(* wake)(LosTaskCB *taskCB)
任务唤醒
const SchedOps * ops
函数调用图:
这是这个函数的调用关系图:

变量说明

◆ g_allSem

LITE_OS_SEC_BSS LosSemCB* g_allSem = NULL

信号池,一次分配 LOSCFG_BASE_IPC_SEM_LIMIT 个信号量

在文件 los_sem.c97 行定义.

◆ g_unusedSemList

LITE_OS_SEC_DATA_INIT STATIC LOS_DL_LIST g_unusedSemList

可用的信号量列表,干嘛不用freeList? 可以看出这里是另一个人写的代码

在文件 los_sem.c96 行定义.