Abstract: This paper leads us to analyze the source code of hongmeng Light kernel semaphore module, including semaphore structure, semaphore pool initialization, semaphore creation and deletion, application release, etc.

This article is shared by Huawei cloud community “Hongmeng light kernel M core source code analysis series 11 Semaphore”, original author: zhushy.

Semaphore is a mechanism to implement inter-task communication, which can achieve synchronization between tasks or mutually exclusive access to shared resources. In the data structure of a semaphore, there is usually a count of the number of available resources, representing the number of available shared resources. There are differences in use between semaphores for synchronization and those for mutual exclusion. This paper analyzes the source code of hongmeng Light kernel semaphore module to master the differences in the use of semaphore. The source code in this article, using the OpenHarmony LitEOS-M kernel as an example, is available on the open source site gitee.com/openharmony… To obtain.

Next, we look at the semaphore structure, semaphore initialization, semaphore common operations of the source code.

1. Semaphore structure definition and common macro definition

1.1 Semaphore structure definition

The semaphore control block structure defined in the file kernel\include\los_sem.h is LosSemCB. The structure source code is as follows. Semaphore status: semStat OS_SEM_UNUSED, OS_SEM_USED. For comments about other member variables, see the comments section.

typedef struct { UINT16 semStat; /** / UINT16 semCount; /**< UINT16 maxSemCount; /**< UINT16 semID; /**< semaphore Id */ LOS_DL_LIST semList; /**< block tasks in the semaphore list */} LosSemCB;Copy the code

1.2 Common macro definitions for semaphores

The number of semaphores supported by the system is defined based on the development board using the macro LOSCFG_BASE_IPC_SEM_LIMIT. Each semId is of the UINT32 type and its value is [0,LOSCFG_BASE_IPC_SEM_LIMIT). Represents the number of each semaphore in the semaphore pool.

The macro at ⑴ indicates that the maximum value of the binary semaphore is 1, while the macro at ⑵ and ⑶ indicates that the semaphore is not in use or in use. (4) Obtain the semaphore control block structure pointer according to the linked list node pointer PTR in the bidirectional linked list of semaphore blocking tasks. At ⑸, the semaphore control block corresponding to the specified semaphore semId is obtained from the semaphore pool.

⑴ #define OS_SEM_BINARY_MAX_COUNT ⑵ #define OS_SEM_UNUSED ⑶ #define OS_SEM_USED 1 ⑷ LOS_DL_LIST_ENTRY(PTR, LosSemCB, semList) ⑸ ⑸ define GET_SEM(semID) (((LosSemCB *)g_allSem + (semID))Copy the code

2. Semaphore initialization

Semaphores are enabled by default in the kernel and can be disabled by using the macro LOSCFG_BASE_IPC_SEM. With the semaphore turned on, OsSemInit() is called in kernel\ SRC \los_init.c to initialize the semaphore module at system startup.

Next, let’s examine the semaphore initialization code.

(1) Initialize g_unusedSemList and maintain unused semaphore pool. ⑵ Apply memory for the semaphore pool. If the application fails, an error is returned. Insert g_unusedSemList (*) into g_unusedSemList (*); insert g_unusedSemList (*) into g_unusedSemList (*);

LITE_OS_SEC_TEXT_INIT UINT32 OsSemInit(VOID) { LosSemCB *semNode = NULL; UINT16 index; (1) 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; (4) LOS_ListTailInsert (& g_unusedSemList, & semNode - > semList); } return LOS_OK; }Copy the code

3. Common semaphore operations

3.1 Semaphore creation

We can use the function LOS_SemCreate(UINT16count, UINT32 *semHandle) to create counting semaphores, Use the UINT32 LOS_BinarySemCreate(UINT16 count,UINT32 *semHandle) to create a binary semaphore.

Both functions pass in the same number of arguments as the semaphore count, and the semHandle that holds the semaphore number. The maximum number of count semaphores is OS_SEM_COUNTING_MAX_COUNT, and the maximum number of binary semaphores is OS_SEM_BINARY_MAX_COUNT. The function OsSemCreate() is further called to create the semaphore, as discussed below.

