An interrupt is a signal to the processor emitted by hardware or software indicating an event that needs immediate attention. An interrupt alerts the processor to a high-priority condition requiring the interruption of the current code being executed by the processor. When a hardware interrupt is triggered, the interrupt handler is located based on the interrupt ID and then executed to handle the interrupt.
By using the interrupt mechanism, the CPU responds to the interrupt request from a peripheral only when required, and execute other tasks when the peripherals do not require the CPU. In this way, the CPU does not need to spend a lot of time in waiting and querying the peripheral status, which effectively improves the real-time performance and execution efficiency of the system.
By using the interrupt mechanism, the CPU responds to the interrupt request (IRQ) from a peripheral only when required, and execute other tasks when the peripherals do not require the CPU. In this way, the CPU does not need to spend a lot of time in waiting and querying the peripheral status, which effectively improves the real-time performance and execution efficiency of the system.
To understand interrupts, you need to know the following concepts:
- Interrupt ID
Identifies an IRQ signal. The computer locates the device that sends the IRQ based on the interrupt ID.
- IRQ
An electrical pulse signal sent to the CPU, alerting the CPU to a high-priority event requiring the interruption of the current code being executed by the CPU.
- Interrupt priority
Prioritizes the sources that trigger interrupts based on the importance and urgency of interrupt events, so that the CPU can respond to and handle all interrupts in a timely manner.
- Interrupt handler
A program executed by the CPU to respond to the IRQ from a peripheral. Each device that triggers an interrupt has its own interrupt handler.
- Interrupt triggering
The interrupt source sends an interrupt signal to the interrupt controller. The interrupt controller arbitrates all pending interrupts, determines the priority, and sends the interrupt signal to the CPU. When an interrupt source generates an interrupt signal, the interrupt trigger is set to **1**, alerting the CPU to respond to the interrupt.
- Interrupt vector
Entry address of an interrupt handler.
- Interrupt vector table
An area for storing interrupt vectors. It stores the mapping between interrupt vectors and interrupt IDs.
The following describes the concepts related to interrupts:
- Interrupt ID
## Available APIs
Identifies an interrupt request signal. The computer locates the device that sends the interrupt request based on the interrupt ID.
The following table describes APIs available for the OpenHarmony LiteOS-M interrupt module. For more details about the APIs, see the API reference.
- Interrupt request
**Table 1** APIs for creating and deleting an interrupt
A process in which an electrical pulse signal is sent to the CPU, alerting the CPU to a high-priority event requiring the interruption of the current code being executed by the CPU.
| API| Description|
| -------- | -------- |
| LOS_HwiCreate | Creates an interrupt and registers the interrupt ID, triggering mode, priority, and interrupt handler. When an interrupt is triggered, the interrupt handler will be called.|
| LOS_HwiDelete | Deletes an interrupt based on the specified interrupt ID.|
- Interrupt priority
**Table 2** APIs for enabling and disabling interrupts
Prioritizes the sources that trigger interrupts based on the importance and urgency of interrupt events, so that the CPU can respond to and handle all interrupts in a timely manner.
| API| Description|
| -------- | -------- |
| LOS_IntUnLock | Enables the CPU to respond to all IRQs.|
| LOS_IntLock | Disables the CPU from responding to IRQs.|
| LOS_IntRestore | Restores the interrupt status before the **LOS_IntLock** and **LOS_IntUnLock** operations are performed.|
- Interrupt handler
**Table 3** APIs for other interrupt operations
A program executed by the CPU to respond to the interrupt request from a peripheral. Each device that triggers an interrupt has its own interrupt handler.
| API | Description |
| :----------------- | ---------------- |
| LOS_HwiTrigger | Triggers an interrupt. |
| LOS_HwiEnable | Enables interrupts. |
| LOS_HwiDisable | Disables interrupts. |
| LOS_HwiClear | Clears an interrupt manually. |
| LOS_HwiSetPriority | Sets the interrupt priority.|
| LOS_HwiCurIrqNum | Obtains the current interrupt ID.|
- Interrupt triggering
The interrupt source sends an interrupt signal to the interrupt controller. The interrupt controller arbitrates all pending interrupts, determines the priority, and sends the interrupt signal to the CPU. When an interrupt source generates an interrupt signal, the interrupt trigger is set to **1**, alerting the CPU to respond to the interrupt.
## How to Develop
- Interrupt vector
1. Call **LOS_HwiCreate** to create an interrupt.
Entry address of an interrupt handler.
2. Call **LOS_HwiTrigger()** to trigger an interrupt (write the related register of the interrupt controller to simulate an external interrupt) or trigger an interrupt by using a peripheral.
- Interrupt vector table
3. Call **LOS_HwiDelete()** to delete an interrupt. Use this API based on actual requirements.
An area for storing interrupt vectors. It stores the mapping between interrupt vectors and interrupt IDs.
> **NOTE**
> - Set the maximum number of interrupts supported and the number of configurable interrupt priorities based on the hardware.
> - Avoid long interrupt disabling time or interrupt handler processing time. Otherwise, the CPU cannot respond to interrupts in a timely manner.
> - Do not directly or indirectly call the API that causes scheduling, such as **LOS_Schedule**, during the interrupt response process.
> - The input parameter of **LOS_IntRestore()** must be the return value of **LOS_IntLock()**, that is, the current program status register (CPSR) value before the interrupt is disabled.
> - Interrupts 0 to 15 are for internal use of the Cortex-M series processors. You are advised not to apply for or create interrupts 0 to 15.
## Available APIs
The following table describes APIs available for the OpenHarmony LiteOS-M interrupt module. For more details about the APIs, see the API reference.
## Development Example
**Table 1** APIs of the interrupt module
| Category| API| Description|
| -------- | -------- | -------- |
| Creating or deleting an interrupt| LOS_HwiCreate | Creates an interrupt and registers the interrupt ID, triggering mode, priority, and interrupt handler. When an interrupt is triggered, the interrupt handler will be called.|
| | LOS_HwiDelete | Deletes an interrupt based on the specified interrupt ID.|
| Locking or unlocking interrupts| LOS_IntUnLock | Enables the CPU to respond to all interrupt requests.|
| | LOS_IntLock | Disables the CPU from responding to interrupt requests.|
| | LOS_IntRestore | Restores the interrupt status before the **LOS_IntLock** and **LOS_IntUnLock** operations are performed.|
| Enabling or disabling an interrupt| LOS_HwiDisable | Disables the CPU from responding to the specified interrupt by setting the register.|
| | LOS_HwiEnable | Enables the CPU to respond to the specified interrupt by setting the register.|
| Setting the interrupt priority| LOS_HwiSetPriority | Sets the interrupt priority.|
| Triggering an interrupt| LOS_HwiTrigger | Triggers an interrupt (simulate an external interrupt by writing the related register of the interrupt controller).|
| Clearing interrupt register status| LOS_HwiClear | Clears the status bit of the interrupt register corresponding to the interrupt ID. The implementation of this API depends on the interrupt controller version. It is optional.|
This example implements the following:
## How to Develop
1. Create an interrupt.
1. Call **LOS_HwiCreate** to create an interrupt.
2. Call **LOS_HwiTrigger** to trigger the interrupt.
3. Call **LOS_HwiDelete** to delete the specified interrupt. Use this API based on actual requirements.
>- Configure the maximum number of interrupts supported and the number of configurable interrupt priorities based on the specific hardware.
>- If the interrupt handler takes long time, the CPU cannot respond to interrupt requests in a timely manner.
>- Functions that trigger **LOS\_Schedule** cannot be directly or indirectly executed during interrupt response process.
>- The input parameter of **LOS\_IntRestore\(\)** must be the return value of **LOS\_IntLock\(\)**, that is, the current program status register \(CPSR\) value before the interrupt is disabled. Interrupts 0 to 15 in the Cortex-M series processors are for internal use. You are advised not to apply for or create interrupts 0 to 15.
3. Delete an interrupt.
## Development Example
The following sample code demonstrates how to create an interrupt, trigger the interrupt to invoke the interrupt handler, and delete the interrupt.
This example implements the following:
1. Create an interrupt.
2. Trigger an interrupt.
3. Delete an interrupt.
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleInterrupt()** in **TestTaskEntry**.
The following sample code shows how to create and delete an interrupt. When the interrupt **HWI\_NUM\_TEST** is generated, the interrupt handler function will be called.
```
#include "los_interrupt.h"
#include "los_compiler.h"
/* Create an interrupt. */
/* Interrupt ID to verify */
#define HWI_NUM_TEST 7
STATIC VOID HwiUsrIrq(VOID)
/* Interrupt handler */
STATIC VOID UsrIrqEntry(VOID)
{
printf("in the func UsrIrqEntry\n");
}
/* Register a thread callback to trigger the interrupt */
An event is a mechanism for communication between tasks. It can be used to synchronize tasks. The events have the following features:
- Events can be synchronized in one-to-many or many-to-many mode. In one-to-many mode, a task can wait for multiple events. In many-to-many mode, multiple tasks can wait for multiple events. However, a write event wakes up only one task from the block.
- Event read timeout mechanism is used.
- Events are used only for task synchronization, but not for data transmission.
- Events can be synchronized in one-to-many or many-to-many mode. In one-to-many mode, a task can wait for multiple events. In many-to-many mode, multiple tasks can wait for multiple events. However, a write event wakes up only one task from the block.
- Event read timeout mechanism is used.
- Events are used only for task synchronization, but not for data transmission.
APIs are provided to initialize, read/write, clear, and destroy events.
## Working Principles
### Event Control Block
The event control block is a struct configured in the event initialization function. It is passed in as an input parameter to identify the event for operations such as event read and write. The data structure of the event control block is as follows:
```
/**
* Event control block data structure
*/
typedef struct tagEvent {
UINT32 uwEventID; /* Event set, which is a collection of events processed (written and cleared). */
LOS_DL_LIST stEventList; /* List of tasks waiting for specific events*/
LOS_DL_LIST stEventList; /* List of tasks waiting for specific events. */
} EVENT_CB_S, *PEVENT_CB_S;
```
### Working Principles<a name="section187761153144617"></a>
### Working Principles
**Initializing an event**: An event control block is created to maintain a collection of processed events and a linked list of tasks waiting for specific events.
...
...
@@ -32,127 +37,98 @@ typedef struct tagEvent {
**Reading an event**: If the read event already exists, it is returned synchronously. In other cases, the return time is determined based on the timeout period and event triggering status. If the wait event condition is met before the timeout period expires, the blocked task will be directly woken up. Otherwise, the blocked task will be woken up only after the timeout period has expired.
The input parameters **eventMask** and **mode** determine whether the condition for reading an event is met. **eventMask** indicates the mask of the event. **mode** indicates the handling mode, which can be any of the following:
The input parameters **eventMask** and **mode** determine whether the condition for reading an event is met. **eventMask** indicates the mask of the event. **mode** indicates the handling mode, which can be any of the following:
-**LOS_WAITMODE_AND**: Event reading is successful only when all the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned.
-**LOS_WAITMODE_OR**: Event reading is successful when any of the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned.
-**LOS\_WAITMODE\_AND**: Event reading is successful only when all the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned.
-**LOS\_WAITMODE\_OR**: Event reading is successful when any of the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned.
-**LOS\_WAITMODE\_CLR**: This mode must be used with **LOS\_WAITMODE\_AND** or **LOS\_WAITMODE\_OR**\(LOS\_WAITMODE\_AND | LOS\_WAITMODE\_CLR or LOS\_WAITMODE\_OR | LOS\_WAITMODE\_CLR\). In this mode, if **LOS\_WAITMODE\_AND** or **LOS\_WAITMODE\_OR** is successful, the corresponding event type bit in the event control block will be automatically cleared.
-**LOS_WAITMODE_CLR**: This mode must be used with one or all of the event modes (LOS_WAITMODE_AND | LOS_WAITMODE_CLR or LOS_WAITMODE_OR | LOS_WAITMODE_CLR). In this mode, if all event modes or any event mode is successful, the corresponding event type bit in the event control block will be automatically cleared.
**Clearing event**: Clear the event set of the event control block based on the specified mask. If the mask is **0**, the event set will be cleared. If the mask is **0xffff**, no event will be cleared, and the event set remains unchanged.
**Clearing events**: Clear the event set of the event control block based on the specified mask. If the mask is **0**, the event set will be cleared. If the mask is**0xffff**, no event will be cleared, and the event set remains unchanged.
**Destroying an event**: Destroy the specified event control block.
**Figure 1** Event working mechanism for mini systems<aname="fig17799175324612"></a>
**Figure 1** Event working mechanism for a mini system
<tdclass="cellrowborder"valign="top"width="59.2959295929593%"headers="mcps1.1.4.1.3 "><pid="p1717215119159"><aname="p1717215119159"></a><aname="p1717215119159"></a>Checks whether the expected event occurs based on <strongid="b209271084844433"><aname="b209271084844433"></a><aname="b209271084844433"></a>eventID</strong>, <strongid="b121646215244433"><aname="b121646215244433"></a><aname="b121646215244433"></a>eventMask</strong>, and <strongid="b192170443144433"><aname="b192170443144433"></a><aname="b192170443144433"></a>mode</strong>.</p>
<divclass="notice"id="note29631113132915"><aname="note29631113132915"></a><aname="note29631113132915"></a><spanclass="noticetitle"> NOTICE: </span><divclass="noticebody"><pid="p886616817302"><aname="p886616817302"></a><aname="p886616817302"></a>If <strongid="b50092866644433"><aname="b50092866644433"></a><aname="b50092866644433"></a>mode</strong> contains <strongid="b195286359344433"><aname="b195286359344433"></a><aname="b195286359344433"></a>LOS_WAITMODE_CLR</strong> and the expected event occurs, the event that meets the requirements in <strongid="b33985475544433"><aname="b33985475544433"></a><aname="b33985475544433"></a>eventID</strong> will be cleared. In this case, <strongid="b637217044433"><aname="b637217044433"></a><aname="b637217044433"></a>eventID</strong> is an input parameter and an output parameter. In other cases, <strongid="b4992196844433"><aname="b4992196844433"></a><aname="b4992196844433"></a>eventID</strong> is used only as an input parameter.</p>
<tdclass="cellrowborder"valign="top"width="59.2959295929593%"headers="mcps1.1.4.1.3 "><pid="p1936143993419"><aname="p1936143993419"></a><aname="p1936143993419"></a>Initializes an event control block.</p>
<tdclass="cellrowborder"valign="top"width="59.2959295929593%"headers="mcps1.1.4.1.3 "><pid="p1935911398345"><aname="p1935911398345"></a><aname="p1935911398345"></a>Reads an event (wait event). The task is blocked to wait based on the timeout period (in ticks).</p>
<pid="p624360131813"><aname="p624360131813"></a><aname="p624360131813"></a>If no event is read, <strongid="b129351022744433"><aname="b129351022744433"></a><aname="b129351022744433"></a>0</strong> is returned.</p>
<pid="p825491321911"><aname="p825491321911"></a><aname="p825491321911"></a>If an event is successfully read, a positive value (event set) is returned.</p>
<pid="p262373895217"><aname="p262373895217"></a><aname="p262373895217"></a>In other cases, a specific error code is returned.</p>
<tdclass="cellrowborder"valign="top"width="59.2959295929593%"headers="mcps1.1.4.1.3 "><pid="p526932914325"><aname="p526932914325"></a><aname="p526932914325"></a>Writes a specific event to the event control block.</p>
<tdclass="cellrowborder"valign="top"width="59.2959295929593%"headers="mcps1.1.4.1.3 "><pid="p19140637968"><aname="p19140637968"></a><aname="p19140637968"></a>Clears an event in the event control block based on the event mask.</p>
<tdclass="cellrowborder"valign="top"width="59.2959295929593%"headers="mcps1.1.4.1.3 "><pid="p17171501971"><aname="p17171501971"></a><aname="p17171501971"></a>Destroys an event control block.</p>
</td>
</tr>
</tbody>
</table>
| Category| API| Description|
| -------- | -------- | -------- |
| Event check| LOS_EventPoll | Checks whether the expected event occurs based on **eventID**, **eventMask**, and **mode**.<br>**NOTICE**<br><br>If **mode** contains **LOS_WAITMODE_CLR** and the expected event occurs, the event that meets the requirements in **eventID** will be cleared. In this case, **eventID** is an input parameter and an output parameter. In other cases, **eventID** is used only as an input parameter.|
| Initialization| LOS_EventInit | Initializes an event control block.|
| Event read| LOS_EventRead | Reads an event (wait event). The task will be blocked to wait based on the timeout period (in ticks).<br>If no event is read, **0** is returned.<br>If an event is successfully read, a positive value (event set) is returned.<br>In other cases, an error code is returned.|
| Event write| LOS_EventWrite | Writes an event to the event control block.|
| Event clearance| LOS_EventClear | Clears an event in the event control block based on the event mask.|
| Event destruction| LOS_EventDestroy | Destroys an event control block.|
## How to Develop
The typical event development process is as follows:
1. Initialize an event control block.
2. Block a read event control block.
3. Write related events.
4. Wake up a blocked task, read the event, and check whether the event meets conditions.
5. Handle the event control block.
6. Destroy an event control block.
1. Initialize an event control block.
2. Block a read event control block.
3. Write related events.
4. Wake up a blocked task, read the event, and check whether the event meets conditions.
5. Handle the event control block.
6. Destroy an event control block.
> **NOTE**
> - When an event is read or written, the 25th bit of the event is reserved and cannot be set.
>
> - Repeated writes of the same event are treated as one write.
>- When an event is read or written, the 25th bit of the event is reserved and cannot be set.
>- Repeated writes of the same event are treated as one write.
## Development Example
### Example Description
In this example, run the **Example\_TaskEntry** task to create the **Example\_Event** task. Run the **Example\_Event** task to read an event to trigger task switching. Run the **Example\_TaskEntry** task to write an event. You can understand the task switching during event operations based on the sequence in which logs are recorded.
In the **ExampleEvent** task, create an **EventReadTask** task with a timout period of 100 ticks. When the **EventReadTask** task is blocked, **ExampleEvent** task writes an event. You can understand the task switching during event operations based on the sequence in which logs are recorded.
1. In the **ExampleEvent** task, create an **EventReadTask** task with a timeout period of 100 ticks. The **EventReadTask** task has a higher priority than the **ExampleEvent** task.
2.**EventReadTask** is scheduled to read event **0x00000001**, but suspended to wait 100 ticks. The **ExampleEvent** task is scheduled to write event **0x00000001**.
1. Create the **Example\_Event** task in the **Example\_TaskEntry** task with a higher priority than the **Example\_TaskEntry** task.
2. Run the **Example\_Event** task to read event **0x00000001**. Task switching is triggered to execute the **Example\_TaskEntry** task.
3. Run the **Example\_TaskEntry** task to write event **0x00000001**. Task switching is triggered to execute the **Example\_Event** task.
4. The **Example\_Event** task is executed.
5. The **Example\_TaskEntry** task is executed.
3. When **ExampleEvent** is scheduled to write event **0x00000001**, the wait time of **EventReadTask** expires and **EventReadTask** task is scheduled to run.
### Sample Code<a name="section149077554912"></a>
4. The **EventReadTask** task is executed.
5. The **ExampleEvent** task is executed.
### Sample Code
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleEvent()** in **TestTaskEntry**.
```
#include "los_event.h"
#include "los_task.h"
#include "securec.h"
/* Task ID*/
UINT32 g_testTaskId;
/* Event control structure*/
/* Event control struct*/
EVENT_CB_S g_exampleEvent;
/* Type of the wait event*/
/* Type of the wait event*/
#define EVENT_WAIT 0x00000001
/* Example task entry function*/
VOID Example_Event(VOID)
/* Wait timeout time */
#define EVENT_TIMEOUT 100
/* Example task entry function */
VOID EventReadTask(VOID)
{
UINT32 ret;
UINT32 event;
...
...
@@ -160,39 +136,39 @@ VOID Example_Event(VOID)
/* Set a timeout period for event reading to 100 ticks. If the specified event is not read within 100 ticks, the read operation times out and the task is woken up. */
A mutual exclusion \(mutex\) is a special binary semaphore used for exclusive access to shared resources.
A mutual exclusion (mutex) is a special binary semaphore used for exclusive access to shared resources.
A mutex can be unlocked or locked. When a mutex is held by a task, the mutex is locked and the task obtains the ownership of the mutex. When the task releases the mutex, the mutex is unlocked and the task will lose the ownership of the mutex. When a task holds a mutex, other tasks cannot unlock or hold the mutex.
A mutex can be unlocked or locked. When a task holds a mutex, the mutex is locked and the task obtains the ownership of the mutex. When the task releases the mutex, the mutex is unlocked and the task loses the ownership of the mutex. When a task holds a mutex, other tasks cannot unlock or hold the mutex.
In an environment where multiple tasks compete for shared resources, the mutex can protect the shared resources via exclusive access. In addition, the mutex can prevent semaphore priority inversion, which occurs when a low-priority task holds a semaphore but a high-priority task has to wait until the low-priority task releases it.
## Working Principles<a name="section115161649726"></a>
## Working Principles
In a multi-task environment, multiple tasks may access the same shared resources. However, certain shared resources are not shared, and can only be accessed exclusively by tasks. A mutex can be used to address this issue.
When non-shared resources are accessed by a task, the mutex is locked. Other tasks will be blocked until the mutex is released by the task. The mutex allows only one task to access the shared resources at a time, ensuring integrity of operations on the shared resources.
**Figure 1** Mutex working mechanism for mini systems
**Figure 1** Mutex working mechanism for a mini system
<tbody><trid="row37115291166"><tdclass="cellrowborder"rowspan="2"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.1 "><pid="p1795312108911"><aname="p1795312108911"></a><aname="p1795312108911"></a>Creating or deleting a mutex</p>
<tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.3 "><pid="p171112291967"><aname="p171112291967"></a><aname="p171112291967"></a>Creates a mutex.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p137111129965"><aname="p137111129965"></a><aname="p137111129965"></a>Deletes the specified mutex.</p>
</td>
</tr>
<trid="row5711192912616"><tdclass="cellrowborder"rowspan="2"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.1 "><pid="p86087143910"><aname="p86087143910"></a><aname="p86087143910"></a>Requesting or releasing a mutex</p>
<tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.3 "><pid="p1271110291969"><aname="p1271110291969"></a><aname="p1271110291969"></a>Requests the specified mutex.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p107118291660"><aname="p107118291660"></a><aname="p107118291660"></a>Releases the specified mutex.</p>
</td>
</tr>
</tbody>
</table>
**Table 1** APIs of the mutex module
| Category| Description|
| -------- | -------- |
| Creating or deleting a mutex| **LOS_MuxCreate**: creates a mutex.<br>**LOS_MuxDelete**: eeletes a mutex.|
| Requesting or releasing a mutex| **LOS_MuxPend**: requests a mutex.<br>**LOS_MuxPost**: releases a mutex.|
## How to Develop
The typical mutex development process is as follows:
1. Call **LOS\_MuxCreate** to create a mutex.
2. Call **LOS\_MuxPend** to request a mutex.
1. Call **LOS_MuxCreate** to create a mutex.
The following modes are available:
2. Call **LOS_MuxPend** to request a mutex.
The following modes are available:
- Non-block mode: A task acquires the mutex if the requested mutex is not held by any task or the task holding the mutex is the same as the task requesting the mutex.
- Permanent block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be unlocked and executed only when the mutex is released.
- Scheduled block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be executed only when the mutex is released within the specified timeout period or when the specified timeout period expires.
- Non-block mode: A task acquires the mutex if the requested mutex is not held by any task or the task holding the mutex is the same as the task requesting the mutex.
- Permanent block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with a highest priority in the ready queue will be executed. The blocked task can be unlocked and executed only when a mutex is acquired.
- Scheduled block mode: A task acquires the mutex if the requested mutex is not occupied. If the mutex is occupied, the task will be blocked and the task with the highest priority in the ready queue will be executed. The blocked task can be executed only when the mutex is released within the specified timeout period or when the specified timeout period expires.
3. Call **LOS\_MuxPost** to release a mutex.
- If tasks are blocked by the specified mutex, the task with a higher priority will be unblocked when the mutex is released. The unblocked task changes to the Ready state and is scheduled.
- If no task is blocked by the specified mutex, the mutex is released successfully.
3.Call **LOS_MuxPost** to release a mutex.
- If tasks are blocked by the specified mutex, the task with a higher priority will be unblocked when the mutex is released. The unblocked task changes to the Ready state and is scheduled.
- If no task is blocked by the specified mutex, the mutex is released successfully.
4. Call **LOS\_MuxDelete** to delete a mutex.
4. Call **LOS_MuxDelete** to delete a mutex.
> **NOTE**
> - Nested mutexes are supported. That is, if a task that attempts to apply for a mutex and the task that already holds the mutex are the same task, the application is considered successful, and the lock is released based on the number of application times.
>
> - Mutexes cannot be used in an interrupt handler.
>
> - The LiteOS-M kernel must ensure real-time task scheduling and avoid long-time task blocking. Therefore, a mutex must be released as soon as possible after use.
>
> - When a mutex is held by a task, the task priority cannot be changed by using APIs such as **LOS_TaskPriSet**.
>- Two tasks cannot lock the same mutex. If a task attempts to lock a mutex held by another task, the task will be blocked until the mutex is unclocked.
>- Mutexes cannot be used in the interrupt service program.
>- When using the LiteOS-M kernel, OpenHarmony must ensure real-time task scheduling and avoid long-time task blocking. Therefore, a mutex must be released as soon as possible after use.
>- When a mutex is held by a task, the task priority cannot be changed by using APIs such as **LOS\_TaskPriSet**.
## Development Example
### Example Description
This example implements the following:
1. Create the **Example\_TaskEntry** task. In this task, create a mutex to lock task scheduling, and create two tasks **Example\_MutexTask1**\(with a lower priority\) and **Example\_MutexTask2**\(with a higher priority\) to unlock task scheduling.
2. When being scheduled, **Example\_MutexTask2** requests a mutex in permanent block mode. After acquiring the mutex, **Example\_MutexTask2** enters the sleep mode for 100 ticks. **Example\_MutexTask2** is suspended, and **Example\_MutexTask1** is woken up.
3.**Example\_MutexTask1** requests a mutex in scheduled block mode, and waits for 10 ticks. Because the mutex is still held by **Example\_MutexTask2**, **Example\_MutexTask1** is suspended. After 10 ticks, **Example\_MutexTask1** is woken up and attempts to request a mutex in permanent block mode. **Example\_MutexTask1** is suspended because the mutex is still held by **Example\_MutexTask2**.
4. After 100 ticks, **Example\_MutexTask2** is woken up and releases the mutex, and then **Example\_MutexTask1** is woken up. **Example\_MutexTask1** acquires the mutex and then releases the mutex. At last, the mutex is deleted.
1. Create a mutex for the **ExampleMutex** task. Lock task scheduling, and create two tasks **ExampleMutexTask1** and **ExampleMutexTask2**. Enable **ExampleMutexTask2** to permanently wait until a mutex is acquired, and enter sleep for 10 ticks after successfully acquiring a mutest. Enable **ExampleMutexTask1** to apply for a mutex with a timeout period of 10 ticks and then wait permanently until obtaining a mutex. **ExampleMutexTask2** has a higher priority than **ExampleMutexTask1**. Then, unlock task scheduling.
2.**ExampleMutexTask2** (which has a higher priority) is scheduled and applies for a mutex. After acquiring the mutex, **ExampleMutexTask2** starts to sleep for 100 ticks. **ExampleMutexTask2** is suspended, and **ExampleMutexTask1** is woken up.
3.**ExampleMutexTask1** applies for the mutex with a timeout period of 10 ticks. Because the mutex is still held by **ExampleMutexTask2**, **ExampleMutexTask1** is suspended. After 10 ticks, **ExampleMutexTask1** is woken up and starts to wait permanently for a mutex. **ExampleMutexTask1** is suspended because the mutex is still held by **ExampleMutexTask2**.
4. After 100 ticks, **ExampleMutexTask2** is woken up and releases the mutex, and **ExampleMutexTask1** is woken up. **ExampleMutexTask1** acquires the mutex and is executed. After the task is complte, **ExampleMutexTask1** releases the mutex. At last, the mutex is deleted.
### Sample Code
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleMutex** in **TestTaskEntry**.
```
#include <string.h>
#include "los_mux.h"
/* Mutex handler ID*/
/* Mutex handle. */
UINT32 g_testMux;
/* Task ID*/
UINT32 g_testTaskId01;
UINT32 g_testTaskId02;
VOID Example_MutexTask1(VOID)
VOID ExampleMutexTask1(VOID)
{
UINT32 ret;
printf("task1 try to get mutex, wait 10 ticks.\n");
/* Request a mutex. */
ret = LOS_MuxPend(g_testMux, 10);
if (ret == LOS_OK) {
printf("task1 get mutex g_testMux.\n");
/*Release the mutex. */
/* Release the mutex. This branch is reserved for exceptions. */
LOS_MuxPost(g_testMux);
LOS_MuxDelete(g_testMux);
return;
}
}
if (ret == LOS_ERRNO_MUX_TIMEOUT ) {
printf("task1 timeout and try to get mutex, wait forever.\n");
/* Request a mutex. */
ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/*Release the mutex. */
LOS_MuxPost(g_testMux);
/* Delete the mutex. */
LOS_MuxDelete(g_testMux);
printf("task1 post and delete mutex g_testMux.\n");
return;
}
printf("task1 timeout and try to get mutex, wait forever.\n");
/* Request a mutex. */
ret = LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("task1 wait forever, get mutex g_testMux.\n");
/* Release the mutex. */
LOS_MuxPost(g_testMux);
/* Delete the mutex. */
LOS_MuxDelete(g_testMux);
printf("task1 post and delete mutex g_testMux.\n");
return;
}
}
return;
}
VOID Example_MutexTask2(VOID)
VOID ExampleMutexTask2(VOID)
{
printf("task2 try to get mutex, wait forever.\n");
/* Request a mutex. */
(VOID)LOS_MuxPend(g_testMux, LOS_WAIT_FOREVER);
printf("task2 get mutex g_testMux and suspend 100 ticks.\n");
/* Enable the task to enter sleep mode for 100 ticks. */
/* Enable the task to enter sleep mode for 100 ticks. */
Semaphore is a mechanism for implementing communication between tasks. It implements synchronization between tasks or exclusive access to shared resources.
In the data structure of a semaphore, there is usually a counter value indicating the available resources. The counter value can be:
-**0**: The semaphore is unavailable. In this case, tasks waiting for the semaphore may exist.
- Positive number: The semaphore is available.
-**0**: The semaphore is unavailable. In this case, tasks waiting for the semaphore may exist.
- Positive number: The semaphore is available.
Semaphores can be used to solve a problem of mutual exclusion or process synchronization. The usage of the counter value varies with the function of the semaphore.
The usage of the counter value varies with the function of the semaphore.
- If the semaphore is used to solve a problem of mutual exclusion, the counter value indicates the number of units of the shared resources available and its initial value cannot be **0**. The semaphore must be acquired before the shared resource is used, and released after the resource is used. When all shared resources are used, the semaphore counter is reduced to **0** and the tasks that need to obtain the semaphores will be blocked. This ensures exclusive access to shared resources. In addition, when the number of shared resources is **1**, a binary semaphore (similar to the mutex mechanism) is recommended.
- If the semaphore is used to solve a problem of process synchronization, the initial semaphore counter value is **0**. Task 1 cannot acquire the semaphore and is blocked. Task 1 enters Ready or Running state only when the semaphore is released by task 2 or an interrupt. In this way, task synchronization is implemented.
- If the semaphore is used as a mutex, the counter value indicates the number of units of the shared resources available and its initial value cannot be **0**. The semaphore must be acquired before the shared resource is used, and released after the resource is used. When all shared resources are used, the semaphore counter is reduced to **0** and the tasks that need to obtain the semaphores will be blocked. This ensures exclusive access to shared resources. In addition, when the number of shared resources is **1**, a binary semaphore \(similar to the mutex mechanism\) is recommended.
- If the semaphore is used for synchronization, the initial semaphore counter value is **0**. When a task fails to acquire the semaphore, it will be blocked and enters Ready or Running state only when the semaphore is released. In this way, task synchronization is implemented.
## Working Principles
### Semaphore control block
### Semaphore Control Block
```
/**
* Data structure of the semaphore control block
* Data structure of the semaphore control block
*/
typedef struct {
UINT16 semStat; /* Semaphore status */
UINT16 semType; /* Semaphore type */
UINT16 semCount; /* Semaphore count */
UINT16 semId; /* Semaphore index */
LOS_DL_LIST semList; /* Insert the task blocked by the semaphore to the DL list.*/
LOS_DL_LIST semList; /* Insert the task blocked by the semaphore to the DL list.*/
} LosSemCB;
```
### Working Principles
Initializing semaphores: Request memory for the semaphores configured \(the number of semaphores can be configured in the **LOSCFG\_BASE\_IPC\_SEM\_LIMIT** macro by users\), set all semaphores to the unused state, and add them to the linked list for unused semaphores.
Initializing semaphores: Request memory for the semaphores configured (the number of semaphores can be configured in the **LOSCFG_BASE_IPC_SEM_LIMIT** macro), set all semaphores to the unused state, and add them to the linked list for unused semaphores.
Creating a semaphore: Obtain a semaphore from the linked list for unused semaphores and set its initial value.
Requesting a semaphore: If the counter value is greater than **0**, the system allocates a semaphore, decreases the counter value by 1, and returns a success message. If the counter value is **0**, the task is blocked and waits for other tasks to release a semaphore. The waiting timeout period can be set. When a task is blocked by a semaphore, the task will be added to the end of the semaphore waiting task queue.
Semaphore request: If the counter value is greater than 0, the system allocates a semaphore, decreases the value by 1, and returns a success message. Otherwise, the system blocks the task and adds the task to the end of a task queue waiting for semaphores. The wait timeout period can be set.
Releasing a semaphore: If there is no task waiting for the semaphore released, the counter is incremented by 1. Otherwise, wake up the first task in the semaphore waiting queue.
When a semaphore is released, if there is no task waiting for it, the counter is increased by 1. Otherwise, the first task in the wait queue is woken up.
Deleting a semaphore: Set the semaphore in use to the unused state, and adds it to the linked list for unused semaphores.
Semaphore deletion: The system sets a semaphore in use to the unused state and inserts it to the linked list of unused semaphores.
A semaphore can also be used to limit the number of tasks that can access the shared resource at the same time. When the number of tasks accessing the resource reaches the limit, other tasks will be blocked until a task releases the semaphore.
A semaphore places a limit on the number of tasks accessing the shared resource concurrently. When the number of tasks accessing the shared resource reaches the maximum, other tasks that attempt to obtain the resource are blocked until a semaphore is released.
**Figure 1** Semaphore working mechanism for mini systems<aname="fig467314634214"></a>
**Figure 1** Semaphore working mechanism for the mini system
<tbody><trid="row168052913104"><tdclass="cellrowborder"rowspan="3"valign="top"width="20.1%"headers="mcps1.1.4.1.1 "><pid="p180618915101"><aname="p180618915101"></a><aname="p180618915101"></a>Creating or deleting a semaphore</p>
<tdclass="cellrowborder"valign="top"width="59.38%"headers="mcps1.1.4.1.3 "><pid="p1980609121010"><aname="p1980609121010"></a><aname="p1980609121010"></a>Creates a semaphore and returns the semaphore ID.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.1.4.1.2 "><pid="p780614919107"><aname="p780614919107"></a><aname="p780614919107"></a>Creates a binary semaphore. The maximum counter value is <strongid="b19111879045016"><aname="b19111879045016"></a><aname="b19111879045016"></a>1</strong>.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.1.4.1.2 "><pid="p168063941015"><aname="p168063941015"></a><aname="p168063941015"></a>Deletes a semaphore.</p>
</td>
</tr>
<trid="row188061098102"><tdclass="cellrowborder"rowspan="2"valign="top"width="20.1%"headers="mcps1.1.4.1.1 "><pid="p5806179161014"><aname="p5806179161014"></a><aname="p5806179161014"></a>Requesting or releasing a semaphore</p>
<tdclass="cellrowborder"valign="top"width="59.38%"headers="mcps1.1.4.1.3 "><pid="p4806149191011"><aname="p4806149191011"></a><aname="p4806149191011"></a>Requests a specified semaphore and sets the timeout period.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.1.4.1.2 "><pid="p178061917109"><aname="p178061917109"></a><aname="p178061917109"></a>Posts (releases) a semaphore.</p>
</td>
</tr>
</tbody>
</table>
| Category| Description|
| -------- | -------- |
| Creating or deleting a semaphore| **LOS_SemCreate**: creates a semaphore and returns the semaphore ID.<br>**LOS_BinarySemCreate**: creates a binary semaphore. The maximum count value is **1**.<br>**LOS_SemDelete**: deletes a semaphore.|
| Requesting or releasing a semaphore| **LOS_SemPend**: requests a semaphore and sets the timeout period.<br>**LOS_SemPost**: releases a semaphore.|
## How to Develop
1. Call **LOS\_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS\_BinarySemCreate**.
2. Call **LOS\_SemPend** to request a semaphore.
3. Call **LOS\_SemPost** to release a semaphore.
4. Call **LOS\_SemDelete** to delete a semaphore.
1. Call **LOS_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS_BinarySemCreate**.
2. Call **LOS_SemPend** to request a semaphore.
3. Call **LOS_SemPost** to release a semaphore.
4. Call **LOS_SemDelete** to delete a semaphore.
> **NOTE**<br>
> As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts.
>As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts.
## Development Example
### Example Description
This example implements the following:
1. Create a semaphore in task **ExampleSem** and lock task scheduling. Create two tasks **ExampleSemTask1** and **ExampleSemTask2**\(with higher priority\). Enable the two tasks to request the same semaphore. Unlock task scheduling. Enable task **ExampleSem** to enter sleep mode for 400 ticks. Release the semaphore in task **ExampleSem**.
2. Enable** ExampleSemTask2** to enter sleep mode for 20 ticks after acquiring the semaphore. \(When **ExampleSemTask2** is delayed, **ExampleSemTask1** is woken up.\)
3. Enable **ExampleSemTask1** to request the semaphore in scheduled block mode, with a wait timeout period of 10 ticks. \(Because the semaphore is still held by **ExampleSemTask2**, **ExampleSemTask1** is suspended. **ExampleSemTask1** is woken up after 10 ticks.\) Enable **ExampleSemTask1** to request the semaphore in permanent block mode after it is woken up 10 ticks later. \(Because the semaphore is still held by **ExampleSemTask2**, **ExampleSemTask1** is suspended.\)
4. After 20 ticks, **ExampleSemTask2** is woken up and releases the semaphore. **ExampleSemTask1** acquires the semaphore and is scheduled to run. When **ExampleSemTask1** is complete, it releases the semaphore.
5. Task **ExampleSem** is woken up after 400 ticks and deletes the semaphore.
1. Create a semaphore for the **ExampleSem** task, and lock task scheduling. Create two tasks **ExampleSemTask1** and **ExampleSemTask2**. The priority of **ExampleSemTask2** is higher than that of **ExampleSemTask1**. **ExampleSemTask1** and **ExampleSemTask2** apply for the same semaphore. Make **ExampleSemTask2** sleep for 20 ticks after acquiring the semaphore. Make **ExampleSemTask1** to apply for the semaphore with a timeout period of 10 ticks. If **ExampleSemTask1** still fails to acquire the semaphore after 10 ticks, **ExampleSemTask1** will wait until a semaphore is acquired. Unlock task scheduling. **ExampleSemTask1** and **ExampleSemTask2** are blocked and attempt to acquire a semaphore. The **ExampleSem** task releases the semaphore.
2.**ExampleSemTask2** (which has the higher priority) acquires the semaphore and starts to sleep for 20 ticks. In this case, **ExampleSemTask1** is woken up.
3.**ExampleSemTask1** requests a semaphore with a timeout period of 10 ticks. During this period, the semaphore is still held by **ExampleSemTask2**, therefore, **ExampleSemTask1** is suspended. After 10 ticks, **ExampleSemTask1** is woken up and waits permanently to acquire a semaphore. **ExampleSemTask1** is suspended.
4. After 20 ticks, **ExampleSemTask2** is woken up and releases the semaphore. **ExampleSemTask1** acquires the semaphore and is scheduled to run. When **ExampleSemTask1** is complete, it releases the semaphore.
5. After 400 ticks, **ExampleSem** is woken up and deletes the semaphore.
### Sample Code
The sample code is as follows:
```
#include "los_sem.h"
#include "securec.h"
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleSem** in **TestTaskEntry**.
/* Task ID*/
static UINT32 g_testTaskId01;
static UINT32 g_testTaskId02;
/* Task priority */
#define TASK_PRIO_TEST 5
```
#include "los_sem.h"
/* Semaphore structure ID */
static UINT32 g_semId;
...
...
@@ -136,19 +115,17 @@ VOID ExampleSemTask1(VOID)
UINT32 ret;
printf("ExampleSemTask1 try get sem g_semId, timeout 10 ticks.\n");
/* Request the semaphore in scheduled block mode, with a wait timeout period of 10 ticks. */
ret = LOS_SemPend(g_semId, 10);
/* The semaphore is acquired. */
if (ret == LOS_OK) {
LOS_SemPost(g_semId);
return;
}
/* The semaphore is not acquired when the timeout period has expired. */
if (ret == LOS_ERRNO_SEM_TIMEOUT) {
printf("ExampleSemTask1 timeout and try get sem g_semId wait forever.\n");
/* Request the semaphore in permanent block mode. */
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
printf("ExampleSemTask1 wait_forever and get sem g_semId.\n");
...
...
@@ -166,15 +143,14 @@ VOID ExampleSemTask2(VOID)
/* Request the semaphore in permanent block mode. */
ret = LOS_SemPend(g_semId, LOS_WAIT_FOREVER);
if (ret == LOS_OK) {
printf("ExampleSemTask2 get sem g_semId and then delay 20 ticks.\n");
}
/* Enable the task to enter sleep mode for 20 ticks. */
/* Enable the task to enter sleep mode for 20 ticks. */
A doubly linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains a pointer to the previous node and a pointer to the next node in the sequence of nodes. The pointer head is unique.
A doubly linked list allows access from a list node to its next node and also the previous node on the list. This data structure facilitates data search, especially traversal of a large amount of data. The symmetry of the doubly linked list also makes operations, such as insertion and deletion, easy. However, pay attention to the pointer direction when performing operations.
## Available APIs<a name="section848334511411"></a>
The doubly linked list module provides the following APIs. For more details about the APIs, see the API reference.
<trid="row20895152284710"><tdclass="cellrowborder"rowspan="2"valign="top"width="8.14%"><pid="p18951922144714"><aname="p18951922144714"></a><aname="p18951922144714"></a>Initializing a linked list</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p38951422144710"><aname="p38951422144710"></a><aname="p38951422144710"></a>Initializes a specified doubly linked list node as a doubly linked list.</p>
<tdclass="cellrowborder"valign="top"><pid="p3895222184710"><aname="p3895222184710"></a><aname="p3895222184710"></a>Defines a doubly linked list node and initializes the node as a doubly linked list.</p>
</td>
</tr>
<trid="row1689522210476"><tdclass="cellrowborder"rowspan="2"valign="top"width="8.14%"><pid="p12896132294720"><aname="p12896132294720"></a><aname="p12896132294720"></a>Adding a node</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p9896122134715"><aname="p9896122134715"></a><aname="p9896122134715"></a>Inserts the specified node to the head of a doubly linked list.</p>
<tdclass="cellrowborder"valign="top"><pid="p188961322144712"><aname="p188961322144712"></a><aname="p188961322144712"></a>Inserts the specified node to the end of a doubly linked list.</p>
</td>
</tr>
<trid="row5896112264710"><tdclass="cellrowborder"rowspan="2"valign="top"width="8.14%"><pid="p28961122174717"><aname="p28961122174717"></a><aname="p28961122174717"></a>Deleting a node</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p289610224473"><aname="p289610224473"></a><aname="p289610224473"></a>Deletes the specified node from a doubly linked list.</p>
<tdclass="cellrowborder"valign="top"><pid="p48965226475"><aname="p48965226475"></a><aname="p48965226475"></a>Deletes the specified node from the linked list and uses the node to initialize the linked list.</p>
</td>
</tr>
<trid="row1689602294714"><tdclass="cellrowborder"valign="top"width="8.14%"><pid="p18961222164716"><aname="p18961222164716"></a><aname="p18961222164716"></a>Checking whether a doubly linked list is empty</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p16896172254720"><aname="p16896172254720"></a><aname="p16896172254720"></a>Checks whether a linked list is empty.</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p08971022144720"><aname="p08971022144720"></a><aname="p08971022144720"></a>Obtains the address of the structure that contains the linked list. The first input parameter of the API indicates a node in the list, the second input parameter indicates the name of the structure to be obtained, and the third input parameter indicates the name of the linked list in the structure.</p>
<tdclass="cellrowborder"valign="top"><pid="p1897822194713"><aname="p1897822194713"></a><aname="p1897822194713"></a>Obtains the offset of a member in a specified structure relative to the start address of the structure.</p>
</td>
</tr>
<trid="row4897192254715"><tdclass="cellrowborder"rowspan="2"valign="top"width="8.14%"><pid="p3897922164714"><aname="p3897922164714"></a><aname="p3897922164714"></a>Traversing a doubly linked list</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p28971222194714"><aname="p28971222194714"></a><aname="p28971222194714"></a>Traverses a doubly linked list.</p>
<tdclass="cellrowborder"valign="top"><pid="p148975222479"><aname="p148975222479"></a><aname="p148975222479"></a>Traverses a doubly linked list, and stores the next node of the current node for security verification.</p>
</td>
</tr>
<trid="row208971622174718"><tdclass="cellrowborder"rowspan="2"valign="top"width="8.14%"><pid="p168977224474"><aname="p168977224474"></a><aname="p168977224474"></a>Traversing the structure that contains the doubly linked list</p>
<tdclass="cellrowborder"valign="top"width="59.77%"><pid="p10897522194717"><aname="p10897522194717"></a><aname="p10897522194717"></a>Traverses the specified doubly linked list and obtains the address of the structure that contains the linked list node.</p>
<tdclass="cellrowborder"valign="top"><pid="p13898102220475"><aname="p13898102220475"></a><aname="p13898102220475"></a>Traverses the specified doubly linked list, obtains the structure address of the node that contains the linked list, and stores the structure address that contains the next node of the current node.</p>
</td>
</tr>
</tbody>
</table>
A doubly linked list (DLL) is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains a pointer to the previous node and a pointer to the next node in the sequence of nodes. The pointer head is unique.
A DLL allows access from a list node to its next node and also the previous node on the list. This data structure facilitates data search, especially traversal of a large amount of data. The symmetry of the DLL also makes operations, such as insertion and deletion, easy. However, pay attention to the pointer direction when performing operations.
## **Function Description**
The table below describes APIs available for the DLL. For more details about the APIs, see the API reference.
| Initializing and deleting a DLL | **LOS_ListInit**: initializes a DLL node as a DLL.<br>**LOS_DL_LIST_HEAD**: Defines a DLL node and initializes the node as a DLL.<br>**LOS_ListDelInit**: deletes a DLL.|
| Adding a node | **LOS_ListAdd**: adds a node to the head of a DLL.<br>**LOS_ListTailInsert**: inserts a node to the tail of a DLL.|
| Deleting a node | **LOS_ListDelete**: deletes a node from this DLL.<br>**LOS_ListDelInit**: deletes a node from this DLL and uses this node to initialize the DLL.|
| Checking a DLL | **LOS_ListEmpty**: checks whether a DLL is empty. |
| Obtaining structure information | **LOS_DL_LIST_ENTRY**: obtains the address of the structure that contains the DLL. The first input parameter of the API indicates a node in the list, the second input parameter indicates the name of the structure to be obtained, and the third input parameter indicates the name of the DLL in the structure.<br>**LOS_OFF_SET_OF**: obtains the offset of a member in the specified structure relative to the start address of the structure.|
| Traversing a DLL | **LOS_DL_LIST_FOR_EACH**: traverses a DLL.<br>**LOS_DL_LIST_FOR_EACH_SAFE**: traverses the DLL and stores the subsequent nodes of the current node for security verification.|
| Traversing the structure that contains a DLL| - **LOS_DL_LIST_FOR_EACH_ENTRY**: traverses a DLL and obtains the address of the structure that contains the linked list node.<br>**LOS_DL_LIST_FOR_EACH_ENTRY_SAFE**: traverses a DLL, obtains the address of the structure that contains the linked list node, and stores the address of the structure that contains the subsequent node of the current node.|
## How to Develop
The typical development process of the doubly linked list is as follows:
The typical development process of the DLL is as follows:
1. Call **LOS_ListInit** or **LOS_DL_LIST_HEAD** to initialize a DLL.
2. Call **LOS_ListAdd** to add a node into the DLL.
3. Call **LOS_ListTailInsert** to insert a node into the tail of the DLL.
4. Call **LOS_ListDelete** to delete the specified node.
5. Call **LOS_ListEmpty** to check whether the DLL is empty.
6. Call **LOS_ListDelInit** to delete the specified node and initialize the DLL based on the node.
1. Call **LOS\_ListInit/LOS\_DL\_LIST\_HEAD** to initialize a doubly linked list.
2. Call **LOS\_ListAdd** to insert a node to the list.
3. Call **LOS\_ListTailInsert** to insert a node to the end of the list.
4. Call **LOS\_ListDelete** to delete the specified node.
5. Call **LOS\_ListEmpty** to check whether a linked list is empty.
6. Call **LOS\_ListDelInit** to delete a specified node, and initialize the linked list based on this node.
>- Pay attention to the operations of the front and back pointer of the node.
>- The linked list operation APIs are underlying APIs and do not check whether the input parameters are empty. You must ensure that the input parameters are valid.
>- If the memory of a linked list node is dynamically requested, release the memory after deleting the node.
> **NOTE**
>
> - Pay attention to the operations operations of the front and back pointer of the node.
>
> - The DLL APIs are underlying interfaces and do not check whether the input parameters are empty. You must ensure that the input parameters are valid.
>
> - If the memory of a linked list node is dynamically allocated, release the memory when deleting the node.
## Development Example<a name="section67569495514"></a>
### Example Description<a name="section48761994551"></a>
## Development Example
### Example Description
This example implements the following:
1. Initialize a doubly linked list.
2. Add nodes.
3. Delete a node.
4. Check whether the operation is performed successfully.
1. Initialize the DLL.
2. Add nodes.
3. Delete nodes.
4. Check the operation result.
### Sample Code
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleList** in **TestTaskEntry**.
```
#include "stdio.h"
#include "los_list.h"
static UINT32 ListSample(VOID)
STATIC UINT32 ExampleList(VOID)
{
LOS_DL_LIST listHead = {NULL,NULL};
LOS_DL_LIST listNode1 = {NULL,NULL};
LOS_DL_LIST listNode2 = {NULL,NULL};
// Initialize the linked list.
/* Initialize a DLL. */
printf("Initial head\n");
LOS_ListInit(&listHead);
// Add node 1 and node 2 and verify their relationship.
/* Add node 1 and node 2 and verify their relationship. */
LOS_ListAdd(&listHead, &listNode1);
if (listNode1.pstNext == &listHead && listNode1.pstPrev == &listHead) {
@@ -32,20 +32,18 @@ The software timer counts time in ticks. When a software timer is created and st
When a tick interrupt occurs, the tick interrupt handler scans the global timing list for expired timers. If such timers are found, the timers are recorded.
When the tick interrupt handling function is complete, the software timer task (with the highest priority) is woken up. In this task, the timeout callback function for the recorded timer is called.
When the tick interrupt handler is complete, the software timer task (with the highest priority) will be woken up. In this task, the timeout callback for the recorded timer is called.
### Timer States
- OS_SWTMR_STATUS_UNUSED
The timer is not in use. When the timer module is initialized, all timer resources in the system are set to this state.
- OS_SWTMR_STATUS_CREATED
The timer is created but not started or the timer is stopped. When **LOS_SwtmrCreate** is called for a timer that is not in use or **LOS_SwtmrStop** is called for a newly started timer, the timer changes to this state.
- OS_SWTMR_STATUS_TICKING
The timer is running (counting). When **LOS_SwtmrStart** is called for a newly created timer, the timer enters this state.
...
...
@@ -64,15 +62,13 @@ The OpenHarmony LiteOS-M kernel provides the following types of software timers:
The following table describes APIs available for the OpenHarmony LiteOS-M software timer module. For more details about the APIs, see the API reference.
**Table 1** Software timer APIs
**Table 1** Software timer APIs
| API| Description|
| Category| Description|
| -------- | -------- |
| LOS_SwtmrCreate| Creates a timer.|
| LOS_SwtmrDelete| Deletes a timer.|
| LOS_SwtmrStart| Starts a timer.|
| LOS_SwtmrStop| Stops a timer.|
| LOS_SwtmrTimeGet| Obtains the remaining ticks of a software timer.|
| Creating or deleting a timer| **LOS_SwtmrCreate**: creates a timer.<br>**LOS_SwtmrDelete**: deletes a timer.|
| Starting or stopping a timer| **LOS_SwtmrStart**: starts a timer.<br>**LOS_SwtmrStop**: Stops a timer.|
| Obtaining remaining ticks of a software timer| **LOS_SwtmrTimeGet**: obtains the remaining ticks of a software timer.|
## How to Develop
...
...
@@ -96,14 +92,14 @@ The typical development process of software timers is as follows:
6. Call **LOS_SwtmrDelete** to delete the software timer.
> - Avoid too many operations in the callback function of the software timer. Do not use APIs or perform operations that may cause task suspension or blocking.
> **NOTE**
> - Avoid too many operations in the callback of the software timer. Do not use APIs or perform operations that may cause task suspension or blocking.
>
> - The software timers use a queue and a task resource of the system. The priority of the software timer tasks is set to **0** and cannot be changed.
>
> - The number of software timer resources that can be configured in the system is the total number of software timer resources available to the entire system, not the number of software timer resources available to users. For example, if the system software timer occupies one more resource, the number of software timer resources available to users decreases by one.
>
> - If a one-shot software timer is created, the system automatically deletes the timer and reclaims resources after the timer times out and the callback function is executed.
> - If a one-shot software timer is created, the system automatically deletes the timer and reclaims resources after the timer times out and the callback is invoked.
>
> - For a one-shot software timer that will not be automatically deleted after expiration, you need to call **LOS_SwtmrDelete** to delete it and reclaim the timer resource to prevent resource leakage.
...
...
@@ -122,7 +118,7 @@ The following programming example demonstrates how to:
### Sample Code
Prerequisites
**Prerequisites**
- In **los_config.h**, **LOSCFG_BASE_CORE_SWTMR** is enabled.
...
...
@@ -134,86 +130,107 @@ Prerequisites
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleSwtmr** in **TestTaskEntry**.
```
#include "los_swtmr.h"
/* Timer count */
UINT32 g_timerCount1 = 0;
UINT32 g_timerCount2 = 0;
/* Timer interval. */
#define SWTMR_INTERVAL_LONG 1000
#define SWTMR_INTERVAL_SHORT 100
/* Task ID*/
UINT32 g_testTaskId01;
/* Number of times that the timers are triggered. */
UINT32 g_timerCount1 = 0;
UINT32 g_timerCount2 = 0;
void Timer1_Callback(UINT32 arg) //Callback 1
/* Callback 1, for the one-shot software timer. */
void Timer1Callback(UINT32 arg)
{
UINT32 tick_last1;
g_timerCount1++;
tick_last1 = (UINT32)LOS_TickCountGet(); // Obtain the current number of ticks.
From the perspective of the operating system, tasks are the minimum running units that compete for system resources. They can use or wait for CPUs, use system resources such as memory, and run independently.
The task module of the OpenHarmony LiteOS-M provides multiple tasks and supports switching between tasks, helping users manage business process procedures. The task module has the following features:
The task module of the OpenHarmony LiteOS-M supports switching between tasks to help users manage business process procedures. The task module has the following features:
- Multiple tasks are supported.
...
...
@@ -36,9 +36,11 @@ A task can be in any of the following states:
A system may have multiple tasks at the same time. Therefore, tasks in the Ready state and Blocked state are added to the **Ready** queue and **Blocked** queue respectively. A queue is a collection of tasks in the same state. The sequence of adding tasks to a queue is irrelevant to the sequence of task status transition. There is only one task running at a time. Therefore, there is no queue for the running task.
The task state transition process is as follows:
...
...
@@ -48,12 +50,12 @@ The task state transition process is as follows:
- Running → Blocked
When a running task is blocked (suspended, delayed, or reading semaphores), it will be inserted to the blocked task queue and changes from the Running state to the Blocked state. Then, task switching is triggered to run the task with the highest priority in the Ready queue.
- Blocked → Ready (Blocked → Running)
- Blocked -> Ready (Prerequisites for Blocked -> Running)
When a blocked task is recovered (for example, the task is resumed, the delay period or semaphore read period times out, or the task successfully reads a semaphore), the task will be added to the Ready queue and change from the Blocked state to the Ready state. If the priority of the recovered task is higher than that of the running task, task switching will be triggered to run the recovered task. Then, the task changes from the Ready state to the Running state.
- Ready → Blocked
When a task in the Ready state is blocked (suspended), the task changes to the Blocked state and is deleted from the Ready queue. The blocked task will not be scheduled until it is recovered.
When a task in the Ready state is blocked (suspended), the task changes to the Blocked state and is removed from the Ready queue. The blocked task will not be scheduled until it is recovered.
- Running → Ready
When a task with a higher priority is created or recovered, tasks will be scheduled. The task with the highest priority in the Ready queue changes to the Running state. The originally running task changes to the Ready state and remains in the Ready queue.
...
...
@@ -66,7 +68,7 @@ The task state transition process is as follows:
**Task ID**
You will receive a task ID after successfully creating a task. The task IDs are unique in the operating system. You can suspend, restore, or query tasks by task ID.
A task ID is returned when a task is created. The task ID uniquely identifies a task in the system. You can suspend, restore, or query tasks by task ID.
**Task Priority**
...
...
@@ -84,7 +86,7 @@ An independent memory space for each task. The stack stores information such as
Resources, such as registers, used during the running of a task. When a task is suspended, other running tasks might modify the register values of the suspended task. If the original task context is not saved when task switching occurs, an unknown error may occur when the task is recovered. The context information of switched-out tasks is saved into their own task stacks so that the context information can be resumed along with tasks and the system can start from the interrupted code after the tasks are resumed.
**Task Control Block**
**TCB**
Each task has a task control block (TCB). A TCB contains task information, such as context stack pointer, state, priority, ID, name, and stack size. The TCB reflects the running status of a task.
...
...
@@ -95,24 +97,23 @@ Task switching involves actions, such as obtaining the task with the highest pri
### Task Running Mechanism
When a task is created, the system initializes the task stack and presets the context. The system places the task entry function in the corresponding position so that the function is executed when the task enters the running state for the first time.
When a task is created, the system initializes the task stack and presets the context. The system places the task entry function in the corresponding position so that the function can be executed when the task enters the running state for the first time.
## Available APIs
The following table describes APIs available for the OpenHarmony LiteOS-M task module. For more details about the APIs, see the API reference.
**Table 1** APIs of the task management module
**Table 1** APIs of the task management module
| Category| Description|
| -------- | -------- |
| Creating or deleting a task| **LOS_TaskCreateOnly**: creates a task and places the task in the Ready state. If there is no task with a higher priority in the Ready queue, the task will be executed.<br>**LOS_TaskCreate**: creates a task and places the task in the Ready state. If there is no task with a higher priority in the Ready queue, the task will be executed.<br>**LOS_TaskDelete**: deletes a task.|
| Controlling task status| **LOS_TaskResume**: resumes a suspended task to place the task in the Ready state.<br>**LOS_TaskSuspend**: suspends the specified task and performs task switching.<br>**LOS_TaskJoin**: suspends this task till the specified task is complete and the task control block resources are reclaimed.<br>**LOS_TaskDelay**: makes a task wait for a period of time (in ticks) and releases CPU resources. When the delay timer expires, the task enters the Ready state again. The input parameter is the number of ticks.<br>**LOS_Msleep**: converts the input parameter number of milliseconds into number of ticks, and use the result to call **LOS_TaskDelay**.<br>**LOS_TaskYield**: sets the time slice of the current task to **0** to release CPU resources and schedule the task with the highest priority in the Ready queue to run.|
| Creating or deleting a task| **LOS_TaskCreateOnly**: creates a task and places the task in the Blocked state.<br>**LOS_TaskCreate**: creates a task and places the task in the Ready state. If there is no task with a higher priority in the Ready queue, the task will be executed.<br>**LOS_TaskDelete**: deletes a task.|
| Controlling task status| **LOS_TaskResume**: resumes a suspended task to place the task in the Ready state.<br>**LOS_TaskSuspend**: suspends the specified task and performs task switching.<br>**LOS_TaskJoin**: suspends this task till the specified task is complete and the task control block resources are reclaimed.<br>**LOS_TaskDelay**: makes a task wait for a period of time (in ticks) and releases CPU resources. When the delay timer expires, the task enters the Ready state again. The input parameter is the number of ticks.<br>**LOS_Msleep**: makes a task wait for a period of time and releases CPU resources. When the delay timer expires, the task enters the Ready state again. The input parameter is the number of milliseconds.<br>**LOS_TaskYield**: sets the time slice of the current task to **0** to release CPU resources and schedule the task with the highest priority in the Ready queue to run.|
| Controlling task scheduling| **LOS_TaskLock**: locks task scheduling. However, tasks can still be interrupted.<br>**LOS_TaskUnlock**: unlocks task scheduling.<br>**LOS_Schedule**: triggers task scheduling.|
| Controlling task priority| **LOS_CurTaskPriSet**: sets the priority for the current task.<br>**LOS_TaskPriSet**: sets the priority for a specified task.<br>**LOS_TaskPriGet**: obtains the priority of a specified task.|
| Obtaining Job information| **LOS_CurTaskIDGet**: obtains the ID of the current task.<br>**LOS_NextTaskIDGet**: obtains the ID of the task with the highest priority in the Ready queue.<br>**LOS_NewTaskIDGet**: equivalent to **LOS_NextTaskIDGet**.<br>**LOS_CurTaskNameGet**: obtains the name of the current task.<br>**LOS_TaskNameGet**: obtains the name of a task.<br>**LOS_TaskStatusGet**: obtains the state of a task.<br>**LOS_TaskInfoGet**: obtains information about a specified task, including the task state, priority, stack size, stack pointer (SP), task entry function, and used stack space.<br>**LOS_TaskIsRunning**: checks whether the task module has started scheduling.|
| Updating task information| **LOS_TaskSwitchInfoGet**: obtains task switching information. The macro **LOSCFG_BASE_CORE_EXC_TSK_SWITCH** must be enabled.|
| Updating task information| **LOS_TaskSwitchInfoGet**: obtains the task switching information. The macro **LOSCFG_BASE_CORE_EXC_TSK_SWITCH** must be enabled.|
## How to Develop
...
...
@@ -130,7 +131,7 @@ The typical development process of the task module is as follows:
6. Use **LOS_TaskResume** to resume the suspended task.
> - Running idle tasks reclaims the TCBs and stacks in the to-be-recycled linked list.
>
> - The task name is a pointer without memory space allocated. When setting the task name, do not assign the local variable address to the task name pointer.
...
...
@@ -162,16 +163,18 @@ The typical development process of the task module is as follows:
This example describes the priority-based task scheduling and use of task-related APIs, including creating, delaying, suspending, and resuming two tasks with different priorities, and locking/unlocking task scheduling.
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleTask** in **TestTaskEntry**.
```
#include "los_task.h"
UINT32 g_taskHiId;
UINT32 g_taskLoId;
#define TSK_PRIOR_HI 4
#define TSK_PRIOR_LO 5
#define TSK_PRIOR_HI 3 /* Priority of a high-priority task. */
#define TSK_PRIOR_LO 4 /* Priority of a low-priority task. */
UINT32 Example_TaskHi(VOID)
UINT32 ExampleTaskHi(VOID)
{
UINT32 ret;
...
...
@@ -184,7 +187,7 @@ UINT32 Example_TaskHi(VOID)
return LOS_NOK;
}
/*After 100 ticks elapse, the task is resumed. */
/*After 100 ticks elapse, the task is resumed. */
printf("TaskHi LOS_TaskDelay Done.\n");
/* Suspend the task. */
...
...
@@ -198,7 +201,7 @@ UINT32 Example_TaskHi(VOID)
}
/* Entry function of low-priority tasks */
UINT32 Example_TaskLo(VOID)
UINT32 ExampleTaskLo(VOID)
{
UINT32 ret;
...
...
@@ -222,25 +225,26 @@ UINT32 Example_TaskLo(VOID)
return ret;
}
/* Task entry function used to create two tasks with different priorities */
UINT32 Example_TskCaseEntry(VOID)
/* Task entry function used to create two tasks with different priorities. */
UINT32 ExampleTask(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S initParam;
TSK_INIT_PARAM_S taskParam1 = { 0 };
TSK_INIT_PARAM_S taskParam2 = { 0 };
/* Lock task scheduling to prevent newly created tasks from being scheduled prior to this task due to higher priority. */
@@ -15,10 +15,9 @@ The time management module of the OpenHarmony LiteOS-M kernel provides time conv
## Time Unit
- Cycle
Cycle is the minimum time unit in the system. The cycle duration is determined by the system clock frequency, that is, the number of cycles per second.
- Tick
Tick is the basic time unit of the operating system and is determined by the number of ticks per second configured by the user.
...
...
@@ -40,9 +39,22 @@ The following table describes APIs available for OpenHarmony LiteOS-M time manag
| API| Description|
| -------- | -------- |
| LOS_SysClockGet | Obtains the system clock.|
| LOS_TickCountGet | Obtains the number of ticks since the system starts.|
| LOS_CyclePerTickGet | Obtains the number of cycles for each tick.|
| LOS_TickCountGet | Obtains the number of ticks since the system starts.|
| LOS_CyclePerTickGet | Obtains the number of cycles for each tick. |
| LOS_CurrNanosec | Obtains the current time, in nanoseconds. |
> - The time management module depends on **OS_SYS_CLOCK** and **LOSCFG_BASE_CORE_TICK_PER_SECOND**.
>
> - The number of system ticks is not counted when the interrupt feature is disabled. Therefore, the number of ticks cannot be used as the accurate time.
>
> - The configuration options are maintained in the **target_config.h** file of the development board project.
> - The preceding configuration is in the **target_config.h** file of the development board project. The default values of some configuration items are defined in the **los_config.h** file of the kernel.
## Development Example
...
...
@@ -68,14 +79,14 @@ The typical development process of time management is as follows:
The following example describes basic time management methods, including:
- Time conversion: convert milliseconds to ticks or convert ticks to milliseconds.
1. Time conversion: convert milliseconds to ticks or convert ticks to milliseconds.
- Time statistics: obtain the number of cycles per tick, number of ticks since system startup, and number of delayed ticks.
2. Time statistics: obtain the number of cycles per tick, number of ticks since system startup, and number of delayed ticks.
### Sample Code
Prerequisites
**Prerequisites**
- The default value of **LOSCFG_BASE_CORE_TICK_PER_SECOND** is **100**.
...
...
@@ -83,17 +94,22 @@ Prerequisites
Time conversion:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleTransformTime** and **ExampleGetTime** in **TestTaskEntry**.
```
VOID Example_TransformTime(VOID)
VOID ExampleTransformTime(VOID)
{
UINT32 ms;
UINT32 tick;
tick = LOS_MS2Tick(10000); // Convert 10000 ms into ticks.
dprintf("tick = %d \n", tick);
ms = LOS_Tick2MS(100); // Convert 100 ticks into ms.