Take a natural course in artificial intelligence, from theory to action, and then click here to enter the portal

1. Semaphore of LiteOS kernel

1.1. The semaphore

In a multitasking operating system, different tasks need to run synchronously, and the semaphore function can provide users with this support. Semaphore is a mechanism for inter-task communication, enabling synchronization between tasks or mutually exclusive access to critical resources.

1.2. Way of using semaphore

Semaphores can be acquired or applied by tasks. Different semaphores are uniquely identified by the semaphore index number. Each semaphore has a count and a task queue.

Usually, the count value of a semaphore is used to correspond to the number of effective resources, indicating the number of remaining mutually exclusive resources that can be occupied. The meaning of the value can be divided into two cases:

  • 0: indicates that there are no accumulated Post operations and there may be tasks blocking on this semaphore.
  • Positive value: indicates that there is one or more Post down the release operation;

When applying for a task (Pend) semaphore, if the application is successful, the count value of the semaphore will decrease. If the application fails, the semaphore will be suspended in the waiting task queue of the semaphore. Once a task releases the semaphore, the task in the waiting task queue will be woken up and start to execute.

1.3. Semaphore usage scenarios

Semaphore is a very flexible synchronization method, can be used in a variety of occasions, to achieve lock, synchronization, resource count and other functions, also can be conveniently used for task and task, interrupt and task synchronization.

  • The mutex

When used as a mutex, the semaphore is full after it is created. When critical resources need to be used, the semaphore should be applied first to make it empty. In this way, when other tasks need to use critical resources, they will be blocked because they cannot apply for the semaphore, thus ensuring the safety of critical resources.

  • Inter-task synchronization

When used for synchronization, the semaphore is set to null after creation, and task 1 is blocked by applying for the semaphore. Task 2 releases the semaphore after some condition occurs, and task 1 enters the READY or RUNNING state, thus achieving synchronization between the two tasks.

  • Resource count

When used as a resource count, a semaphore acts as a special counter that can increase or decrease, but can never be negative. The typical application scenario is the producer and consumer scenario.

  • Interrupts synchronization with tasks

When used as the synchronization of interrupt and task, the value of semaphore can be set to 0 when the interrupt is not triggered, so as to block the interrupt service processing task. Once the interrupt is triggered, the blocked interrupt service processing task will be woken up for interrupt processing.

2. Semaphore API

The semaphore module in Huawei LiteOS provides users with the functions of creating/deleting semaphore and applying/releasing semaphore.

All semaphore apis provided by Huawei LiteOS system start with LOS, but these apis are complicated to use, so in this paper, we use the unified API interface provided by Huawei IoT Link SDK to conduct experiments. These interfaces have been implemented using the API provided by LiteOS, which is more concise for users. The API list is as follows:

At <osal. H >, this header needs to be included to use all the interfaces at the temples, and refer to the statement for detailed parameters of the functions.

The relevant interfaces are defined in osal. C, and litEOS-based interfaces in LiteOS_IMP. C:

The interface name Functional description
osal_semp_create Semaphore creation
osal_semp_del Semaphore deletion
osal_semp_pend Semaphore request
osal_semp_post Semaphore release

2.1. osal_semp_create

The osal_semp_create interface is used to create a semaphore. The interface prototype is as follows:

bool_t  osal_semp_create(osal_semp_t *semp,int limit,int initvalue)
{
    bool_t ret = false;

    if((NULL! = s_os_cb) &&(NULL! = s_os_cb->ops) &&(NULL! = s_os_cb->ops->semp_create)) { ret = s_os_cb->ops->semp_create(semp,limit,initvalue); }return ret;

}
Copy the code

The following table describes the interface parameters:

parameter describe
semp Address of the semaphore index ID
limit The maximum value of a semaphore meter
initvalue The initial value of a semaphore meter value
The return value False – Creation failed
The return value True – Creation succeeded

2.2. osal_semp_del

The osal_semp_del interface is used to delete a semaphore. Its interface prototype is as follows:

bool_t  osal_semp_del(osal_semp_t semp)
{
    bool_t ret = false;

    if((NULL! = s_os_cb) &&(NULL! = s_os_cb->ops) &&(NULL! = s_os_cb->ops->semp_del)) { ret = s_os_cb->ops->semp_del(semp); }return ret;

}
Copy the code

The following table describes the interface parameters:

parameter describe
semp Semaphore index ID
The return value False – Delete failed
The return value True – The deletion succeeded

2.3. osal_semp_pend

The oSAL_semp_pend interface is used to apply for a semaphore. Its interface prototype is as follows:

bool_t  osal_semp_pend(osal_semp_t semp,int timeout)
{
    bool_t ret = false;

    if((NULL! = s_os_cb) &&(NULL! = s_os_cb->ops) &&(NULL! = s_os_cb->ops->semp_pend)) { ret = s_os_cb->ops->semp_pend(semp,timeout); }return ret;

}
Copy the code

The following table describes the interface parameters:

parameter describe
semp Semaphore index ID
timeout 32-bit values, as detailed below
The return value False – Application failed
The return value True – The application was successful

Semaphores can be applied in three modes: non-blocking mode, permanent blocking mode, and timed blocking mode, selected using the timeout parameter.

  • Non-blocking mode (0) :

A task needs to apply for a semaphore. If the number of tasks does not reach the upper limit set by the semaphore, the application is successful. Otherwise, the application fails immediately

  • Permanent blocking mode (CN_OSAL_timeout_forever or 0xFFFFFFFF) :