LITE_OS_SEC_TEXT_INIT UINT32 LOS_SemCreate(UINT16 count, UINT32 *semHandle)
{
    return OsSemCreate(count, OS_SEM_COUNTING_MAX_COUNT, semHandle);
}


LITE_OS_SEC_TEXT_INIT UINT32 LOS_BinarySemCreate(UINT16 count, UINT32 *semHandle)
{
    return OsSemCreate(count, OS_SEM_BINARY_MAX_COUNT, semHandle);
}
Copy the code

Let’s look at the function OsSemCreate(), which takes three arguments, the number of semaphores created, the maximum number, and the semaphore number.

1. Is g_unusedSemList empty and available semaphore resources? If there are no semaphores available, call OsSemInfoGetFullDataHook() to do some checking related to commissioning. This function needs to be switched on and will be covered in subsequent series.

(2) if g_unusedSemList is not empty, get the first available semaphore node, then remove it from the bidirectional list g_unusedSemList, and call the macro GET_SEM_LIST to get LosSemCB*semCreated. Initializes the created semaphore information, including the state of the semaphore, the number of semaphore, the maximum number of semaphore information. SemCreated ->semList; tasks that block on the semaphore will hang on the semaphore. (4) Assign the output parameter *semHandle, and subsequent programs use this semaphore number to perform other operations on the semaphore.

LITE_OS_SEC_TEXT_INIT UINT32 OsSemCreate(UINT16 count, UINT16 maxCount, UINT32 *semHandle) { UINT32 intSave; LosSemCB *semCreated = NULL; LOS_DL_LIST *unusedSem = NULL; UINT32 errNo; UINT32 errLine; if (semHandle == NULL) { return LOS_ERRNO_SEM_PTR_NULL; } if (count > maxCount) { OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_OVERFLOW); } intSave = LOS_IntLock(); ⑴ if (LOS_ListEmpty(&g_unusedSemList)) {LOS_IntRestore(intSave); OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_ALL_BUSY); } ⑵ unusedSem = LOS_DL_LIST_FIRST(&(g_unusedSemList)); LOS_ListDelete(unusedSem); semCreated = (GET_SEM_LIST(unusedSem)); semCreated->semCount = count; semCreated->semStat = OS_SEM_USED; semCreated->maxSemCount = maxCount; (3) LOS_ListInit (& semCreated - > semList); (4) * semHandle = (UINT32) semCreated - > semID; LOS_IntRestore(intSave); OsHookCall(LOS_HOOK_TYPE_SEM_CREATE, semCreated); return LOS_OK; ERR_HANDLER: OS_RETURN_ERROR_P2(errLine, errNo); }Copy the code

3.2 Semaphore deletion

We can use the UINT32semHandle function LOS_semDelete(UINT32semHandle) to delete semaphores.

(1) Determine whether the semaphore semHandle exceeds LOSCFG_BASE_IPC_SEM_LIMIT, if so, return an error code. If there is no problem with the semaphore id, obtain the semaphore control block LosSemCB *semDeleted. (2) determine the state of the semaphore to be deleted. If it is not in use, go to the error label ERR_HANDLER: for processing. (3) If the semaphore block task list is not empty, it is not allowed to delete, jump to the error label for processing. OS_SEM_UNUSED; insert g_unusedSemList (*) into g_unusedSemList (*);

LITE_OS_SEC_TEXT_INIT UINT32 LOS_SemDelete(UINT32 semHandle) { UINT32 intSave; LosSemCB *semDeleted = NULL; UINT32 errNo; UINT32 errLine; ⑴ IF (semHandle >= (UINT32)LOSCFG_BASE_IPC_SEM_LIMIT) {OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_INVALID); } semDeleted = GET_SEM(semHandle); intSave = LOS_IntLock(); ⑵ if (semDeleted->semStat == OS_SEM_UNUSED) {LOS_IntRestore(intSave); OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_INVALID); } (3) if (! LOS_ListEmpty(&semDeleted->semList)) { LOS_IntRestore(intSave); OS_GOTO_ERR_HANDLER(LOS_ERRNO_SEM_PENDED); } (4) LOS_ListAdd (& g_unusedSemList, & semDeleted - > semList); semDeleted->semStat = OS_SEM_UNUSED; LOS_IntRestore(intSave); OsHookCall(LOS_HOOK_TYPE_SEM_DELETE, semDeleted); return LOS_OK; ERR_HANDLER: OS_RETURN_ERROR_P2(errLine, errNo); }Copy the code

