提交 326948ad 编写于 作者: A Annie_wang

update docs

Signed-off-by: NAnnie_wang <annie.wangli@huawei.com>
上级 a2100ba9
# Kernel
# Kernel
- Kernel
- [Kernel Overview](kernel-overview.md)
......@@ -13,7 +13,7 @@
- [Mutex](kernel-mini-basic-ipc-mutex.md)
- [Queue](kernel-mini-basic-ipc-queue.md)
- [Semaphore](kernel-mini-basic-ipc-sem.md)
- [Time Management](kernel-basic-mini-time.md)
- [Time Management](kernel-mini-basic-time.md)
- [Software Timer](kernel-mini-basic-soft.md)
- [Doubly Linked List](kernel-mini-basic-list.md)
- Extended Components
......
# Interrupt Management
## Basic Concepts
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.
2. Trigger an interrupt.
>![](../public_sys-resources/icon-note.gif) **NOTE**<br/>
>- 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 */
STATIC VOID InterruptTest(VOID)
{
printf("in the func HwiUsrIrq \n");
LOS_HwiTrigger(HWI_NUM_TEST);
}
static UINT32 Example_Interrupt(VOID)
UINT32 ExampleInterrupt(VOID)
{
UINT32 ret;
HWI_PRIOR_T hwiPrio = 3;
HWI_PRIOR_T hwiPrio = 3; // Interrupt priority
HWI_MODE_T mode = 0;
HwiIrqParam irqParam;
(void)memset_s(&irqParam, sizeof(HwiIrqParam), 0, sizeof(HwiIrqParam));
irqParam.pDevId = 0;
HWI_ARG_T arg = 0;
/* Create an interrupt. */
ret = LOS_HwiCreate(HWI_NUM_TEST, hwiPrio, mode, (HWI_PROC_FUNC)HwiUsrIrq, &irqParam);
ret = LOS_HwiCreate(HWI_NUM_TEST, hwiPrio, mode, (HWI_PROC_FUNC)UsrIrqEntry, arg);
if(ret == LOS_OK){
printf("Hwi create success!\n");
} else {
......@@ -107,32 +130,44 @@ static UINT32 Example_Interrupt(VOID)
return LOS_NOK;
}
/* Trigger the interrupt. */
ret = LOS_HwiTrigger(HWI_NUM_TEST);
if(ret == LOS_OK){
printf("Hwi trigger success!\n");
} else {
printf("Hwi trigger failed!\n");
return LOS_NOK;
TSK_INIT_PARAM_S taskParam = { 0 };
UINT32 testTaskID;
/* Create a thread with a low priority to verify interrupt triggering.*/
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)InterruptTest;
taskParam.uwStackSize = OS_TSK_TEST_STACK_SIZE;
taskParam.pcName = "InterruptTest";
taskParam.usTaskPrio = TASK_PRIO_TEST - 1;
taskParam.uwResved = LOS_TASK_ATTR_JOINABLE;
ret = LOS_TaskCreate(&testTaskID, &taskParam);
if (LOS_OK != ret) {
PRINTF("InterruptTest task error\n");
}
/* Delete the interrupt. */
ret = LOS_HwiDelete(HWI_NUM_TEST);
/* Delay 50 ticks to release the scheduling of the current thread. */
LOS_TaskDelay(50);
/* Delete the registered interrupt. */
ret = LOS_HwiDelete(HWI_NUM_TEST, NULL);
if(ret == LOS_OK){
printf("Hwi delete success!\n");
} else {
printf("Hwi delete failed!\n");
return LOS_NOK;
}
return LOS_OK;
}
```
## Verification
The development is successful if the return result is as follows:
```
Hwi create success!
in the func UsrIrqEntry
Hwi delete success!
```
# Event
# Events
## Basic Concepts
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<a name="fig17799175324612"></a>
**Figure 1** Event working mechanism for a mini system
![](figures/event-working-mechanism-for-mini-systems.png "event-working-mechanism-for-mini-systems")
## Available APIs
<a name="table14277123518139"></a>
<table><thead align="left"><tr id="row152771935131315"><th class="cellrowborder" valign="top" width="17.77177717771777%" id="mcps1.1.4.1.1"><p id="p1127733591316"><a name="p1127733591316"></a><a name="p1127733591316"></a>Function</p>
</th>
<th class="cellrowborder" valign="top" width="22.932293229322934%" id="mcps1.1.4.1.2"><p id="p22771357138"><a name="p22771357138"></a><a name="p22771357138"></a>API</p>
</th>
<th class="cellrowborder" valign="top" width="59.2959295929593%" id="mcps1.1.4.1.3"><p id="p327714358130"><a name="p327714358130"></a><a name="p327714358130"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row1627793517136"><td class="cellrowborder" valign="top" width="17.77177717771777%" headers="mcps1.1.4.1.1 "><p id="p10525141151410"><a name="p10525141151410"></a><a name="p10525141151410"></a>Checking events</p>
</td>
<td class="cellrowborder" valign="top" width="22.932293229322934%" headers="mcps1.1.4.1.2 "><p id="p1027783551315"><a name="p1027783551315"></a><a name="p1027783551315"></a>LOS_EventPoll</p>
</td>
<td class="cellrowborder" valign="top" width="59.2959295929593%" headers="mcps1.1.4.1.3 "><p id="p1717215119159"><a name="p1717215119159"></a><a name="p1717215119159"></a>Checks whether the expected event occurs based on <strong id="b209271084844433"><a name="b209271084844433"></a><a name="b209271084844433"></a>eventID</strong>, <strong id="b121646215244433"><a name="b121646215244433"></a><a name="b121646215244433"></a>eventMask</strong>, and <strong id="b192170443144433"><a name="b192170443144433"></a><a name="b192170443144433"></a>mode</strong>.</p>
<div class="notice" id="note29631113132915"><a name="note29631113132915"></a><a name="note29631113132915"></a><span class="noticetitle"> NOTICE: </span><div class="noticebody"><p id="p886616817302"><a name="p886616817302"></a><a name="p886616817302"></a>If <strong id="b50092866644433"><a name="b50092866644433"></a><a name="b50092866644433"></a>mode</strong> contains <strong id="b195286359344433"><a name="b195286359344433"></a><a name="b195286359344433"></a>LOS_WAITMODE_CLR</strong> and the expected event occurs, the event that meets the requirements in <strong id="b33985475544433"><a name="b33985475544433"></a><a name="b33985475544433"></a>eventID</strong> will be cleared. In this case, <strong id="b637217044433"><a name="b637217044433"></a><a name="b637217044433"></a>eventID</strong> is an input parameter and an output parameter. In other cases, <strong id="b4992196844433"><a name="b4992196844433"></a><a name="b4992196844433"></a>eventID</strong> is used only as an input parameter.</p>
</div></div>
</td>
</tr>
<tr id="row20278035131316"><td class="cellrowborder" valign="top" width="17.77177717771777%" headers="mcps1.1.4.1.1 "><p id="p135816209511"><a name="p135816209511"></a><a name="p135816209511"></a>Initializing events</p>
</td>
<td class="cellrowborder" valign="top" width="22.932293229322934%" headers="mcps1.1.4.1.2 "><p id="p5361103903417"><a name="p5361103903417"></a><a name="p5361103903417"></a>LOS_EventInit</p>
</td>
<td class="cellrowborder" valign="top" width="59.2959295929593%" headers="mcps1.1.4.1.3 "><p id="p1936143993419"><a name="p1936143993419"></a><a name="p1936143993419"></a>Initializes an event control block.</p>
</td>
</tr>
<tr id="row1736713145208"><td class="cellrowborder" valign="top" width="17.77177717771777%" headers="mcps1.1.4.1.1 "><p id="p65802020512"><a name="p65802020512"></a><a name="p65802020512"></a>Reading events</p>
</td>
<td class="cellrowborder" valign="top" width="22.932293229322934%" headers="mcps1.1.4.1.2 "><p id="p1436015394341"><a name="p1436015394341"></a><a name="p1436015394341"></a>LOS_EventRead</p>
</td>
<td class="cellrowborder" valign="top" width="59.2959295929593%" headers="mcps1.1.4.1.3 "><p id="p1935911398345"><a name="p1935911398345"></a><a name="p1935911398345"></a>Reads an event (wait event). The task is blocked to wait based on the timeout period (in ticks).</p>
<p id="p624360131813"><a name="p624360131813"></a><a name="p624360131813"></a>If no event is read, <strong id="b129351022744433"><a name="b129351022744433"></a><a name="b129351022744433"></a>0</strong> is returned.</p>
<p id="p825491321911"><a name="p825491321911"></a><a name="p825491321911"></a>If an event is successfully read, a positive value (event set) is returned.</p>
<p id="p262373895217"><a name="p262373895217"></a><a name="p262373895217"></a>In other cases, a specific error code is returned.</p>
</td>
</tr>
<tr id="row19475718122016"><td class="cellrowborder" valign="top" width="17.77177717771777%" headers="mcps1.1.4.1.1 "><p id="p18580201754"><a name="p18580201754"></a><a name="p18580201754"></a>Writing events</p>
</td>
<td class="cellrowborder" valign="top" width="22.932293229322934%" headers="mcps1.1.4.1.2 "><p id="p1135843933412"><a name="p1135843933412"></a><a name="p1135843933412"></a>LOS_EventWrite</p>
</td>
<td class="cellrowborder" valign="top" width="59.2959295929593%" headers="mcps1.1.4.1.3 "><p id="p526932914325"><a name="p526932914325"></a><a name="p526932914325"></a>Writes a specific event to the event control block.</p>
</td>
</tr>
<tr id="row913918371962"><td class="cellrowborder" valign="top" width="17.77177717771777%" headers="mcps1.1.4.1.1 "><p id="p13581201655"><a name="p13581201655"></a><a name="p13581201655"></a>Clearing events</p>
</td>
<td class="cellrowborder" valign="top" width="22.932293229322934%" headers="mcps1.1.4.1.2 "><p id="p12140137165"><a name="p12140137165"></a><a name="p12140137165"></a>LOS_EventClear</p>
</td>
<td class="cellrowborder" valign="top" width="59.2959295929593%" headers="mcps1.1.4.1.3 "><p id="p19140637968"><a name="p19140637968"></a><a name="p19140637968"></a>Clears an event in the event control block based on the event mask.</p>
</td>
</tr>
<tr id="row1173017715"><td class="cellrowborder" valign="top" width="17.77177717771777%" headers="mcps1.1.4.1.1 "><p id="p1458102010519"><a name="p1458102010519"></a><a name="p1458102010519"></a>Destroying events</p>
</td>
<td class="cellrowborder" valign="top" width="22.932293229322934%" headers="mcps1.1.4.1.2 "><p id="p31740171"><a name="p31740171"></a><a name="p31740171"></a>LOS_EventDestroy</p>
</td>
<td class="cellrowborder" valign="top" width="59.2959295929593%" headers="mcps1.1.4.1.3 "><p id="p17171501971"><a name="p17171501971"></a><a name="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.
>![](../public_sys-resources/icon-note.gif) **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.
## 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. */
printf("Example_Event wait event 0x%x \n", EVENT_WAIT);
event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, 100);
event = LOS_EventRead(&g_exampleEvent, EVENT_WAIT, LOS_WAITMODE_AND, EVENT_TIMEOUT);
if (event == EVENT_WAIT) {
printf("Example_Event,read event :0x%x\n", event);
printf("Example_Event, read event :0x%x\n", event);
} else {
printf("Example_Event,read event timeout\n");
printf("Example_Event, read event timeout\n");
}
}
UINT32 Example_TaskEntry(VOID)
UINT32 ExampleEvent(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
UINT32 taskId;
TSK_INIT_PARAM_S taskParam = { 0 };
/* Initialize the event. */
/* Initialize the event control block. */
ret = LOS_EventInit(&g_exampleEvent);
if (ret != LOS_OK) {
printf("init event failed .\n");
return -1;
return LOS_NOK;
}
/* Create a task. */
(VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_Event;
task1.pcName = "EventTsk1";
task1.uwStackSize = OS_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId, &task1);
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)EventReadTask;
taskParam.pcName = "EventReadTask";
taskParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam.usTaskPrio = 3;
ret = LOS_TaskCreate(&taskId, &taskParam);
if (ret != LOS_OK) {
printf("task create failed.\n");
return LOS_NOK;
}
/* Write the task wait event (g_testTaskId). */
/* Write an event. */
printf("Example_TaskEntry write event.\n");
ret = LOS_EventWrite(&g_exampleEvent, EVENT_WAIT);
......@@ -206,10 +182,10 @@ UINT32 Example_TaskEntry(VOID)
LOS_EventClear(&g_exampleEvent, ~g_exampleEvent.uwEventID);
printf("EventMask:%d\n", g_exampleEvent.uwEventID);
/* Delete the task. */
ret = LOS_TaskDelete(g_testTaskId);
/* Delete the event. */
ret = LOS_EventDestroy(&g_exampleEvent);
if (ret != LOS_OK) {
printf("task delete failed.\n");
printf("destory event failed .\n");
return LOS_NOK;
}
......@@ -217,15 +193,17 @@ UINT32 Example_TaskEntry(VOID)
}
```
### Verification
The development is successful if the return result is as follows:
```
Example_Event wait event 0x1
Example_Event wait event 0x1
Example_TaskEntry write event.
Example_Event,read event :0x1
Example_Event, read event :0x1
EventMask:1
EventMask:0
```
# Mutex
## Basic Concepts
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
![](figures/mutex-working-mechanism-for-mini-systems.png "mutex-working-mechanism-for-mini-systems")
## Available APIs
**Table 1** APIs of the mutex module
<a name="table37108292611"></a>
<table><thead align="left"><tr id="row8711112919610"><th class="cellrowborder" valign="top" width="33.33333333333333%" id="mcps1.2.4.1.1"><p id="p3711102912617"><a name="p3711102912617"></a><a name="p3711102912617"></a>Function</p>
</th>
<th class="cellrowborder" valign="top" width="33.33333333333333%" id="mcps1.2.4.1.2"><p id="p1671110293610"><a name="p1671110293610"></a><a name="p1671110293610"></a>API</p>
</th>
<th class="cellrowborder" valign="top" width="33.33333333333333%" id="mcps1.2.4.1.3"><p id="p87114292617"><a name="p87114292617"></a><a name="p87114292617"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row37115291166"><td class="cellrowborder" rowspan="2" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.1 "><p id="p1795312108911"><a name="p1795312108911"></a><a name="p1795312108911"></a>Creating or deleting a mutex</p>
</td>
<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.2 "><p id="p1671120293611"><a name="p1671120293611"></a><a name="p1671120293611"></a>LOS_MuxCreate</p>
</td>
<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.3 "><p id="p171112291967"><a name="p171112291967"></a><a name="p171112291967"></a>Creates a mutex.</p>
</td>
</tr>
<tr id="row17711329268"><td class="cellrowborder" valign="top" headers="mcps1.2.4.1.1 "><p id="p071114291864"><a name="p071114291864"></a><a name="p071114291864"></a>LOS_MuxDelete</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.2.4.1.2 "><p id="p137111129965"><a name="p137111129965"></a><a name="p137111129965"></a>Deletes the specified mutex.</p>
</td>
</tr>
<tr id="row5711192912616"><td class="cellrowborder" rowspan="2" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.1 "><p id="p86087143910"><a name="p86087143910"></a><a name="p86087143910"></a>Requesting or releasing a mutex</p>
</td>
<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.2 "><p id="p1171112295614"><a name="p1171112295614"></a><a name="p1171112295614"></a>LOS_MuxPend</p>
</td>
<td class="cellrowborder" valign="top" width="33.33333333333333%" headers="mcps1.2.4.1.3 "><p id="p1271110291969"><a name="p1271110291969"></a><a name="p1271110291969"></a>Requests the specified mutex.</p>
</td>
</tr>
<tr id="row1571162918615"><td class="cellrowborder" valign="top" headers="mcps1.2.4.1.1 "><p id="p57111229967"><a name="p57111229967"></a><a name="p57111229967"></a>LOS_MuxPost</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.2.4.1.2 "><p id="p107118291660"><a name="p107118291660"></a><a name="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**.
>![](../public_sys-resources/icon-note.gif) **NOTE**<br/>
>- 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. */
LOS_TaskDelay(100);
printf("task2 resumed and post the g_testMux\n");
......@@ -155,11 +137,13 @@ VOID Example_MutexTask2(VOID)
return;
}
UINT32 Example_TaskEntry(VOID)
UINT32 ExampleMutex(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2;
TSK_INIT_PARAM_S task1 = { 0 };
TSK_INIT_PARAM_S task2 = { 0 };
UINT32 taskId01;
UINT32 taskId02;
/* Create a mutex. */
LOS_MuxCreate(&g_testMux);
......@@ -168,24 +152,22 @@ UINT32 Example_TaskEntry(VOID)
LOS_TaskLock();
/* Create task 1. */
memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask1;
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleMutexTask1;
task1.pcName = "MutexTsk1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
ret = LOS_TaskCreate(&taskId01, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
return LOS_NOK;
}
/* Create task 2. */
memset(&task2, 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_MutexTask2;
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleMutexTask2;
task2.pcName = "MutexTsk2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = 4;
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
ret = LOS_TaskCreate(&taskId02, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
return LOS_NOK;
......@@ -198,9 +180,10 @@ UINT32 Example_TaskEntry(VOID)
}
```
### Verification
The development is successful if the return result is as follows:
The development is successful if the return result is as follows:
```
task2 try to get mutex, wait forever.
......@@ -211,4 +194,3 @@ task2 resumed and post the g_testMux
task1 wait forever, get mutex g_testMux.
task1 post and delete mutex g_testMux.
```
# Queue
## Basic Concepts<a name="section5747112216469"></a>
A queue, also called a message queue, is a data structure used for communication between tasks. The queue receives messages of unfixed length from tasks or interrupts, and determines whether to store the transferred messages in the queue based on different APIs.
## Basic Concepts
Tasks can read messages from a queue. When the queue has no messages, the tasks are suspended. When the queue has a new message, the suspended tasks are woken up and process the new message. Tasks can also write messages to the queue. When the queue is full, the write task is suspended. When there is an available message node in the queue, the suspended write task is woken up and writes a message.
A message queue, also called a queue, is a mechanism for communication between tasks. The queue receives messages of unfixed length from tasks or interrupts, and determines whether to put the messages in the queue based on different APIs.
Tasks can read messages from a queue. When the queue has no messages, the read task is suspended. When the queue has a new message, the suspended task is woken up to process new messages. Tasks can also write messages to the queue. When the queue is full, the write task is suspended. When there is an available message node in the queue, the suspended write task is woken up and writes a message.
You can adjust the timeout period of the read queue and write queue to adjust the block mode of the read and write APIs. If the timeout period is set to **0** for the read queue and write queue, tasks will not be suspended and the API directly returns. This is the non-block mode. If the timeout period is greater than **0**, block mode is used.
An asynchronous processing mechanism is provided to allow messages in a queue not to be processed immediately. In addition, queues can be used to buffer messages and implement asynchronous task communication. Queues have the following features:
- Messages are queued in first-in-first-out \(FIFO\) mode and can be read and written asynchronously.
- Both the read queue and write queue support the timeout mechanism.
- Each time a message is read, the message node becomes available.
- The types of messages to be sent are determined by the parties involved in communication. Messages of different lengths \(not exceeding the message node size of the queue\) are allowed.
- A task can receive messages from and send messages to any message queue.
- Multiple tasks can receive messages from and send messages to the same queue.
- When a queue is created, the required dynamic memory space is automatically allocated in the queue API.
- Messages are queued in first-in-first-out (FIFO) mode and can be read and written asynchronously.
- Both the read queue and write queue support the timeout mechanism.
- Each time a message is read, the message node becomes available.
- The types of messages to be sent are determined by the parties involved in communication. Messages of different lengths (not exceeding the message node size of the queue) are allowed.
- A task can receive messages from and send messages to any message queue.
- Multiple tasks can receive messages from and send messages to the same queue.
- The system dynamically applies for memory space required for creating normal queues.
- The space required for creating a static queue is passed in by the user. When the static queue is deleted, the space also needs to be released by the user.
## Working Principles
### Queue Control Block
During the initialization of a queue, a control block, containing the queue name and status, is allocated. The control block is released when the queue is deleted.
The data structure of the queue control block is as follows:
```
/**
* Data structure of the queue control block
*/
typedef struct
{
UINT8 *queue; /* Pointer to the memory space of queue messages */
UINT16 queueState; /* Queue status*/
UINT16 queueLen; /* Number of message nodes in a queue, that is, the queue length */
UINT16 queueSize; /* Size of a message node */
UINT16 queueID; /*Queue ID */
UINT16 queueHead; /* Position of the message head node (array subscript)*/
UINT16 queueTail; /* Position of the message tail node (array subscript) */
UINT16 readWriteableCnt[OS_READWRITE_LEN];/* The array element with subscript 0 indicates the number of readable messages in a queue.
The element with subscript 1 indicates the number of writable messages in a queue. */
LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /* A linked list of tasks waiting to read or write messages.
Subscript 0: list of tasks waiting to read messages. Subscript 1: list of tasks waiting to write messages. */
LOS_DL_LIST memList; /* A linked list of memory blocks*/
UINT8 *queue; /* Pointer to the memory space of the queue */
UINT8 *queueName /* Queue name */
UINT16 queueState; /* Queue status */
UINT16 queueLen; /* Number of message nodes in the queue, that is, the queue length */
UINT16 queueSize; /* Size of a message node */
UINT16 queueID; /* Queue ID */
UINT16 queueHead; /* Position of the message head node (array subscript) */
UINT16 queueTail; /* Position of the message tail node (array subscript) */
UINT16 readWriteableCnt[OS_READWRITE_LEN]; /* The element whose array subscript is 0 indicates the number of readable messages in the queue,
The element whose array subscript is 1 indicates the number of writable messages in the queue */
LOS_DL_LIST readWriteList[OS_READWRITE_LEN]; /* A linked list of tasks waiting to read or write messages.
Subscript 0: read the linked list. Subscript 1: write the linked list. */
LOS_DL_LIST memList; /* A linked list of memory blocks */
} LosQueueCB;
```
Each queue control block contains information about the queue status.
- **OS\_QUEUE\_UNUSED**: The queue is not in use.
- **OS\_QUEUE\_INUSED**: The queue is in use.
- **OS_QUEUE_UNUSED**: The queue is not in use.
- **OS_QUEUE_INUSED**: The queue is in use.
### Working Principles
- The queue ID is returned when a queue is created successfully.
- The queue control block contains **Head** and **Tail**, which indicate the storage status of messages in a queue. **Head** indicates the start position of occupied message nodes in the queue. **Tail** indicates the end position of the occupied message nodes and the start position of idle message nodes. When a queue is created, **Head** and **Tail** point to the start position of the queue.
### Working Principles<a name="section15384012164811"></a>
- When data is to be written to a queue, **readWriteableCnt[1]** is used to determine whether data can be written to the queue. If **readWriteableCnt[1]** is **0**, the queue is full and data cannot be written to it. Data can be written to the head node or tail node of a queue. To write data to the tail node, locate the start idle message node based on **Tail** and write data to it. If **Tail** is pointing to the tail of the queue, the rewind mode is used. To write data to the head node, locate previous node based on **Head** and write data to it. If **Head** is pointing to the start position of the queue, the rewind mode is used.
- The queue ID is returned if a queue is created successfully.
- The queue control block contains **Head** and **Tail**, which indicate the storage status of messages in a queue. **Head** indicates the start position of occupied message nodes in the queue. **Tail** indicates the end position of the occupied message nodes and the start position of idle message nodes. When a queue is created, **Head** and **Tail** point to the start position of the queue.
- When data is to be written to a queue, **readWriteableCnt\[1\]** is used to determine whether data can be written to the queue. If **readWriteableCnt\[1\]** is **0**, the queue is full and data cannot be written to it. Data can be written to the head node or tail node of a queue. To write data to the tail node, locate the start idle message node based on **Tail** and write data to it. If **Tail** is pointing to the tail of the queue, the rewind mode is used. To write data to the head node, locate previous node based on **Head** and write data to it. If **Head** is pointing to the start position of the queue, the rewind mode is used.
- When a queue is to be read, **readWriteableCnt\[0\]** is used to determine whether the queue has messages to read. Reading an idle queue \(**readWriteableCnt\[0\]** is **0**\) will cause task suspension. If the queue has messages to read, the system locates the first node to which data is written based on **Head** and read the message from the node. If **Head** is pointing to the tail of the queue, the rewind mode is used.
- When a queue is to be deleted, the system locates the queue based on the queue ID, sets the queue status to **OS\_QUEUE\_UNUSED**, sets the queue control block to the initial state, and releases the memory occupied by the queue.
- When a queue is to be read, **readWriteableCnt[0]** is used to determine whether the queue has messages to read. Reading an idle queue (**readWriteableCnt[0]** is** 0**) will cause task suspension. If the queue has messages to read, the system locates the first node to which data is written based on **Head** and read the message from the node. If **Head** is pointing to the tail of the queue, the rewind mode is used.
**Figure 1** Reading and writing data in a queue<a name="fig1343517592468"></a>
![](figures/reading-and-writing-data-in-a-queue.png "reading-and-writing-data-in-a-queue")
- When a queue is to be deleted, the system locates the queue based on the queue ID, sets the queue status to **OS_QUEUE_UNUSED**, sets the queue control block to the initial state, and releases the memory occupied by the queue.
**Figure 1** Reading and writing data in a queue
![](figures/reading-and-writing-data-in-a-queue.png "reading-and-writing-data-in-a-queue")
The preceding figure illustrates how to write data to the tail node only. Writing data to the head node is similar.
## Available APIs
<a name="table10903105695114"></a>
<table><thead align="left"><tr id="row1293645645110"><th class="cellrowborder" valign="top" width="23.56%" id="mcps1.1.4.1.1"><p id="p59361562512"><a name="p59361562512"></a><a name="p59361562512"></a>Function</p>
</th>
<th class="cellrowborder" valign="top" width="24.29%" id="mcps1.1.4.1.2"><p id="p1393665645118"><a name="p1393665645118"></a><a name="p1393665645118"></a>API</p>
</th>
<th class="cellrowborder" valign="top" width="52.15%" id="mcps1.1.4.1.3"><p id="p119363564516"><a name="p119363564516"></a><a name="p119363564516"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row1693665613516"><td class="cellrowborder" rowspan="2" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p193675615514"><a name="p193675615514"></a><a name="p193675615514"></a>Creating or deleting a message queue</p>
</td>
<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p11936115612514"><a name="p11936115612514"></a><a name="p11936115612514"></a>LOS_QueueCreate</p>
</td>
<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p1593620562517"><a name="p1593620562517"></a><a name="p1593620562517"></a>Creates a message queue. The system dynamically allocates the queue space.</p>
</td>
</tr>
<tr id="row79361156175113"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p893615567517"><a name="p893615567517"></a><a name="p893615567517"></a>LOS_QueueDelete</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p4936155695111"><a name="p4936155695111"></a><a name="p4936155695111"></a>Deletes the specified queue based on the queue ID. </p>
</td>
</tr>
<tr id="row093614566519"><td class="cellrowborder" rowspan="3" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p1593685614513"><a name="p1593685614513"></a><a name="p1593685614513"></a>Reading or writing data in a queue (without the content contained in the address)</p>
</td>
<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p6936556155118"><a name="p6936556155118"></a><a name="p6936556155118"></a>LOS_QueueRead</p>
</td>
<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p11936556155118"><a name="p11936556155118"></a><a name="p11936556155118"></a>Reads data in the head node of the specified queue. The data in the queue node is an address.</p>
</td>
</tr>
<tr id="row199369565518"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p393655620513"><a name="p393655620513"></a><a name="p393655620513"></a>LOS_QueueWrite</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p12936256175120"><a name="p12936256175120"></a><a name="p12936256175120"></a>Writes the value of the input parameter <strong id="b2093863854458"><a name="b2093863854458"></a><a name="b2093863854458"></a>bufferAddr</strong> (buffer address) to the tail node of the specified queue.</p>
</td>
</tr>
<tr id="row1293615635114"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p893625665119"><a name="p893625665119"></a><a name="p893625665119"></a>LOS_QueueWriteHead</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p193620566515"><a name="p193620566515"></a><a name="p193620566515"></a>Writes the value of the input parameter <strong id="b2553137414458"><a name="b2553137414458"></a><a name="b2553137414458"></a>bufferAddr</strong> (buffer address) to the head node of the specified queue.</p>
</td>
</tr>
<tr id="row593675635117"><td class="cellrowborder" rowspan="3" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p293675615111"><a name="p293675615111"></a><a name="p293675615111"></a>Reading or writing in a queue (with the content contained in the address)</p>
</td>
<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p14936356155113"><a name="p14936356155113"></a><a name="p14936356155113"></a>LOS_QueueReadCopy</p>
</td>
<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p11936155616510"><a name="p11936155616510"></a><a name="p11936155616510"></a>Reads data from the head node of the specified queue.</p>
</td>
</tr>
<tr id="row093619569510"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p179361256175117"><a name="p179361256175117"></a><a name="p179361256175117"></a>LOS_QueueWriteCopy</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p6936155616515"><a name="p6936155616515"></a><a name="p6936155616515"></a>Writes the data saved in the input parameter <strong id="b13905064014458"><a name="b13905064014458"></a><a name="b13905064014458"></a>bufferAddr</strong> to the tail node of the specified queue.</p>
</td>
</tr>
<tr id="row16936856185111"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p49361156195113"><a name="p49361156195113"></a><a name="p49361156195113"></a>LOS_QueueWriteHeadCopy</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p1193625675116"><a name="p1193625675116"></a><a name="p1193625675116"></a>Writes the data saved in the input parameter <strong id="b1312795384458"><a name="b1312795384458"></a><a name="b1312795384458"></a>bufferAddr</strong> to the head node of the specified queue.</p>
</td>
</tr>
<tr id="row1936756155114"><td class="cellrowborder" valign="top" width="23.56%" headers="mcps1.1.4.1.1 "><p id="p149371956105114"><a name="p149371956105114"></a><a name="p149371956105114"></a>Obtaining queue information</p>
</td>
<td class="cellrowborder" valign="top" width="24.29%" headers="mcps1.1.4.1.2 "><p id="p7937145613516"><a name="p7937145613516"></a><a name="p7937145613516"></a>LOS_QueueInfoGet</p>
</td>
<td class="cellrowborder" valign="top" width="52.15%" headers="mcps1.1.4.1.3 "><p id="p19371356175110"><a name="p19371356175110"></a><a name="p19371356175110"></a>Obtains information about the specified queue, including the queue ID, queue length, message node size, head node, tail node, number of readable nodes, number of writable nodes, tasks waiting for read operations, and tasks waiting for write operations.</p>
</td>
</tr>
</tbody>
</table>
| Category| Description|
| -------- | -------- |
| Creating or deleting a message queue| **LOS_QueueCreate**: creates a message queue. The system dynamically allocates the queue space.<br>**LOS_QueueCreateStatic**: creates a static message queue. You need to pass in the queue space.<br>**LOS_QueueDelete**: deletes a message queue. After a static message queue is deleted, you need to release the queue space.|
| Reading or writing data (address without the content) in a queue| **LOS_QueueRead**: reads data in the head node of the specified queue. The data in the queue node is an address.<br>**LOS_QueueWrite**: writes the **bufferAddr** (buffer address) to the tail node of the specified queue.<br>**LOS_QueueWriteHead**: writes the **bufferAddr** (buffer address) to the head node of the specified queue.|
| Reading or writing data (data and address) in a queue| **LOS_QueueReadCopy**: reads data from the head node of a specified queue.<br>**LOS_QueueWriteCopy**: writes the data saved in the **bufferAddr** to the tail node of the specified queue.<br>**LOS_QueueWriteHeadCopy**: writes the data saved in the **bufferAddr** to the head node of the specified queue.|
| Obtaining queue information| **LOS_QueueInfoGet**: obtains queue information, including the queue ID, queue length, message node size, head node, tail node, number of readable/writable nodes, and tasks waiting for read/write operations.|
## How to Develop
1. Call **LOS\_QueueCreate** to create a queue. The queue ID is returned when the queue is created.
2. Call **LOS\_QueueWrite** or **LOS\_QueueWriteCopy** to write messages to the queue.
3. Call **LOS\_QueueRead** or **LOS\_QueueReadCopy** to read messages from the queue.
4. Call **LOS\_QueueInfoGet** to obtain queue information.
5. Call **LOS\_QueueDelete** to delete the queue.
>![](../public_sys-resources/icon-note.gif) **NOTE**<br/>
>- The maximum number of queues supported by the system is the total number of queue resources of the system, not the number of queue resources available to users. For example, if the system software timer occupies one more queue resource, the number of queue resources available to users decreases by one.
>- The input parameters queue name and flags passed when a queue is created are reserved for future use.
>- The input parameter **timeOut** in the queue interface function is relative time.
>- **LOS\_QueueReadCopy**, **LOS\_QueueWriteCopy**, and **LOS\_QueueWriteHeadCopy** are a group of APIs that must be used together. **LOS\_QueueRead**, **LOS\_QueueWrite**, and **LOS\_QueueWriteHead** are a group of APIs that must be used together.
>- As **LOS\_QueueWrite**, **LOS\_QueueWriteHead**, and **LOS\_QueueRead** are used to manage data addresses, you must ensure that the memory directed by the pointer obtained by calling **LOS\_QueueRead** is not modified or released abnormally when the queue is being read. Otherwise, unpredictable results may occur.
>- If the input parameter **bufferSize** in **LOS\_QueueReadCopy** is less than the length of the message, the message will be truncated.
>- **LOS\_QueueWrite**, **LOS\_QueueWriteHead**, and **LOS\_QueueRead** are called to manage data addresses, which means that the actual data read or written is pointer data. Therefore, before using these APIs, ensure that the message node size is the pointer length during queue creation, to avoid waste and read failures.
1. Call **LOS_QueueCreate** to create a queue. The queue ID is returned when the queue is created.
2. Call **LOS_QueueWrite** or **LOS_QueueWriteCopy** to write data to the queue.
3. Call **LOS_QueueRead** or **LOS_QueueReadCopy** to read data from the queue.
4. Call **LOS_QueueInfoGet** to obtain queue information.
5. Call **LOS_QueueDelete** to delete a queue.
> **NOTE**
> - The maximum number of queues supported by the system is the total number of queue resources of the system, not the number of queue resources available to users. For example, if the system software timer occupies one more queue resource, the number of queue resources available to users decreases by one.
>
> - The input parameters queue name and flags passed when a queue is created are reserved for future use.
>
> - The input parameter **timeOut** in the queue interface function is relative time.
>
> - **LOS_QueueReadCopy**, **LOS_QueueWriteCopy**, and **LOS_QueueWriteHeadCopy** are a group of APIs that must be used together. **LOS_QueueRead**, **LOS_QueueWrite**, and **LOS_QueueWriteHead** are a group of APIs that must be used together.
>
> - As **LOS_QueueWrite**, **LOS_QueueWriteHead**, and **LOS_QueueRead** are used to manage data addresses, you must ensure that the memory directed by the pointer obtained by calling **LOS_QueueRead** is not modified or released abnormally when the queue is being read. Otherwise, unpredictable results may occur.
>
> - If the read length of **LOS_QueueReadCopy** is less than the actual message length, the message will be truncated.
>
> - **LOS_QueueWrite**, **LOS_QueueWriteHead**, and **LOS_QueueRead** are called to manage data addresses, which means that the actual data read or written is pointer data. Therefore, before using these APIs, ensure that the message node size is the pointer length during queue creation, to avoid waste and read failures.
## Development Example
### Example Description
Create a queue and two tasks. Enable task 1 to call the queue write API to send messages, and enable task 2 to receive messages by calling the queue read API.
Create a queue and two tasks. Enable task 1 to write data to the queue, and task 2 to read data from the queue.
1. Call **LOS_TaskCreate** to create task 1 and task 2.
2. Call **LOS_QueueCreate** to create a message queue.
3. Task 1 sends a message in **SendEntry**.
4. Task 2 receives message in **RecvEntry**.
5. Call **LOS_QueueDelete** to delete the queue.
1. Create task 1 and task 2 by calling **LOS\_TaskCreate**.
2. Create a message queue by calling **LOS\_QueueCreate**.
3. Enable messages to be sent in task 1 by calling **SendEntry**.
4. Enable messages to be received in task 2 by calling **RecvEntry**.
5. Call **LOS\_QueueDelete** to delete the queue.
### Sample Code
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleQueue** in **TestTaskEntry**.
```
#include "los_task.h"
#include "los_queue.h"
static UINT32 g_queue;
STATIC UINT32 g_queue;
#define BUFFER_LEN 50
VOID SendEntry(VOID)
......@@ -174,8 +153,8 @@ VOID SendEntry(VOID)
UINT32 len = sizeof(abuf);
ret = LOS_QueueWriteCopy(g_queue, abuf, len, 0);
if(ret != LOS_OK) {
printf("Failed to send the message, error: %x\n", ret);
if (ret != LOS_OK) {
printf("send message failure, error: %x\n", ret);
}
}
......@@ -185,70 +164,75 @@ VOID RecvEntry(VOID)
CHAR readBuf[BUFFER_LEN] = {0};
UINT32 readLen = BUFFER_LEN;
// Sleep for 1s.
/* Sleep for 1s */
usleep(1000000);
ret = LOS_QueueReadCopy(g_queue, readBuf, &readLen, 0);
if(ret != LOS_OK) {
printf("Failed to receive the message, error: %x\n", ret);
if (ret != LOS_OK) {
printf("recv message failure, error: %x\n", ret);
}
printf("recv message: %s\n", readBuf);
printf("recv message: %s.\n", readBuf);
ret = LOS_QueueDelete(g_queue);
if(ret != LOS_OK) {
printf("Failed to delete the queue, error: %x\n", ret);
if (ret != LOS_OK) {
printf("delete the queue failure, error: %x\n", ret);
}
printf("Deleted the queue successfully.\n");
printf("delete the queue success.\n");
}
UINT32 ExampleQueue(VOID)
{
printf("start queue example.\n");
UINT32 ret = 0;
UINT32 task1, task2;
TSK_INIT_PARAM_S initParam = {0};
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry;
initParam.usTaskPrio = 9;
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
initParam.pcName = "SendQueue";
UINT32 task1;
UINT32 task2;
TSK_INIT_PARAM_S taskParam1 = { 0 };
TSK_INIT_PARAM_S taskParam2 = { 0 };
LOS_TaskLock();
ret = LOS_TaskCreate(&task1, &initParam);
taskParam1.pfnTaskEntry = (TSK_ENTRY_FUNC)SendEntry;
taskParam1.usTaskPrio = 9;
taskParam1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam1.pcName = "SendQueue";
ret = LOS_TaskCreate(&task1, &taskParam1);
if(ret != LOS_OK) {
printf("Failed to create task1, error: %x\n", ret);
printf("create task1 failed, error: %x\n", ret);
return ret;
}
initParam.pcName = "RecvQueue";
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry;
initParam.usTaskPrio = 10;
ret = LOS_TaskCreate(&task2, &initParam);
taskParam2.pfnTaskEntry = (TSK_ENTRY_FUNC)RecvEntry;
taskParam2.usTaskPrio = 10;
taskParam2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam2.pcName = "RecvQueue";
ret = LOS_TaskCreate(&task2, &taskParam2);
if(ret != LOS_OK) {
printf("Failed to create task2, error: %x\n", ret);
printf("create task2 failed, error: %x\n", ret);
return ret;
}
ret = LOS_QueueCreate("queue", 5, &g_queue, 0, 50);
if(ret != LOS_OK) {
printf("Failed to create the queue, error: %x\n", ret);
printf("create queue failure, error: %x\n", ret);
}
printf("Created the queue successfully.\n");
printf("create the queue success.\n");
LOS_TaskUnlock();
return ret;
}
```
### Verification
The development is successful if the return result is as follows:
```
start queue example.
create the queue success.
recv message: test message.
delete the queue success.
```
# Semaphore
## Basic Concepts
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<a name="fig467314634214"></a>
**Figure 1** Semaphore working mechanism for the mini system
![](figures/semaphore-working-mechanism-for-mini-systems.png "semaphore-working-mechanism-for-mini-systems")
## Available APIs
<a name="table1078714915105"></a>
<table><thead align="left"><tr id="row1280518971010"><th class="cellrowborder" valign="top" width="20.1%" id="mcps1.1.4.1.1"><p id="p1380510912104"><a name="p1380510912104"></a><a name="p1380510912104"></a>Function</p>
</th>
<th class="cellrowborder" valign="top" width="20.52%" id="mcps1.1.4.1.2"><p id="p08051291106"><a name="p08051291106"></a><a name="p08051291106"></a>API</p>
</th>
<th class="cellrowborder" valign="top" width="59.38%" id="mcps1.1.4.1.3"><p id="p12805149151012"><a name="p12805149151012"></a><a name="p12805149151012"></a>Description</p>
</th>
</tr>
</thead>
<tbody><tr id="row168052913104"><td class="cellrowborder" rowspan="3" valign="top" width="20.1%" headers="mcps1.1.4.1.1 "><p id="p180618915101"><a name="p180618915101"></a><a name="p180618915101"></a>Creating or deleting a semaphore</p>
</td>
<td class="cellrowborder" valign="top" width="20.52%" headers="mcps1.1.4.1.2 "><p id="p198061196105"><a name="p198061196105"></a><a name="p198061196105"></a>LOS_SemCreate</p>
</td>
<td class="cellrowborder" valign="top" width="59.38%" headers="mcps1.1.4.1.3 "><p id="p1980609121010"><a name="p1980609121010"></a><a name="p1980609121010"></a>Creates a semaphore and returns the semaphore ID.</p>
</td>
</tr>
<tr id="row4806990105"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p280620917109"><a name="p280620917109"></a><a name="p280620917109"></a>LOS_BinarySemCreate</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p780614919107"><a name="p780614919107"></a><a name="p780614919107"></a>Creates a binary semaphore. The maximum counter value is <strong id="b19111879045016"><a name="b19111879045016"></a><a name="b19111879045016"></a>1</strong>.</p>
</td>
</tr>
<tr id="row17806159151018"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p38067931012"><a name="p38067931012"></a><a name="p38067931012"></a>LOS_SemDelete</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p168063941015"><a name="p168063941015"></a><a name="p168063941015"></a>Deletes a semaphore.</p>
</td>
</tr>
<tr id="row188061098102"><td class="cellrowborder" rowspan="2" valign="top" width="20.1%" headers="mcps1.1.4.1.1 "><p id="p5806179161014"><a name="p5806179161014"></a><a name="p5806179161014"></a>Requesting or releasing a semaphore</p>
</td>
<td class="cellrowborder" valign="top" width="20.52%" headers="mcps1.1.4.1.2 "><p id="p16806159201015"><a name="p16806159201015"></a><a name="p16806159201015"></a>LOS_SemPend</p>
</td>
<td class="cellrowborder" valign="top" width="59.38%" headers="mcps1.1.4.1.3 "><p id="p4806149191011"><a name="p4806149191011"></a><a name="p4806149191011"></a>Requests a specified semaphore and sets the timeout period.</p>
</td>
</tr>
<tr id="row4806199141019"><td class="cellrowborder" valign="top" headers="mcps1.1.4.1.1 "><p id="p280618913105"><a name="p280618913105"></a><a name="p280618913105"></a>LOS_SemPost</p>
</td>
<td class="cellrowborder" valign="top" headers="mcps1.1.4.1.2 "><p id="p178061917109"><a name="p178061917109"></a><a name="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.
>![](../public_sys-resources/icon-note.gif) **NOTE:**
>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. */
LOS_TaskDelay(20);
printf("ExampleSemTask2 post sem g_semId.\n");
/* Release the semaphore. */
LOS_SemPost(g_semId);
return;
......@@ -183,8 +159,10 @@ VOID ExampleSemTask2(VOID)
UINT32 ExampleSem(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S task2;
TSK_INIT_PARAM_S task1 = { 0 };
TSK_INIT_PARAM_S task2 = { 0 };
UINT32 taskId1;
UINT32 taskId2;
/* Create a semaphore. */
LOS_SemCreate(0, &g_semId);
......@@ -193,24 +171,22 @@ UINT32 ExampleSem(VOID)
LOS_TaskLock();
/* Create task 1. */
(VOID)memset_s(&task1, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask1;
task1.pcName = "TestTask1";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = TASK_PRIO_TEST;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&taskId1, &task1);
if (ret != LOS_OK) {
printf("task1 create failed.\n");
return LOS_NOK;
}
/* Create task 2. */
(VOID)memset_s(&task2, sizeof(TSK_INIT_PARAM_S), 0, sizeof(TSK_INIT_PARAM_S));
task2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleSemTask2;
task2.pcName = "TestTask2";
task2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task2.usTaskPrio = (TASK_PRIO_TEST - 1);
ret = LOS_TaskCreate(&g_testTaskId02, &task2);
task2.usTaskPrio = 4;
ret = LOS_TaskCreate(&taskId2, &task2);
if (ret != LOS_OK) {
printf("task2 create failed.\n");
return LOS_NOK;
......@@ -221,26 +197,27 @@ UINT32 ExampleSem(VOID)
ret = LOS_SemPost(g_semId);
/* Enable the task to enter sleep mode for 400 ticks. */
/* Enable the task to enter sleep mode for 400 ticks. */
LOS_TaskDelay(400);
/* Delete the semaphore. */
/* Delete the semaphore. */
LOS_SemDelete(g_semId);
return LOS_OK;
}
```
### Verification
The development is successful if the return result is as follows:
```
ExampleSemTask2 try get sem g_semId wait forever.
ExampleSemTask2 get sem g_semId and then delay 20 ticks.
ExampleSemTask1 try get sem g_semId, timeout 10 ticks.
ExampleSemTask2 get sem g_semId and then delay 20 ticks.
ExampleSemTask1 timeout and try get sem g_semId wait forever.
ExampleSemTask2 post sem g_semId.
ExampleSemTask1 wait_forever and get sem g_semId.
```
......@@ -3,150 +3,90 @@
## Basic Concepts
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.
<a name="table9827162254713"></a>
<table><tbody><tr id="row2089515228470"><td class="cellrowborder" valign="top" width="8.14%"><p id="p4895182214473"><a name="p4895182214473"></a><a name="p4895182214473"></a>Function</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p58951922124713"><a name="p58951922124713"></a><a name="p58951922124713"></a>API</p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p5895122134719"><a name="p5895122134719"></a><a name="p5895122134719"></a>Description</p>
</td>
</tr>
<tr id="row20895152284710"><td class="cellrowborder" rowspan="2" valign="top" width="8.14%"><p id="p18951922144714"><a name="p18951922144714"></a><a name="p18951922144714"></a>Initializing a linked list</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p789516220474"><a name="p789516220474"></a><a name="p789516220474"></a><span>LOS_ListInit</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p38951422144710"><a name="p38951422144710"></a><a name="p38951422144710"></a>Initializes a specified doubly linked list node as a doubly linked list.</p>
</td>
</tr>
<tr id="row289552216475"><td class="cellrowborder" valign="top"><p id="p11895222194718"><a name="p11895222194718"></a><a name="p11895222194718"></a><span>LOS_DL_LIST_HEAD</span></p>
</td>
<td class="cellrowborder" valign="top"><p id="p3895222184710"><a name="p3895222184710"></a><a name="p3895222184710"></a>Defines a doubly linked list node and initializes the node as a doubly linked list.</p>
</td>
</tr>
<tr id="row1689522210476"><td class="cellrowborder" rowspan="2" valign="top" width="8.14%"><p id="p12896132294720"><a name="p12896132294720"></a><a name="p12896132294720"></a>Adding a node</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p789611221477"><a name="p789611221477"></a><a name="p789611221477"></a><span>LOS_ListAdd</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p9896122134715"><a name="p9896122134715"></a><a name="p9896122134715"></a>Inserts the specified node to the head of a doubly linked list.</p>
</td>
</tr>
<tr id="row188961225475"><td class="cellrowborder" valign="top"><p id="p18961122154718"><a name="p18961122154718"></a><a name="p18961122154718"></a><span>LOS_ListTailInsert</span></p>
</td>
<td class="cellrowborder" valign="top"><p id="p188961322144712"><a name="p188961322144712"></a><a name="p188961322144712"></a>Inserts the specified node to the end of a doubly linked list.</p>
</td>
</tr>
<tr id="row5896112264710"><td class="cellrowborder" rowspan="2" valign="top" width="8.14%"><p id="p28961122174717"><a name="p28961122174717"></a><a name="p28961122174717"></a>Deleting a node</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p28961422154710"><a name="p28961422154710"></a><a name="p28961422154710"></a><span>LOS_ListDelete</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p289610224473"><a name="p289610224473"></a><a name="p289610224473"></a>Deletes the specified node from a doubly linked list.</p>
</td>
</tr>
<tr id="row3896522124711"><td class="cellrowborder" valign="top"><p id="p0896152213471"><a name="p0896152213471"></a><a name="p0896152213471"></a><span>LOS_ListDelInit</span></p>
</td>
<td class="cellrowborder" valign="top"><p id="p48965226475"><a name="p48965226475"></a><a name="p48965226475"></a>Deletes the specified node from the linked list and uses the node to initialize the linked list.</p>
</td>
</tr>
<tr id="row1689602294714"><td class="cellrowborder" valign="top" width="8.14%"><p id="p18961222164716"><a name="p18961222164716"></a><a name="p18961222164716"></a>Checking whether a doubly linked list is empty</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p78961522184715"><a name="p78961522184715"></a><a name="p78961522184715"></a><span>LOS_ListEmpty</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p16896172254720"><a name="p16896172254720"></a><a name="p16896172254720"></a>Checks whether a linked list is empty.</p>
</td>
</tr>
<tr id="row128977221474"><td class="cellrowborder" rowspan="2" valign="top" width="8.14%"><p id="p138971322174717"><a name="p138971322174717"></a><a name="p138971322174717"></a>Obtaining structure information</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p138971922194712"><a name="p138971922194712"></a><a name="p138971922194712"></a><span>LOS_DL_LIST_ENTRY</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p08971022144720"><a name="p08971022144720"></a><a name="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>
</td>
</tr>
<tr id="row17897102264718"><td class="cellrowborder" valign="top"><p id="p1889792218473"><a name="p1889792218473"></a><a name="p1889792218473"></a><span>LOS_OFF_SET_OF</span></p>
</td>
<td class="cellrowborder" valign="top"><p id="p1897822194713"><a name="p1897822194713"></a><a name="p1897822194713"></a>Obtains the offset of a member in a specified structure relative to the start address of the structure.</p>
</td>
</tr>
<tr id="row4897192254715"><td class="cellrowborder" rowspan="2" valign="top" width="8.14%"><p id="p3897922164714"><a name="p3897922164714"></a><a name="p3897922164714"></a>Traversing a doubly linked list</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p58971222194713"><a name="p58971222194713"></a><a name="p58971222194713"></a><span>LOS_DL_LIST_FOR_EACH</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p28971222194714"><a name="p28971222194714"></a><a name="p28971222194714"></a>Traverses a doubly linked list.</p>
</td>
</tr>
<tr id="row589792254710"><td class="cellrowborder" valign="top"><p id="p689792264718"><a name="p689792264718"></a><a name="p689792264718"></a><span>LOS_DL_LIST_FOR_EACH_SAFE</span></p>
</td>
<td class="cellrowborder" valign="top"><p id="p148975222479"><a name="p148975222479"></a><a name="p148975222479"></a>Traverses a doubly linked list, and stores the next node of the current node for security verification.</p>
</td>
</tr>
<tr id="row208971622174718"><td class="cellrowborder" rowspan="2" valign="top" width="8.14%"><p id="p168977224474"><a name="p168977224474"></a><a name="p168977224474"></a>Traversing the structure that contains the doubly linked list</p>
</td>
<td class="cellrowborder" valign="top" width="32.09%"><p id="p1489752216479"><a name="p1489752216479"></a><a name="p1489752216479"></a><span>LOS_DL_LIST_FOR_EACH_ENTRY</span></p>
</td>
<td class="cellrowborder" valign="top" width="59.77%"><p id="p10897522194717"><a name="p10897522194717"></a><a name="p10897522194717"></a>Traverses the specified doubly linked list and obtains the address of the structure that contains the linked list node.</p>
</td>
</tr>
<tr id="row10897622104713"><td class="cellrowborder" valign="top"><p id="p2897112215478"><a name="p2897112215478"></a><a name="p2897112215478"></a><span>LOS_DL_LIST_FOR_EACH_ENTRY_SAFE</span></p>
</td>
<td class="cellrowborder" valign="top"><p id="p13898102220475"><a name="p13898102220475"></a><a name="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.
| **Category** | **Description** |
| ------------------------ | ------------------------------------------------------------ |
| 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.
>![](../public_sys-resources/icon-note.gif) **NOTE:**
>- 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) {
printf("Add listNode1 success\n");
......@@ -157,11 +97,11 @@ static UINT32 ListSample(VOID)
printf("Tail insert listNode2 success\n");
}
// Delete the two nodes.
/* Delete the two nodes. */
LOS_ListDelete(&listNode1);
LOS_ListDelete(&listNode2);
// Check that the linked list is empty.
/* Check whether the DLL is empty. */
if (LOS_ListEmpty(&listHead)) {
printf("Delete success\n");
}
......@@ -170,14 +110,15 @@ static UINT32 ListSample(VOID)
}
```
### Verification
The development is successful if the return result is as follows:
```
Initial head
Add listNode1 success
Tail insert listNode2 success
Delete success
```
......@@ -27,8 +27,9 @@ The static memory is a static array. The block size in the static memory pool is
The static memory pool consists of a control block **LOS_MEMBOX_INFO** and several memory blocks **LOS_MEMBOX_NODE** of the same size. The control block is located at the head of the memory pool and used for memory block management. It contains the memory block size (**uwBlkSize**), number of memory blocks (**uwBlkNum**), number of allocated memory blocks (**uwBlkCnt**), and free list (**stFreeList**). Memory is allocated and released by block. Each memory block contains the pointer **pstNext** that points to the next memory block.
**Figure 1** Static memory
![](figures/static-memory.png "static-memory")
**Figure 1** Static memory
![](figures/static-memory.png "static-memory")
### Development Guidelines
......@@ -43,14 +44,14 @@ Use static memory allocation to obtain memory blocks of the fixed size. When the
The following table describes APIs available for OpenHarmony LiteOS-M static memory management. For more details about the APIs, see the API reference.
**Table 1** APIs of the static memory module
**Table 1** APIs of the static memory module
| Category| API|
| -------- | -------- |
| Initializing the static memory pool| **LOS_MemboxInit**: initializes a static memory pool, that is, sets the start address, total size, and size of each memory block based on input parameters.|
| Clearing static memory blocks| **LOS_MemboxClr**: clears the memory blocks allocated from the static memory pool.|
| Allocating or releasing static memory| - **LOS_MemboxAlloc**: allocates a memory block from a specified static memory pool.<br>- **LOS_MemboxFree**: releases a memory block allocated from the static memory pool.|
| Obtaining or printing static memory pool information| - **LOS_MemboxStatisticsGet**: obtains information about a specified static memory pool, including the total number of memory blocks in the memory pool, number of allocated memory blocks, and size of each memory block.<br>- **LOS_ShowBox**: prints information about all nodes in a specified static memory pool (the print level is **LOS_INFO_LEVEL**). The information includes the start address of the memory pool, memory block size, total number of memory blocks, start address of each idle memory block, and start addresses of all memory blocks.|
| Allocating or releasing static memory| **LOS_MemboxAlloc**: allocates a memory block from a specified static memory pool.<br>**LOS_MemboxFree**: releases a memory block allocated from the static memory pool.|
| Obtaining or printing static memory pool information| **LOS_MemboxStatisticsGet**: obtains information about a specified static memory pool, including the total number of memory blocks in the memory pool, number of allocated memory blocks, and size of each memory block.<br>**LOS_ShowBox**: prints information about all nodes in a specified static memory pool (the print level is **LOS_INFO_LEVEL**). The information includes the start address of the memory pool, memory block size, total number of memory blocks, start address of each idle memory block, and start addresses of all memory blocks.|
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
> The number of memory blocks in the memory pool after initialization is not equal to the total memory size divided by the memory block size. The reason is the control block of the memory pool and the control header of each memory block have memory overheads. When setting the total memory size, you need to consider these factors.
......@@ -91,21 +92,26 @@ This example implements the following:
6. Release the memory block.
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleStaticMem** in **TestTaskEntry**.
```
#include "los_membox.h"
VOID Example_StaticMem(VOID)
#define MEMBOX_POOL_SIZE 100
#define MEMBOX_BLOCK_SZIE 10
#define MEMBOX_WR_TEST_NUM 828
VOID ExampleStaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize = 10;
UINT32 boxSize = 100;
UINT32 boxMem[1000];
UINT32 blkSize = MEMBOX_BLOCK_SZIE;
UINT32 poolSize = MEMBOX_POOL_SIZE;
UINT32 boxMem[MEMBOX_POOL_SIZE];
UINT32 ret;
/* Initialize the memory pool. */
ret = LOS_MemboxInit(&boxMem[0], boxSize, blkSize);
ret = LOS_MemboxInit(&boxMem[0], poolSize, blkSize);
if(ret != LOS_OK) {
printf("Membox init failed!\n");
return;
......@@ -115,19 +121,19 @@ VOID Example_StaticMem(VOID)
/* Request a memory block. */
mem = (UINT32 *)LOS_MemboxAlloc(boxMem);
if (NULL == mem) {
if (mem == NULL) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/* Assign a value. */
*mem = 828;
/*Verify the read and write operations on the memory address. */
*mem = MEMBOX_WR_TEST_NUM;
printf("*mem = %d\n", *mem);
/* Clear the memory. */
LOS_MemboxClr(boxMem, mem);
printf("Mem clear success \n *mem = %d\n", *mem);
printf("Mem clear success \n*mem = %d\n", *mem);
/ Release the memory. */
ret = LOS_MemboxFree(boxMem, mem);
......@@ -139,6 +145,7 @@ VOID Example_StaticMem(VOID)
return;
}
```
......@@ -164,8 +171,9 @@ Dynamic memory management allows memory blocks of any size to be allocated from
The dynamic memory of the OpenHarmony LiteOS-M has optimized the memory space partitioning based on the Two-Level Segregate Fit (TLSF) algorithm to achieve higher performance and minimize fragmentation. The figure below shows the core algorithm of the dynamic memory.
**Figure 1** Dynamic memory algorithm for mini systems
![](figures/dynamic-memory-algorithm-for-mini-systems.png "dynamic-memory-algorithm-for-mini-systems")
**Figure 2** Dynamic memory algorithm for mini systems
![](figures/dynamic-memory-algorithm-for-mini-systems.png "dynamic-memory-algorithm-for-mini-systems")
Multiple free lists are used for management based on the size of the free memory block. The free memory blocks are divided into two parts: [4, 127] and [2<sup>7</sup>, 2<sup>31</sup>], as indicated by the size class in the above figure.
......@@ -173,28 +181,25 @@ Multiple free lists are used for management based on the size of the free memory
2. The memory greater than 127 bytes is managed in power of two increments. The size of each range is [2^n, 2^(n+1) -1], where n is an integer in [7, 30]. This range is divided into 24 parts, each of which is further divided into 8 second-level (L2) ranges, as shown in Size Class and Size SubClass in the upper part of the figure. Each L2 range corresponds to a free list and a bit that indicates whether the free list is empty. There are a total of 192 (24 x 8) L2 ranges, corresponding to 192 free lists and 192 bits.
For example, insert 40-byte free memory to a free list. The 40-byte free memory corresponds to the 10th free list in the range of [40, 43], and the 10th bit indicates the use of the free list. The system inserts the 40-byte free memory to the 10th free list and determines whether to update the bitmap flag. When 40-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list.
If 580-byte free memory needs to be inserted to a free list, the 580-byte free memory corresponds to the 47th (31 + 2 x 8) free list in L2 range [2^9, 2^9+2^6], and the 47th bit indicates the use of the free list. The system inserts the 580-byte free memory to the 47th free list and determines whether to update the bitmap flag. When 580-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If the corresponding free list is empty, the system checks for a free list meeting the requirements in a larger memory range. In actual application, the system can locate the free list that meets the requirements at a time.
For example, insert 40-byte free memory to a free list. The 40-byte free memory corresponds to the 10th free list in the range of [40, 43], and the 10th bit indicates the use of the free list. The system inserts the 40-byte free memory to the 10th free list and determines whether to update the bitmap flag. When 40-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If 580-byte free memory needs to be inserted to a free list, the 580-byte free memory corresponds to the 47th (31 + 2 x 8) free list in L2 range [2^9, 2^9+2^6], and the 47th bit indicates the use of the free list. The system inserts the 580-byte free memory to the 47th free list and determines whether to update the bitmap flag. When 580-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If the corresponding free list is empty, the system checks for a free list meeting the requirements in a larger memory range. In actual application, the system can locate the free list that meets the requirements at a time.
The figure below shows the memory management structure.
**Figure 2** Dynamic memory management structure for mini systems
![](figures/dynamic-memory-management-structure-for-mini-systems.png "dynamic-memory-management-structure-for-mini-systems")
**Figure 3** Dynamic memory management structure for mini systems
![](figures/dynamic-memory-management-structure-for-mini-systems.png "dynamic-memory-management-structure-for-mini-systems")
- Memory pool header
The memory pool header contains the memory pool information, bitmap flag array, and free list array. The memory pool information includes the start address of the memory pool, total size of the heap memory, and attributes of the memory pool. The bitmap flag array consists of seven 32-bit unsigned integers. Each bit indicates whether the free list is inserted with free memory block nodes. The free list contains information about 223 free memory head nodes. The free memory head node information contains a memory node header and information about the previous and next nodes in the free list.
- Memory pool nodes
There are three types of nodes: free node, used node, and end node. Each memory node maintains the size and use flag of the memory node and a pointer to the previous memory node in the memory pool. The free nodes and used nodes have a data area, but the end node has no data area.
The off-chip physical memory needs to be used because the on-chip RAMs of some chips cannot meet requirements. The OpenHarmony LiteOS-M kernel can logically combine multiple discontiguous memory regions so that users are unaware of the discontiguous memory regions in the underlying layer. The OpenHarmony LiteOS-M kernel memory module inserts discontiguous memory regions into a free list as free memory nodes and marks the discontiguous parts as virtual memory nodes that have been used. In this way, the discontinuous memory regions are logically combined as a unified memory pool.
The off-chip physical memory needs to be used because the on-chip RAMs of some chips cannot meet requirements. The OpenHarmony LiteOS-M kernel can logically combine multiple discontiguous memory regions so that users are unaware of the discontiguous memory regions in the underlying layer. The OpenHarmony LiteOS-M kernel memory module inserts discontiguous memory regions into a free list as free memory nodes and marks the discontiguous parts as virtual memory nodes that have been used. In this way, the discontinuous memory regions are logically combined as a unified memory pool. The figure below shows how the discontiguous memory regions are logically integrated.
The figure below shows how the discontiguous memory regions are logically integrated.
**Figure 4** Integrating discontiguous memory regions
**Figure 3** Integrating discontiguous memory regions
![](figures/integrating-discontiguous-memory-regions.png "integrating-discontiguous-memory-regions")
![](figures/integrating-discontiguous-memory-regions.png "integrating-discontiguous-memory-regions")
The discontiguous memory regions are integrated into a unified memory pool as follows:
......@@ -202,7 +207,7 @@ The discontiguous memory regions are integrated into a unified memory pool as fo
2. Obtain the start address and length of the next memory region, and calculate the **gapSize** between the current memory region and its previous memory region. The **gapSize** is considered as a used virtual node.
3. Set the size of the end node of the previous memory region to the sum of **gapSize** and **OS_MEM_NODE_HEAD_SIZE**.
3. Set the size of the end node of the previous memory region to the sum of **gapSize** and **OS_MEM_NODE_HEAD_SIZE**, that is, **sizeof(struct OsMemUsedNodeHead)**.
4. Divide the current memory region into a free memory node and an end node, insert the free memory node to the free list, and set the link relationship between the nodes.
......@@ -221,15 +226,15 @@ Dynamic memory management allocates and manages memory resources requested by us
The following table describes APIs available for OpenHarmony LiteOS-M dynamic memory management. For more details about the APIs, see the API reference.
**Table 1** APIs of the dynamic memory module
**Table 2** APIs of the dynamic memory module
| Category| Description|
| -------- | -------- |
| Initializing or deleting a memory pool| - **LOS_MemInit**: initializes a dynamic memory pool of the specified size.<br>- **LOS_MemDeInit**: deletes a memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.|
| Allocating or releasing dynamic memory| - **LOS_MemAlloc**: allocates memory of the specified size from the dynamic memory pool.<br>- **LOS_MemFree**: releases the memory allocated from the specified dynamic memory.<br>- **LOS_MemRealloc**: re-allocates a memory block of the required size and copies data from the original block to the newly allocated bock. If the new memory block is successfully allocated, the original memory block will be released.|
| Obtaining memory pool information| - **LOS_MemPoolSizeGet**: obtains the total size of the specified dynamic memory pool.<br>- **LOS_MemTotalUsedGet**: obtains the total memory usage of the specified dynamic memory pool.<br>- **LOS_MemInfoGet**: obtains the memory structure information of the specified memory pool, including the free memory, used memory, number of free memory blocks, number of used memory blocks, and maximum size of the free memory block.<br>- **LOS_MemPoolList**: prints information about all initialized memory pools in the system, including the start address, size, total free memory, used memory, maximum size of the free memory block, number of free memory blocks, and number of used memory blocks of each memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled. |
| Obtaining memory block information| - **LOS_MemFreeNodeShow**: prints the size and number of free memory blocks in the specified memory pool.<br>- **LOS_MemUsedNodeShow**: prints the size and number of used memory blocks in the specified memory pool.|
| Checking memory pool integrity| **LOS_MemIntegrityCheck**: checks the integrity of the specified memory pool. It is valid only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled.|
| Initializing or deleting a memory pool| **LOS_MemInit**: initializes a dynamic memory pool of the specified size.<br>**LOS_MemDeInit**: deletes a memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.|
| Allocating or releasing dynamic memory| **LOS_MemAlloc**: allocates memory of the specified size from the dynamic memory pool.<br>**LOS_MemFree**: releases the memory allocated from the specified dynamic memory.<br>**LOS_MemRealloc**: re-allocates a memory block of the required size and copies data from the original block to the newly allocated bock. If the new memory block is successfully allocated, the original memory block will be released.|
| Obtaining memory pool information| **LOS_MemPoolSizeGet**: obtains the total size of the specified dynamic memory pool.<br>**LOS_MemTotalUsedGet**: obtains the total memory usage of the specified dynamic memory pool.<br>**LOS_MemInfoGet**: obtains the memory structure information of the specified memory pool, including the free memory, used memory, number of free memory blocks, number of used memory blocks, and maximum size of the free memory block.<br>**LOS_MemPoolList**: prints information about all initialized memory pools in the system, including the start address, size, total free memory, used memory, maximum size of the free memory block, number of free memory blocks, and number of used memory blocks of each memory pool. This parameter is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.|
| Obtaining memory block information| **LOS_MemFreeNodeShow**: prints the size and number of free memory blocks in a memory pool.<br>**LOS_MemUsedNodeShow**: prints the size and number of used memory blocks in a memory pool.|
| Checking memory pool integrity| **LOS_MemIntegrityCheck**: checks the integrity of a memory pool. This parameter is valid only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled.|
| Adding discontiguous memory regions| **LOS_MemRegionsAdd**: logically integrates multiple discontiguous memory regions into a unified memory pool. This parameter is valid only when **LOSCFG_MEM_MUL_REGIONS** is enabled. If the memory pool pointer **pool** is empty, initialize the first of the multiple memory regions in the memory pool and insert other memory regions as free nodes. If **pool** is not empty, insert the multiple memory regions into the specified memory pool as free nodes.|
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**<br>
......@@ -245,15 +250,12 @@ The following table describes APIs available for OpenHarmony LiteOS-M dynamic me
The typical development process of dynamic memory is as follows:
1. Call **LOS_MemInit** to initialize a memory pool.
After a memory pool is initialized, a memory pool control header and end node will be generated, and the remaining memory is marked as free nodes. The end node is the last node in the memory pool, and its size is **0**.
2. Call **LOS_MemAlloc** to allocate dynamic memory of any size.
1. Call **LOS_MemAlloc** to allocate dynamic memory of any size.
The system checks whether the dynamic memory pool has free memory blocks greater than the requested size. If yes, the system allocates a memory block and returns the pointer to the memory block. If no, the system returns NULL. If the memory block allocated is greater than the requested size, the system splits the memory block and inserts the remaining memory block to the free list.
3. Call **LOS_MemFree** to release dynamic memory.
1. Call **LOS_MemFree** to release dynamic memory.
The released memory block can be reused. When **LOS_MemFree** is called, the memory block will be reclaimed and marked as free nodes. When memory blocks are reclaimed, adjacent free nodes are automatically merged.
......@@ -273,18 +275,24 @@ This example implements the following:
The sample code is as follows:
The sample code is compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. Call **ExampleDynMem** in **TestTaskEntry**.
```
#include "los_memory.h"
#define TEST_POOL_SIZE (2*1024)
__attribute__((aligned(4))) UINT8 g_testPool[TEST_POOL_SIZE];
VOID Example_DynMem(VOID)
#define MEMBOX_WR_TEST_NUM 828
__attribute__((aligned(4))) UINT8 g_testDynPool[TEST_POOL_SIZE];
VOID ExampleDynMem(VOID)
{
UINT32 *mem = NULL;
UINT32 ret;
/* Initialize the memory pool. */
ret = LOS_MemInit(g_testPool, TEST_POOL_SIZE);
ret = LOS_MemInit(g_testDynPool, TEST_POOL_SIZE);
if (LOS_OK == ret) {
printf("Mem init success!\n");
} else {
......@@ -292,20 +300,20 @@ VOID Example_DynMem(VOID)
return;
}
/* Allocate memory. */
mem = (UINT32 *)LOS_MemAlloc(g_testPool, 4);
if (NULL == mem) {
/* Request a memory block. */
mem = (UINT32 *)LOS_MemAlloc(g_testDynPool, 4);
if (mem == NULL) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/* Assign a value. */
*mem = 828;
/*Verify the read and write operations on the memory address. */
*mem = MEMBOX_WR_TEST_NUM;
printf("*mem = %d\n", *mem);
/ Release the memory. */
ret = LOS_MemFree(g_testPool, mem);
ret = LOS_MemFree(g_testDynPool, mem);
if (LOS_OK == ret) {
printf("Mem free success!\n");
} else {
......
......@@ -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.
>![](../public_sys-resources/icon-note.gif) **NOTE**
> - 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.
printf("g_timerCount1=%d, tick_last1=%d\n", g_timerCount1, tick_last1);
}
printf("g_timerCount1=%d\n", g_timerCount1);
}
void Timer2_Callback(UINT32 arg) //Callback 2
/* Callback 2, for the periodic software timer. */
void Timer2Callback(UINT32 arg)
{
UINT32 tick_last2;
tick_last2 = (UINT32)LOS_TickCountGet();
g_timerCount2++;
printf("g_timerCount2=%d tick_last2=%d\n", g_timerCount2, tick_last2);
}
printf("g_timerCount2=%d\n", g_timerCount2);
}
void Timer_example(void)
void SwtmrTest(void)
{
UINT32 ret;
UINT32 id1; // timer id1
UINT32 id2; // timer id2
UINT32 id1; // One-shot software timer.
UINT32 id2; // Periodic software timer.
UINT32 tickCount;
/* Create a one-shot software timer, with the number of ticks set to 1000. When the number of ticks reaches 1000, callback function 1 is executed. */
LOS_SwtmrCreate(1000, LOS_SWTMR_MODE_ONCE, Timer1_Callback, &id1, 1);
#if (LOSCFG_BASE_CORE_SWTMR_ALIGN == 1)
/* Create a one-shot software timer, with the number of ticks set to 1000. Invoke callback 1 when the number of ticks reaches 1000. */
LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0,
OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE);
/* Create a periodic software timer and invoke callback 2 every 100 ticks. */
LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0,
OS_SWTMR_ROUSES_IGNORE, OS_SWTMR_ALIGN_SENSITIVE);
#else
/* Create a one-shot software timer, with the number of ticks set to 1000. Callback 1 will be invoked when the number of ticks reaches 1000. */
LOS_SwtmrCreate(SWTMR_INTERVAL_LONG, LOS_SWTMR_MODE_ONCE, Timer1Callback, &id1, 0);
/* Create a periodic software timer and execute callback function 2 every 100 ticks. */
LOS_SwtmrCreate(100, LOS_SWTMR_MODE_PERIOD, Timer2_Callback, &id2, 1);
printf("create Timer1 success\n");
/* Create a periodic software timer and invoke callback 2 every 100 ticks. */
LOS_SwtmrCreate(SWTMR_INTERVAL_SHORT, LOS_SWTMR_MODE_PERIOD, Timer2Callback, &id2, 0);
#endif
LOS_SwtmrStart(id1); // Start the one-shot software timer.
printf("start Timer1 success\n");
/* Start the one-time software timer. */
ret = LOS_SwtmrStart(id1);
printf("start Timer1 %s\n", (ret == LOS_OK) ? "success" : "failed");
LOS_TaskDelay(200); // Delay 200 ticks.
LOS_SwtmrTimeGet(id1, &tickCount); // Obtain the number of remaining ticks of the one-short software timer.
printf("tickCount=%d\n", tickCount);
/* Short delay. The timer is not triggered yet. */
LOS_TaskDelay(SWTMR_INTERVAL_SHORT);
LOS_SwtmrStop(id1); // Stop the software timer.
printf("stop Timer1 success\n");
/* The one-short timer is not triggered yet. The timer can be stopped successfully. */
ret = LOS_SwtmrStop(id1);
printf("stop timer1 %s\n", (ret == LOS_OK) ? "success" : "failed");
LOS_SwtmrStart(id1);
LOS_TaskDelay(1000);
/* Long-time delay, triggered by the timer. */
LOS_TaskDelay(SWTMR_INTERVAL_LONG);
/* The timer is automatically deleted after being triggered. The stop operation should fail. */
ret = LOS_SwtmrStop(id1);
printf("timer1 self delete test %s\n", (ret != LOS_OK) ? "success" : "failed");
LOS_SwtmrStart(id2); // Start the periodic software timer.
printf("start Timer2\n");
/* Start the periodic software timer. */
ret = LOS_SwtmrStart(id2);
printf("start Timer2 %s\n", (ret == LOS_OK) ? "success" : "failed");
/* Long-time delay, triggered periodically by the timer. */
LOS_TaskDelay(SWTMR_INTERVAL_LONG);
LOS_TaskDelay(1000);
LOS_SwtmrStop(id2);
ret = LOS_SwtmrDelete(id2); // Delete the software timer.
ret = LOS_SwtmrDelete(id2);
if (ret == LOS_OK) {
printf("delete Timer2 success\n");
}
}
UINT32 Example_TaskEntry(VOID)
UINT32 ExampleSwtmr(VOID)
{
UINT32 ret;
TSK_INIT_PARAM_S task1;
TSK_INIT_PARAM_S taskParam = { 0 };
UINT32 taskId;
/* Lock task scheduling. */
LOS_TaskLock();
/* Create task 1. */
(VOID)memset(&task1, 0, sizeof(TSK_INIT_PARAM_S));
task1.pfnTaskEntry = (TSK_ENTRY_FUNC)Timer_example;
task1.pcName = "TimerTsk";
task1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
task1.usTaskPrio = 5;
ret = LOS_TaskCreate(&g_testTaskId01, &task1);
/* Create a task. */
taskParam.pfnTaskEntry = (TSK_ENTRY_FUNC)SwtmrTest;
taskParam.pcName = "TimerTsk";
taskParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam.usTaskPrio = 5;
ret = LOS_TaskCreate(&taskId, &taskParam);
if (ret != LOS_OK) {
printf("TimerTsk create failed.\n");
return LOS_NOK;
......@@ -221,7 +238,6 @@ UINT32 Example_TaskEntry(VOID)
/* Unlock task scheduling. */
LOS_TaskUnlock();
return LOS_OK;
}
```
......@@ -233,22 +249,20 @@ The output is as follows:
```
create Timer1 success
start Timer1 success
tickCount=798
stop Timer1 success
g_timerCount1=1, tick_last1=1208
delete Timer1 success
start Timer2
g_timerCount2=1 tick_last2=1313
g_timerCount2=2 tick_last2=1413
g_timerCount2=3 tick_last2=1513
g_timerCount2=4 tick_last2=1613
g_timerCount2=5 tick_last2=1713
g_timerCount2=6 tick_last2=1813
g_timerCount2=7 tick_last2=1913
g_timerCount2=8 tick_last2=2013
g_timerCount2=9 tick_last2=2113
g_timerCount2=10 tick_last2=2213
stop timer1 success
g_timerCount1=1
timer1 self delete test success
start Timer2 success
g_timerCount2=1
g_timerCount2=2
g_timerCount2=3
g_timerCount2=4
g_timerCount2=5
g_timerCount2=6
g_timerCount2=7
g_timerCount2=8
g_timerCount2=9
g_timerCount2=10
delete Timer2 success
```
......@@ -5,7 +5,7 @@
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:
**Task State Transitions**
**Figure 1** Task state transition<br>
**Figure 1** Task state transitions
![](figures/task-state-transitions.png "task-state-transitions")
![](figures/task-state-transitions.png "task-state-transitions")
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.
>![](../public_sys-resources/icon-note.gif) **NOTE**<br/>
> **NOTE**
> - 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. */
LOS_TaskLock();
printf("LOS_TaskLock() Success!\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskHi;
initParam.usTaskPrio = TSK_PRIOR_HI;
initParam.pcName = "TaskHi";
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
initParam.uwResved = 0; /* Detach attributes. */
taskParam1.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleTaskHi;
taskParam1.usTaskPrio = TSK_PRIOR_HI;
taskParam1.pcName = "TaskHi";
taskParam1.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam1.uwResved = LOS_TASK_ATTR_JOINABLE; /* Detach attribute. */
/* Create a task with higher priority. The task will not be executed immediately after being created, because task scheduling is locked. */
ret = LOS_TaskCreate(&g_taskHiId, &initParam);
ret = LOS_TaskCreate(&g_taskHiId, &taskParam1);
if (ret != LOS_OK) {
LOS_TaskUnlock();
......@@ -250,13 +254,13 @@ UINT32 Example_TskCaseEntry(VOID)
printf("Example_TaskHi create Success!\n");
initParam.pfnTaskEntry = (TSK_ENTRY_FUNC)Example_TaskLo;
initParam.usTaskPrio = TSK_PRIOR_LO;
initParam.pcName = "TaskLo";
initParam.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
taskParam2.pfnTaskEntry = (TSK_ENTRY_FUNC)ExampleTaskLo;
taskParam2.usTaskPrio = TSK_PRIOR_LO;
taskParam2.pcName = "TaskLo";
taskParam2.uwStackSize = LOSCFG_BASE_CORE_TSK_DEFAULT_STACK_SIZE;
/* Create a low-priority task. The task will not be executed immediately after being created, because task scheduling is locked. */
ret = LOS_TaskCreate(&g_taskLoId, &initParam);
ret = LOS_TaskCreate(&g_taskLoId, &taskParam2);
if (ret != LOS_OK) {
LOS_TaskUnlock();
printf("Example_TaskLo create Failed!\n");
......@@ -269,7 +273,7 @@ UINT32 Example_TskCaseEntry(VOID)
LOS_TaskUnlock();
ret = LOS_TaskJoin(g_taskHiId, NULL);
if (ret != LOS_OK) {
printf("Join Example_TaskHi Failed!\n");
printf("Join Example_TaskHi Failed!, 0x%x\n", ret);
} else {
printf("Join Example_TaskHi Success!\n");
}
......@@ -277,13 +281,12 @@ UINT32 Example_TskCaseEntry(VOID)
}
```
### Verification
**Verification**
The development is successful if the return result is as follows:
```
```
LOS_TaskLock() Success!
Example_TaskHi create Success!
Example_TaskLo create Success!
......
......@@ -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. |
**Table 3** API for time registration
| API | Description |
| --------------------- | ---------------------------------------------- |
| LOS_TickTimerRegister | Re-registers the timer of the system clock and the corresponding interrupt handler.|
**Table 4** APIs for delay
| API | Description |
| ---------- | ------------------------ |
| LOS_MDelay | Delays a task, in ms.|
| LOS_UDelay | Delays a task, in μs.|
## How to Develop
......@@ -52,13 +64,12 @@ The typical development process of time management is as follows:
2. Call the clock conversion and statistics APIs.
>![](../public_sys-resources/icon-note.gif) **NOTE**
>
> ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**
> - 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.
dprintf("ms = %d \n", ms);
/* Convert 10000 ms to ticks. */
tick = LOS_MS2Tick(10000);
printf("tick = %d \n", tick);
/* Convert 100 ticks to ms. */
ms = LOS_Tick2MS(100);
printf("ms = %d \n", ms);
}
```
......@@ -101,26 +117,21 @@ Time statistics and delay:
```
VOID Example_GetTime(VOID)
VOID ExampleGetTime(VOID)
{
UINT32 cyclePerTick;
UINT64 tickCount;
UINT64 tickCountBefore;
UINT64 tickCountAfter;
cyclePerTick = LOS_CyclePerTickGet();
if(0 != cyclePerTick) {
dprintf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
}
tickCount = LOS_TickCountGet();
if(0 != tickCount) {
dprintf("LOS_TickCountGet = %d \n", (UINT32)tickCount);
if (0 != cyclePerTick) {
printf("LOS_CyclePerTickGet = %d \n", cyclePerTick);
}
tickCountBefore = LOS_TickCountGet();
LOS_TaskDelay(200);
tickCount = LOS_TickCountGet();
if(0 != tickCount) {
dprintf("LOS_TickCountGet after delay = %d \n", (UINT32)tickCount);
}
tickCountAfter = LOS_TickCountGet();
printf("LOS_TickCountGet after delay rising = %d \n", (UINT32)(tickCountAfter - tickCountBefore));
}
```
......@@ -140,8 +151,7 @@ ms = 1000
Time statistics and delay:
```
LOS_CyclePerTickGet = 495000
LOS_TickCountGet = 1
LOS_TickCountGet after delay = 201
```
LOS_CyclePerTickGet = 250000 (The data may vary depending on the actual running environment.)
LOS_TickCountGet after delay rising = 200
```
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册