A task needs to apply for a semaphore. If the number of tasks does not reach the upper limit set by the semaphore, the application is successful. Otherwise, the task is blocked, and the system switches to the task with the highest priority. After a task enters a blocked state, it will not resume execution until another task releases the semaphore

  • Timing blocking mode (arbitrary timing, 32bit) :

A task needs to apply for a semaphore. If the number of tasks does not reach the upper limit set by the semaphore, the application is successful. Otherwise, the task is blocked, and the system switches to the task with the highest priority. After a task enters the blocked state, other tasks will release the semaphore before the specified timeout, or the blocked task will be executed again after the specified timeout

Since interrupts cannot be blocked, blocking mode cannot be used in interrupts when requesting semaphores.

2.4. osal_semp_post

The oSAL_SEMP_POST interface is used to release a semaphore. If a task is blocked by the semaphore, the first task on the blocking queue is woken up. The task enters the ready state and is scheduled.

Its interface prototype is as follows:

bool_t  osal_semp_post(osal_semp_t semp)
{
    bool_t ret = false;

    if((NULL! = s_os_cb) &&(NULL! = s_os_cb->ops) &&(NULL! = s_os_cb->ops->semp_post)) { ret = s_os_cb->ops->semp_post(semp); }return ret;

}
Copy the code

The following table describes the interface parameters:

parameter describe
semp Semaphore index ID
The return value False – Release failed
The return value True – The release succeeded

3. Hands-on experiments — Use semaphores for inter-task synchronization

The experiment content,

In this experiment, two tasks will be created, one low priority task task1 and one high priority task Task2. Semaphore is used to run synchronously between the two tasks, and the operation of the two tasks will be observed in the serial port terminal.

The experimental code

First, open the HelloWorld project used in the previous article and conduct experiments based on this project.

Right-click in the Demo folder and create a new folder osal_kernel_demo to store the kernel experiment files (skip this step if you already have one).

Create a new experiment file osal_semp_demo.c in this folder and start writing code:

/* To use the osal interface, include the header */
#include <osal.h>

/* Task priority macro definition (shell task priority is 10) */
#define USER_TASK1_PRI  12  // Low priority
#define USER_TASK2_PRI  11  // High priority

/* Semaphore index ID */
osal_semp_t sync_semp;

/* Task1 entry function */
static int user_task1_entry(a)
{
    while(1)
    {
        /* Print information on the serial port */
        printf("task 1 post a semp! \r\n");

        /* Release semaphore after printing */
        osal_semp_post(sync_semp);

        /* The task actively suspends 2s */
        osal_task_sleep(2*1000); }}/* Task2 entry function */
static int user_task2_entry(a)
{
    while (1)
    {
        /* High priority, preempt to print information */
        printf("task2 is waiting for a semp... \r\n");

        /* Apply for semaphore, fail to apply, suspend wait */
        osal_semp_pend(sync_semp, cn_osal_timeout_forever);

        /* Once the semaphore is applied, the execution is resumed */
        printf("task 2 access a semp! \r\n"); }}/* Standard demo start function, do not change the function name, otherwise it will affect the next experiment */
int standard_app_demo_main(a)
{
    /* Create semaphore sync_semp */
    osal_semp_create(&sync_semp, 1.0);
    printf("sync_semp semp create success.\r\n");

    /* Create task task1 */
    osal_task_create("user_task1",user_task1_entry,NULL.0x400.NULL,USER_TASK1_PRI);

    /* Create task task2 */
    osal_task_create("user_task2",user_task2_entry,NULL.0x400.NULL,USER_TASK2_PRI);

    return 0;
}
Copy the code

After writing, add the osal_semp_demo file we wrote to the makefile and add the compilation of the entire project:

An easy way to do this is to directly modify the user_demo.mk configuration file in the Demo folder and add the following code:

	#example for osal_semp_demo
	ifeq ($(CONFIG_USER_DEMO), "osal_semp_demo")	
		user_demo_src  = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/osal_kernel_demo/osal_semp_demo.c}
		user_demo_defs = -D CONFIG_OSAL_SEMP_DEMO_ENABLE=1
	endif
Copy the code

Add location as shown below:

This code means:

If the CONFIG_USER_DEMO macro defines a value of osal_semp_demo, then the osal_semp_demo.c file is added to the makefile for compilation.

So how do you configure the CONFIG_USER_DEMO macro definition? At the end of the. Sdkconfig file in the project root directory:

Because we modified the MK configuration file, click the recompile buttonAfter compiling, click the download button to burn the program.

After compiling, there is a warning that user_task1_entry and user_task2_entry do not return a value. While (1) does not return, so ignore it.

The experimental phenomena

After the program is burned, it can be seen that the program has started to run, and the output content of the experiment can be seen in the serial terminal:

Linkmain :V1.2.1 AT 11:30:59 ON Nov 28 2019 Sync_semp semp create success. WELCOME TO IOT_LINK SHELL LiteOS:/>task2 is waiting for a semp... task 1 post a semp! task 2 access a semp! task2 is waiting for a semp... task 1 post a semp! task 2 access a semp! task2 is waiting for a semp... task 1 post a semp! task 2 access a semp! ...Copy the code

Task1 is created first, but its priority is low, so task2 preempts execution. Task2 does not wait for the semaphore, and then suspends printing. The low-priority task Task1 then continues, prints the message and releases the semaphore, after which task2 wakes up and resumes execution.

This article was first published on the public account “McUlover666”. During the course of this tutorial, you can log on to the Huawei Cloud Forum xiaoxiong Pai section to post and communicate!