3.3 Semaphore application

We can use the function UINT32LOS_SemPend(UINT32 semHandle, UINT32 timeout) to request semaphores. The two parameters are semHandle and timeout. The value ranges from 0 to LOS_WAIT_FOREVER. The unit is Tick. Here is how to request a semaphore by analyzing the source code.

When applying for semaphore, semaphore number and parameter validity check will be carried out first. The code in ⑴ indicates that if the semaphore is greater than the maximum value configured, an error code is returned. (2) obtain the semaphore control block to be applied at semPended. ⑶ the function is called to verify the semaphore control block. If the semaphore is not created, it is in the interrupt processing period, and it is in the lock task scheduling period, the error code will be returned. (4) If the check is not passed, skip to ERROR_SEM_PEND: label stop semaphore application.

⑸ If the semaphore count is greater than 0, the semaphore count will be reduced by 1 and the result of successful application will be returned. ⑹ If the semaphore count is 0 and there is no timeout wait time, the result code LOS_ERRNO_SEM_UNAVAILABLE is returned. ⑺ If the requested semaphore is fully occupied and needs to wait, the current task is blocked. TaskSem marks the requested semaphore and calls OsSchedTaskWait() to make the current task blocked. Join the blocking list of semaphores. SemList. If you do not wait for LOS_WAIT_FOREVER, change the task status to OS_TASK_STATUS_PEND_TIME and set the waitTimes wait time. ⑻ department triggers the task scheduling for task switching, and does not execute the subsequent code for the time being.

If the waiting time times out and the semaphore is still unavailable, the task cannot obtain the semaphore, continue to execute the Aspect, change the task status, and return the error code. If the semaphores are available, execute the → task to obtain the semaphores, return to apply successfully.

LITE_OS_SEC_TEXT UINT32 LOS_SemPend(UINT32 semHandle, UINT32 timeout) { UINT32 intSave; LosSemCB *semPended = NULL; UINT32 retErr; LosTaskCB *runningTask = NULL; ⑴ IF (semHandle >= (UINT32)LOSCFG_BASE_IPC_SEM_LIMIT) {OS_RETURN_ERROR(LOS_ERRNO_SEM_INVALID); } ⑵ semPended = GET_SEM(semHandle); intSave = LOS_IntLock(); (3) retErr = OsSemValidCheck (semPended); If (retErr) {⑷ goto ERROR_SEM_PEND; } ⑸ if (semPended->semCount > 0) {semPended->semCount--; LOS_IntRestore(intSave); OsHookCall(LOS_HOOK_TYPE_SEM_PEND, semPended, runningTask); return LOS_OK; } [6] the if (! timeout) { retErr = LOS_ERRNO_SEM_UNAVAILABLE; goto ERROR_SEM_PEND; } ⑺ runningTask = (LosTaskCB *) g_lostask.runtask; runningTask->taskSem = (VOID *)semPended; OsSchedTaskWait(&semPended->semList, timeout); LOS_IntRestore(intSave); OsHookCall(LOS_HOOK_TYPE_SEM_PEND, semPended, runningTask); Being LOS_Schedule (); intSave = LOS_IntLock(); ⑼ runningTask->taskStatus &= (~OS_TASK_STATUS_TIMEOUT); retErr = LOS_ERRNO_SEM_TIMEOUT; goto ERROR_SEM_PEND; } LOS_IntRestore(intSave); ⑽ return LOS_OK; ERROR_SEM_PEND: LOS_IntRestore(intSave); OS_RETURN_ERROR(retErr); }Copy the code

3.4 Semaphore release

The UINT32LOS_semPost(UINT32 semHandle) function is used to release semaphores.

The release of semaphore will be the first semaphore number, parameter validity check, these are relatively simple, you can read by yourself. (1) Determine whether the semaphore overflows. (2) If the semaphore task block list is not empty, execute (3) fetch the first task from the block list and set.tasksem to NULL. Perform ⑷ to adjust the state of the semaphore task and add it to the queue. ⑸ Triggers task scheduling for task switching. If the task blocking list of the semaphore is empty, increment the count of the semaphore by 1.

LITE_OS_SEC_TEXT UINT32 LOS_SemPost(UINT32 semHandle) { UINT32 intSave; LosSemCB *semPosted = GET_SEM(semHandle); LosTaskCB *resumedTask = NULL; if (semHandle >= LOSCFG_BASE_IPC_SEM_LIMIT) { return LOS_ERRNO_SEM_INVALID; } intSave = LOS_IntLock(); if (semPosted->semStat == OS_SEM_UNUSED) { LOS_IntRestore(intSave); OS_RETURN_ERROR(LOS_ERRNO_SEM_INVALID); } ⑴ if (semPosted->maxSemCount == semPosted->semCount) {LOS_IntRestore(intSave); OS_RETURN_ERROR(LOS_ERRNO_SEM_OVERFLOW); } [2] the if (! ResumedTask = OS_TCB_FROM_PENDLIST(&(semPosted->semList)); resumedTask->taskSem = NULL; (4) OsSchedTaskWake (resumedTask); LOS_IntRestore(intSave); OsHookCall(LOS_HOOK_TYPE_SEM_POST, semPosted, resumedTask); 5] LOS_Schedule (); } else {⑹ semPosted->semCount++; LOS_IntRestore(intSave); OsHookCall(LOS_HOOK_TYPE_SEM_POST, semPosted, resumedTask); } return LOS_OK; }Copy the code

4. Summary of semaphore use

4.1 Counting semaphores, binary semaphores, and mutex

The only difference between counting semaphores and binary semaphores is that the initial quantity of the binary semaphores can only be 0 and 1, while the initial value of counting semaphores can be 0 and an integer greater than 1.

Mutex can be understood as a characteristic binary semaphore. There is no essential difference in the realization of exclusive processing of critical resources and mutex scenarios. The muxCount is the number of times the lock is held, the semaphore is the number of times the lock is held, the semaphore is the number of times the lock is held, the semaphore is the number of times the lock is held, the semaphore is counted, the meaning is slightly different.

4.2 Mutual exclusion and synchronization of semaphore

Semaphores can be used in mutex and synchronization scenarios. There are differences in the use of synchronization and mutex semaphores:

  • A semaphore used for mutual exclusion

The initial semaphore meter value is not 0, indicating the number of available shared resources. A semaphore is acquired before a shared resource is used, then a shared resource is used, and the semaphore is released when it is used. In this way, when the shared resource is exhausted, that is, when the semaphore count is reduced to 0, other tasks that need to acquire the semaphore will be blocked, thus ensuring mutually exclusive access to the shared resource. Semaphores need to be applied and released in pairs, in the same task.

  • A semaphore used for synchronization

When multiple tasks access the same shared resource at the same time, conflicts may occur. In this case, a task synchronization mechanism must be introduced to enable each task to access the shared resource one by one based on service requirements. The essence of task synchronization is that tasks are queued on demand.

A semaphore used for synchronization with an initial semaphore count of 0. Task 1 requests a semaphore and blocks until task 2 or an interrupt releases the semaphore and task 1 enters the Ready or Running state, thus achieving synchronization between tasks. Semaphores can be applied successfully depending on whether other tasks release semaphores, which are applied and released in different tasks.

summary

This article leads us to analyze the source code of hongmeng light kernel semaphore module, including the structure of semaphore, semaphore pool initialization, semaphore creation and deletion, application release, etc. Thanks for reading. If you have any questions or suggestions, please leave a comment at gitee.com/openharmony… . To make it easier to find the light kernel repository, visit gitee.com/openharmony… “, follow Watch, like Star, and Fork to your account, thank you.

Click to follow, the first time to learn about Huawei cloud fresh technology ~