An event is a mechanism for communication between tasks. It can be used to synchronize tasks. The events have the following features:
An event is a communication mechanism used to synchronize tasks. 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.
- 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.
- Event read timeout mechanism is used.
- Events are used only for task synchronization, but not for data transmission.
- Events are used for task synchronization, but not for data transmission.
APIs are provided to initialize, read/write, clear, and destroy events.
APIs are provided to initialize, read/write, clear, and destroy events.
...
@@ -18,7 +18,7 @@ APIs are provided to initialize, read/write, clear, and destroy events.
...
@@ -18,7 +18,7 @@ APIs are provided to initialize, read/write, clear, and destroy events.
### Event Control Block
### 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:
The event control block is a structure in the event initialization function. It passes in event identifies for operations such as event read and write. The data structure of the event control block is as follows:
```
```
...
@@ -31,23 +31,33 @@ typedef struct tagEvent {
...
@@ -31,23 +31,33 @@ typedef struct tagEvent {
### Working Principles
### 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.
**Initializing an Event**
**Writing an event**: When a specified event is written to the event control block, the event control block updates the event set, traverses the task linked list, and determines whether to wake up related task based on the task conditions.
An event control block is created to maintain a set of processed events and a linked list of tasks waiting for specific events.
**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.
**Writing an Event**
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:
When an event is written to the event control block, the event control block updates the event set, traverses the task linked list, and determines whether to wake up related tasks based on the task conditions.
-**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.
**Reading an Event**
-**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.
If the event to read already exists, it is returned synchronously. In other cases, the event is returned based on the timeout period and event triggering conditions. If the wait 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 parameters **eventMask** and **mode** determine whether the condition for reading an event is met. **eventMask** specifies the event mask. **mode** specifies the handling mode, which can be any of the following:
-**LOS_WAITMODE_AND**: Read the event 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**: Read the event only 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 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.
-**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 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.
**Clearing Events**
The events in the event set of the event control block can be cleared based on the specified mask. The mask **0** means to clear the event set; the mask **0xffff** means the opposite.
**Destroying Events**
**Destroying an event**: Destroy the specified event control block.
The event control block can be destroyed to release resources.
**Figure 1** Event working mechanism for a mini system
**Figure 1** Event working mechanism for a mini system
...
@@ -58,12 +68,12 @@ The input parameters **eventMask** and **mode** determine whether the condition
...
@@ -58,12 +68,12 @@ The input parameters **eventMask** and **mode** determine whether the condition
| Category| API| Description|
| 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.|
| Checking an event | LOS_EventPoll | Checks whether the expected event occurs based on **eventID**, **eventMask**, and **mode**.<br>**NOTE**<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.|
| Initializing an event control block | 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.|
| Reading an event | 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.|
| Writing an event | 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.|
| Clearing events | LOS_EventClear | Clears events in the event control block based on the event mask. |
| Event destruction| LOS_EventDestroy | Destroys an event control block.|
| Destroying events | LOS_EventDestroy | Destroys an event control block.|
## How to Develop
## How to Develop
...
@@ -72,11 +82,11 @@ The typical event development process is as follows:
...
@@ -72,11 +82,11 @@ The typical event development process is as follows:
1. Initialize an event control block.
1. Initialize an event control block.
2. Block a read event control block.
2. Block a read event.
3. Write related events.
3. Write events.
4. Wake up a blocked task, read the event, and check whether the event meets conditions.
4. Wake up the blocked task, read the event, and check whether the event meets conditions.
5. Handle the event control block.
5. Handle the event control block.
...
@@ -84,7 +94,7 @@ The typical event development process is as follows:
...
@@ -84,7 +94,7 @@ The typical event development process is as follows:
> **NOTE**
> **NOTE**
> - When an event is read or written, the 25th bit of the event is reserved and cannot be set.
> - For event read and write operations, the 25th bit (`0x02U << 24`) of the event is reserved and cannot be set.
>
>
> - Repeated writes of the same event are treated as one write.
> - Repeated writes of the same event are treated as one write.
...
@@ -111,7 +121,7 @@ In the **ExampleEvent** task, create an **EventReadTask** task with a timout per
...
@@ -111,7 +121,7 @@ In the **ExampleEvent** task, create an **EventReadTask** task with a timout per
The sample code is as follows:
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**.
The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **ExampleEvent()** function is called in **TestTaskEntry**.
@@ -77,7 +77,7 @@ The preceding figure illustrates how to write data to the tail node only. Writin
...
@@ -77,7 +77,7 @@ The preceding figure illustrates how to write data to the tail node only. Writin
## Available APIs
## Available APIs
| Category| Description|
| Category| API 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.|
| 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 (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.|
...
@@ -136,7 +136,7 @@ Create a queue and two tasks. Enable task 1 to write data to the queue, and task
...
@@ -136,7 +136,7 @@ Create a queue and two tasks. Enable task 1 to write data to the queue, and task
The sample code is as follows:
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**.
The sample code can be compiled and verified in **./kernel/liteos_m/testsuites/src/osTest.c**. The **ExampleQueue** function is called in **TestTaskEntry**.
A bitwise operation operates on a binary number at the level of its individual bits. For example, a variable can be set as a program status word \(PSW\), and each bit \(flag bit\) in the PSW can have a self-defined meaning.
A bitwise operation operates on the bits of a binary number. A variable can be set as a program status word (PSW), and each bit (flag bit) in the PSW can have a self-defined meaning.
## Available APIs<a name="section848334511411"></a>
## **Available APIs**
The system provides operations for setting the flag bit to **1** or **0**, changing the flag bit content, and obtaining the most significant bit and least significant bit of the flag bit 1 in a PSW. You can also perform bitwise operations on system registers. The following table describes the APIs available for the bitwise operation module. For more details about the APIs, see the API reference.
The system provides operations for setting the flag bit to **1** or **0**, changing the flag bit content, and obtaining the most significant bit (MSB) and least significant bit (LSB) of the flag bit 1 in a PSW. You can also perform bitwise operations on system registers. The following table describes the APIs available for the bitwise operation module. For more details about the APIs, see the API reference.
| Setting a flag bit| - **LOS_BitmapSet**: sets a flag bit of a PSW to **1**.<br>- **LOS_BitmapClr**: sets a flag bit of a PSW to **0**. |
</th>
| Obtaining the bit whose flag bit is **1**| -**LOS_HighBitGet**: obtains the most significant bit of 1 in a PSW.<br>- **LOS_LowBitGet**: obtains the least significant bit of 1 in a PSW. |
| Operating continuous bits| - **LOS_BitmapSetNBits**: sets the consecutive flag bits of a PSW to **1**.<br>- **LOS_BitmapClrNBits**: sets the consecutive flag bits of a PSW to **0**.<br>- **LOS_BitmapFfz**: obtains the first 0 bit starting from the LSB. |
</th>
</tr>
</thead>
## Development Example
<tbody><trid="row18801722069"><tdclass="cellrowborder"rowspan="2"valign="top"width="16.89168916891689%"headers="mcps1.2.4.1.1 "><pid="p108717579612"><aname="p108717579612"></a><aname="p108717579612"></a>Setting the flag bit to <strongid="b129301229122320"><aname="b129301229122320"></a><aname="b129301229122320"></a>1</strong> or <strongid="b1899463182312"><aname="b1899463182312"></a><aname="b1899463182312"></a>0</strong></p>
<tdclass="cellrowborder"valign="top"width="47.56475647564757%"headers="mcps1.2.4.1.3 "><pid="p16871957668"><aname="p16871957668"></a><aname="p16871957668"></a>Sets a flag bit of a PSW to <strongid="b1283195411179"><aname="b1283195411179"></a><aname="b1283195411179"></a>1</strong>.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p14871155718618"><aname="p14871155718618"></a><aname="p14871155718618"></a>Sets a flag bit of a PSW to <strongid="b15267438112312"><aname="b15267438112312"></a><aname="b15267438112312"></a>0</strong>.</p>
</td>
</tr>
<trid="row16880112663"><tdclass="cellrowborder"rowspan="2"valign="top"width="16.89168916891689%"headers="mcps1.2.4.1.1 "><pid="p158710579615"><aname="p158710579615"></a><aname="p158710579615"></a>Obtaining the bit whose flag bit is <strongid="b58742415239"><aname="b58742415239"></a><aname="b58742415239"></a>1</strong></p>
<tdclass="cellrowborder"valign="top"width="47.56475647564757%"headers="mcps1.2.4.1.3 "><pid="p168713571468"><aname="p168713571468"></a><aname="p168713571468"></a>Obtains the most significant bit of <strongid="b485014714235"><aname="b485014714235"></a><aname="b485014714235"></a>1</strong> in the PSW.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p148719571569"><aname="p148719571569"></a><aname="p148719571569"></a>Obtains the least significant bit of <strongid="b9907125542319"><aname="b9907125542319"></a><aname="b9907125542319"></a>1</strong> in the PSW.</p>
<tdclass="cellrowborder"valign="top"width="47.56475647564757%"headers="mcps1.2.4.1.3 "><pid="p10871135714613"><aname="p10871135714613"></a><aname="p10871135714613"></a>Sets the continuous flag bits of a PSW to <strongid="b145631313234"><aname="b145631313234"></a><aname="b145631313234"></a>1</strong>.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p1387145711610"><aname="p1387145711610"></a><aname="p1387145711610"></a>Sets the continuous flag bits of a PSW to <strongid="b185031722103115"><aname="b185031722103115"></a><aname="b185031722103115"></a>0</strong>.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p187115571369"><aname="p187115571369"></a><aname="p187115571369"></a>Obtains the first 0 bit starting from the least significant bit (LSB).</p>
</td>
</tr>
</tbody>
</table>
## Development Example<a name="section67569495514"></a>
### Example Description<a name="section33551554391"></a>
This example implements the following:
This example implements the following:
1. Set a flag bit to **1**.
1. Set a flag bit to **1**.
2. Obtain the most significant bit of flag bit 1.
3. Set a flag bit to **0**.
2. Obtain the MSB of flag bit 1.
4. Obtain the least significant bit of the flag bit 1.
3. Set a flag bit to **0**.
4. Obtain the LSB of flag bit 1.
### Sample Code
The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **BitSample** function is called in **TestTaskEntry**.
| Initializing a DLL| - **LOS_ListInit**: initializes a node as a DLL node.<br>- **LOS_DL_LIST_HEAD**: defines a node and initializes it as a DLL node.|
| Initializing a DLL | - **LOS_ListInit**: initializes a node as a DLL node.<br>- **LOS_DL_LIST_HEAD**: defines a node and initializes it as a DLL node.|
| Adding a node| - **LOS_ListAdd**: adds a node to the head of a DLL.<br>- **LOS_ListHeadInsert**: same as **LOS_ListAdd**.<br>- **LOS_ListTailInsert**: inserts a node to the tail of a DLL.|
| Adding a node | - **LOS_ListAdd**: adds a node to the head of a DLL.<br>- **LOS_ListHeadInsert**: same as **LOS_ListAdd**.<br>- **LOS_ListTailInsert**: inserts a node to the tail of a DLL.|
| Adding a DLL| - **LOS_ListAddList**: adds the head of a DLL to the head of this DLL.<br>- **LOS_ListHeadInsertList**: inserts the head of a DLL to the head of this DLL.<br>- **LOS_ListTailInsertList**: Inserts the end of a DLL to the head of this DLL.|
| Adding a DLL | - **LOS_ListAddList**: adds the head of a DLL to the head of this DLL.<br>- **LOS_ListHeadInsertList**: inserts the head of a DLL to the head of this DLL.<br>- **LOS_ListTailInsertList**: inserts the end of a DLL to the head of this 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.|
| 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.<br>- **LOS_DL_LIST_IS_END**: checks whether a node is the tail of the DLL.<br>- **LOS_DL_LIST_IS_ON_QUEUE**: checks whether a node is in the DLL.|
| Checking a DLL | - **LOS_ListEmpty**: checks whether a DLL is empty.<br>- **LOS_DL_LIST_IS_END**: checks whether a node is the tail of the DLL.<br>- **LOS_DL_LIST_IS_ON_QUEUE**: checks whether a node is in the DLL.|
| Obtains structure information.| - **LOS_OFF_SET_OF**: obtains the offset of a member in the specified structure relative to the start address of the structure.<br>- **LOS_DL_LIST_ENTRY**: obtains the address of the structure that contains the first node in the DLL. The first input parameter of the API indicates the head 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.<br>- **LOS_ListPeekHeadType**: obtains the address of the structure that contains the first node in the linked list. The first input parameter of the API indicates the head 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. Null will be returned if the DLL is empty.<br>- **LOS_ListRemoveHeadType**: obtains the address of the structure that contains the first node in the linked list, and deletes the first node from the list. The first input parameter of the API indicates the head 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. Null will be returned if the DLL is empty.<br>- **LOS_ListNextType**: obtains the address of the structure that contains the next node of the specified node in the linked list. The first input parameter of the API indicates the head node in the list, the second input parameter indicates the specified node, the third parameter indicates the name of the structure to be obtained, and the fourth input parameter indicates the name of the linked list in the structure. If the next node of the linked list node is the head node and is empty, NULL will be returned.|
| Obtaining structure information | - **LOS_OFF_SET_OF**: obtains the offset of a member in the specified structure relative to the start address of the structure.<br>- **LOS_DL_LIST_ENTRY**: obtains the address of the structure that contains the first node in the DLL. The first input parameter of the API indicates the head 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.<br>- **LOS_ListPeekHeadType**: obtains the address of the structure that contains the first node in the linked list. The first input parameter of the API indicates the head 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. Null will be returned if the DLL is empty.<br>- **LOS_ListRemoveHeadType**: obtains the address of the structure that contains the first node in the linked list, and deletes the first node from the list. The first input parameter of the API indicates the head 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. Null will be returned if the DLL is empty.<br>- **LOS_ListNextType**: obtains the address of the structure that contains the next node of the specified node in the linked list. The first input parameter of the API indicates the head node in the list, the second input parameter indicates the specified node, the third parameter indicates the name of the structure to be obtained, and the fourth input parameter indicates the name of the linked list in the structure. If the next node of the linked list node is the head node and is empty, NULL will be returned.|
| 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 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 the 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.|
| 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
## How to Develop
...
@@ -30,7 +29,7 @@ The typical development process of the DLL is as follows:
...
@@ -30,7 +29,7 @@ The typical development process of the DLL is as follows:
2. Call **LOS_ListAdd** to add a node into the DLL.
2. Call **LOS_ListAdd** to add a node into the DLL.
3. Call **LOS_ListTailInsert** to insert a node to the tail of 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.
4. Call **LOS_ListDelete** to delete the specified node.
...
@@ -39,18 +38,19 @@ The typical development process of the DLL is as follows:
...
@@ -39,18 +38,19 @@ The typical development process of the DLL is as follows:
6. Call **LOS_ListDelInit** to delete the specified node and initialize the DLL based on the node.
6. Call **LOS_ListDelInit** to delete the specified node and initialize the DLL based on the node.
> - Pay attention to the operations operations of the front and back pointer of the node.
>
>
> - Pay attention to the operations before and after the node pointer.
>
> - 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.
> - 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.
> - If the memory of a linked list node is dynamically allocated, release the memory when deleting the node.
**Development Example**
## Development Example
**Example Description**
### Example Description
This example implements the following:
This example implements the following:
...
@@ -63,7 +63,11 @@ This example implements the following:
...
@@ -63,7 +63,11 @@ This example implements the following:
4. Check the operation result.
4. Check the operation result.
### Sample Code
The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **ListSample** function is called in **TestTaskEntry**.
The OpenHarmony kernel uses the musl libc library that supports the Portable Operating System Interface \(POSIX\). You can develop components and applications working on the kernel based on the POSIX.
The OpenHarmony kernel uses the musl libc library that supports the Portable Operating System Interface (POSIX). You can develop components and applications working on the kernel based on the POSIX.
The musl libc library supports POSIX standards. The OpenHarmony kernel adapts the related system call APIs to implement external functions.
The musl libc library supports POSIX standards. The OpenHarmony kernel adapts the related system call APIs to implement external functions.
For details about the APIs supported by the standard library, see the API document of the C library, which also covers the differences between the standard library and the POSIX standard library.
For details about the APIs supported by the standard library, see the API document of the C library, which also covers the differences between the standard library and the POSIX standard library.
## Development Example
In this example, the main thread creates **THREAD\_NUM** child threads. Once a child thread is started, it enters the standby state. After the main thread successfully wakes up all child threads, they continue to execute until the lifecycle ends. The main thread uses the **pthread\_join** method to wait until all child threads are executed.
### Development Example
#### Example Description
In this example, the main thread creates THREAD_NUM child threads. Once a child thread is started, it enters the standby state. After the main thread successfully wakes up all child threads, they continue to execute until the lifecycle ends. The main thread uses the **pthread_join** method to wait until all child threads are executed.
#### Sample Code
The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **ExamplePosix** function is called in **TestTaskEntry**.
The sample code is as follows:
```
```
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <unistd.h>
#include <pthread.h>
#include <pthread.h>
#ifdef __cplusplus
#if __cplusplus
extern "C" {
#endif /* __cplusplus */
#endif /* __cplusplus */
#define THREAD_NUM 3
#define THREAD_NUM 3
int g_startNum = 0; /* Number of started threads */
int g_startNum = 0; /* Number of threads to start */
int g_wakenNum = 0; /* Number of wakeup threads */
int g_wakenNum = 0; /* Number of threads to wake up */
printf("ERROR: pthread create failed, error code is %d!\n", rc);
dprintf("ERROR: pthread create failed, error code is %d!\n", rc);
goto ERROROUT;
goto ERROROUT;
}
}
}
}
dprintf("pthread_create ok\n");
/* Wait until all child threads lock a mutex. */
/* Wait until all child threads obtain a mutex. */
while (g_startNum < THREAD_NUM) {
while (g_startNum < THREAD_NUM) {
usleep(100);
usleep(100);
}
}
...
@@ -118,14 +123,14 @@ static int testcase(void)
...
@@ -118,14 +123,14 @@ static int testcase(void)
/* Acquire a mutex and block all threads using pthread_cond_wait. */
/* Acquire a mutex and block all threads using pthread_cond_wait. */
rc = pthread_mutex_lock(&g_td.mutex);
rc = pthread_mutex_lock(&g_td.mutex);
if (rc != 0) {
if (rc != 0) {
printf("ERROR: mutex lock failed, error code is %d\n", rc);
dprintf("ERROR: mutex lock failed, error code is %d\n", rc);
goto ERROROUT;
goto ERROROUT;
}
}
/* Release a mutex. */
/* Release the mutex. */
rc = pthread_mutex_unlock(&g_td.mutex);
rc = pthread_mutex_unlock(&g_td.mutex);
if (rc != 0) {
if (rc != 0) {
printf("ERROR: mutex unlock failed, error code is %d!\n", rc);
dprintf("ERROR: mutex unlock failed, error code is %d!\n", rc);
goto ERROROUT;
goto ERROROUT;
}
}
...
@@ -133,7 +138,7 @@ static int testcase(void)
...
@@ -133,7 +138,7 @@ static int testcase(void)
/* Broadcast signals on the cond variable. */
/* Broadcast signals on the cond variable. */
rc = pthread_cond_signal(&g_td.cond);
rc = pthread_cond_signal(&g_td.cond);
if (rc != 0) {
if (rc != 0) {
printf("ERROR: pthread condition failed, error code is %d!\n", rc);
dprintf("ERROR: pthread condition failed, error code is %d!\n", rc);
goto ERROROUT;
goto ERROROUT;
}
}
}
}
...
@@ -142,73 +147,69 @@ static int testcase(void)
...
@@ -142,73 +147,69 @@ static int testcase(void)
/* Check whether all child threads are woken up. */
/* Check whether all child threads are woken up. */
if (g_wakenNum != THREAD_NUM) {
if (g_wakenNum != THREAD_NUM) {
printf("ERROR: not all threads awaken, only %d thread(s) awaken!\n", g_wakenNum);
dprintf("ERROR: not all threads awaken, only %d thread(s) awaken!\n", g_wakenNum);
goto ERROROUT;
goto ERROROUT;
}
}
dprintf("all threads awaked\n");
/* Wait for all threads to terminate. */
/* Join all child threads, that is, wait for the end of all child threads. */
for (i = 0; i < THREAD_NUM; i++) {
for (i = 0; i < THREAD_NUM; i++) {
rc = pthread_join(thread[i], NULL);
rc = pthread_join(thread[i], NULL);
if (rc != 0) {
if (rc != 0) {
printf("ERROR: pthread join failed, error code is %d!\n", rc);
dprintf("ERROR: pthread join failed, error code is %d!\n", rc);
goto ERROROUT;
goto ERROROUT;
}
}
}
}
dprintf("all threads join ok\n");
/* Destroy the cond variable. */
/* Destroy the cond variable. */
rc = pthread_cond_destroy(&g_td.cond);
rc = pthread_cond_destroy(&g_td.cond);
if (rc != 0) {
if (rc != 0) {
printf("ERROR: pthread condition destroy failed, error code is %d!\n", rc);
dprintf("ERROR: pthread condition destroy failed, error code is %d!\n", rc);
goto ERROROUT;
goto ERROROUT;
}
}
return 0;
return 0;
ERROROUT:
ERROROUT:
return -1;
return -1;
}
}
```
/*
#### Verification
* Main function
*/
int main(int argc, char *argv[])
{
int rc;
/* Start the test function. */
The output is as follows:
rc = testcase();
if (rc != 0) {
printf("ERROR: testcase failed!\n");
}
return 0;
```
}
pthread_create ok
#ifdef __cplusplus
all threads awaked
#if __cplusplus
all threads join ok
}
#endif /* __cplusplus */
#endif /* __cplusplus */
```
```
## Differences from the Linux Standard Library
## Differences from the Linux Standard Library
This section describes the key differences between the standard library carried by the OpenHarmony kernel and the Linux standard library. For more differences, see the API document of the C library.
The following describes the key differences between the standard library supported by the OpenHarmony kernel and the Linux standard library. For more differences, see the API document of the C library.
### Process
### Process
1. The OpenHarmony user-mode processes support only static priorities, which range from 10 \(highest\) to 31 \(lowest\).
- The OpenHarmony user-mode processes support only static priorities, which range from 10 (highest) to 31 (lowest).
2. The OpenHarmony user-mode threads support only static priorities, which range from 0 \(highest\) to 31 \(lowest\).
3. The OpenHarmony process scheduling supports **SCHED\_RR** only, and thread scheduling supports **SCHED\_RR** or **SCHED\_FIFO**.
- The OpenHarmony user-mode threads support only static priorities, which range from 0 (highest) to 31 (lowest).
- The OpenHarmony process scheduling supports **SCHED_RR** only, and thread scheduling supports **SCHED_RR** or **SCHED_FIFO**.
### Memory
### Memory
**h2****Difference with Linux mmap**
**Differences from Linux mmap**
mmap prototype: **void \*mmap (void \*addr, size_t length, int prot, int flags, int fd, off_t offset)**
mmap prototype: **void \*mmap \(void \*addr, size\_t length, int prot, int flags, int fd, off\_t offset\)**
The lifecycle implementation of **fd** is different from that of Linux glibc. glibc releases the **fd** handle immediately after successfully invoking **mmap** for mapping. In the OpenHarmony kernel, you are not allowed to close the **fd** immediately after the mapping is successful. You can close the **fd** only after **munmap** is called. If you do not close **fd**, the OS reclaims the **fd** when the process exits.
The lifecycle implementation of **fd** is different from that of Linux glibc. glibc releases the **fd** handle immediately after successfully invoking **mmap** for mapping. In the OpenHarmony kernel, you are not allowed to close the **fd** immediately after the mapping is successful. You can close the **fd** only after **munmap** is called. If you do not close **fd**, the OS reclaims the **fd** when the process exits.
**Example**
**h2****Sample Code**
Linux OS:
Linux:
```
```
int main(int argc, char *argv[])
int main(int argc, char *argv[])
...
@@ -226,13 +227,14 @@ int main(int argc, char *argv[])
...
@@ -226,13 +227,14 @@ int main(int argc, char *argv[])
perror("mmap");
perror("mmap");
exit(EXIT_FAILURE);
exit(EXIT_FAILURE);
}
}
close(fd); /* OpenHarmony does not support close fd immediately after the mapping is successful. */
close(fd); /* OpenHarmony does not support closing fd immediately after the mapping is successful. */
...
...
exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS);
}
}
```
```
OpenHarmony:
OpenHarmony:
```
```
int main(int argc, char *argv[])
int main(int argc, char *argv[])
...
@@ -252,27 +254,32 @@ int main(int argc, char *argv[])
...
@@ -252,27 +254,32 @@ int main(int argc, char *argv[])
}
}
...
...
munmap(addr, length);
munmap(addr, length);
close(fd); /* Close fd after the munmap is canceled. */
close(fd); /* Close fd after the munmap is canceled. */
exit(EXIT_SUCCESS);
exit(EXIT_SUCCESS);
}
}
```
```
### File System
### File System
**System directories**: You cannot modify system directories and device mount directories, which include **/dev**, **/proc**, **/app**, **/bin**, **/data**, **/etc**, **/lib**, **/system** and **/usr**.
System directories: You cannot modify system directories and device mount directories, which include **/dev**, **/proc**, **/app**, **/bin**, **/data**, **/etc**, **/lib**, **/system**, and**/usr**.
**User directory**: The user directory refers to the **/storage** directory. You can create, read, and write files in this directory, but cannot mount devices.
User directory: The user directory refers to the **/storage** directory. You can create, read, and write files in this directory, but cannot mount it to a device.
Except in the system and user directories, you can create directories and mount them to devices. Note that nested mount is not allowed, that is, a mounted folder and its subfolders cannot be mounted repeatedly. A non-empty folder cannot be mounted.
Except in the system and user directories, you can create directories and mount devices. Note that nested mount is not allowed, that is, a mounted folder and its subfolders cannot be mounted repeatedly. A non-empty folder cannot be mounted.
### Signal
### Signal
- The default behavior for signals does not include **STOP**, **CONTINUE**, or **COREDUMP**.
- The default behavior for signals does not include **STOP**, **CONTINUE**, or **COREDUMP**.
- A sleeping process \(for example, a process enters the sleeping status by calling the sleep function\) cannot be woken up by a signal. The signal mechanism does not support the wakeup function. The behavior for a signal can be processed only when the process is scheduled by the CPU.
- After a process exits, **SIGCHLD** is sent to the parent process. The sending action cannot be canceled.
- Only signals 1 to 30 are supported. The callback is executed only once even if the same signal is received multiple times.
### Time
- A sleeping process (for example, a process enters the sleeping status by calling the sleep function) cannot be woken up by a signal. The signal mechanism does not support the wakeup function. The behavior for a signal can be processed only when the process is scheduled by the CPU.
The OpenHarmony time precision is based on tick. The default value is 10 ms/tick. The time error of the **sleep** and **timeout** functions is less than or equal to 20 ms.
- After a process exits, **SIGCHLD** is sent to the parent process. The sending action cannot be canceled.
- Only signals 1 to 30 are supported. The callback is invoked only once even if the same signal is received multiple times.
### Time
The default time precision of OpenHarmony is 10 ms/tick. The time error of the **sleep** and **timeout** functions is less than or equal to 20 ms.
An event is a mechanism for communication between tasks. It can be used to synchronize tasks.
## Basic Concepts
An event is a communication mechanism used to synchronize tasks.
In multi-task environment, synchronization is required between tasks. Events can be used for synchronization in the following cases:
In multi-task environment, synchronization is required between tasks. Events can be used for synchronization in the following cases:
- One-to-many synchronization: A task waits for the triggering of multiple events. A task is woken up by one or multiple events.
- One-to-many synchronization: A task waits for the triggering of multiple events. A task can be woken up by one or multiple events.
- Many-to-many synchronization: Multiple tasks wait for the triggering of multiple events.
- Many-to-many synchronization: Multiple tasks wait for the triggering of multiple events.
The event mechanism provided by the OpenHarmony LiteOS-A event module has the following features:
The event mechanism provided by the OpenHarmony LiteOS-A event module has the following features:
- A task triggers or waits for an event by creating an event control block.
- A task triggers or waits for an event by creating an event control block.
- Events are independent of each other. The internal implementation is a 32-bit unsigned integer, and each bit indicates an event type. The 25th bit is unavailable. Therefore, a maximum of 31 event types are supported.
- Events are used only for synchronization between tasks, but not for data transmission.
- Events are independent of each other. The internal implementation is a 32-bit unsigned integer, and each bit indicates an event type. The value **0** indicates that the event type does not occur, and the value **1** indicates that the event type has occurred. There are 31 event types in total. The 25th bit (`0x02U << 24`) is reserved.
- Writing the same event type to the event control block for multiple times is equivalent to writing the event type only once before the event control block is cleared.
- Multiple tasks can read and write the same event.
- Events are used for task synchronization, but not for data transmission.
- The event read/write timeout mechanism is supported.
- Writing the same event type to an event control block multiple times is equivalent to writing the event type only once before the event control block is cleared.
- Multiple tasks can read and write the same event.
## Working Principles<a name="section94611116593"></a>
- The event read/write timeout mechanism is supported.
## Working Principles
### Event Control Block
### Event Control Block<a name="section1161415384467"></a>
```
```
/**
/**
* Event control block data structure
* Event control block data structure
*/
*/
typedef struct tagEvent {
typedef struct tagEvent {
UINT32 uwEventID; /* Event set, which is a collection of events processed (written and cleared). */
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;
} EVENT_CB_S, *PEVENT_CB_S;
```
```
### Working Principles<a name="section187761153144617"></a>
**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.
### Working Principles
**Initializing an Event**
An event control block is created to maintain a set of processed events and a linked list of tasks waiting for specific events.
**Writing an event**: When a specified event is written to the event control block, the event control block updates the event set, traverses the task linked list, and determines whether to wake up related task based on the task conditions.
**Writing an Event**
**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.
When an event is written to the event control block, the event control block updates the event set, traverses the task linked list, and determines whether to wake up related task based on the specified conditions.
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:
**Reading an Event**
-**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.
If the event to read already exists, it is returned synchronously. In other cases, the event is returned based on the timeout period and event triggering conditions. If the wait 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.
-**LOS\_WAITMODE\_OR**: Event reading is successful when any of the events corresponding to **eventMask** occurs. 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.
**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.
The parameters **eventMask** and **mode** determine whether the condition for reading an event is met. **eventMask** specifies the event mask. **mode** specifies the handling mode, which can be any of the following:
**Destroying an event**: Destroy the specified event control block.
-**LOS_WAITMODE_AND**: Read the event only when all the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned.
**Figure 1** Event working mechanism for small systems<aname="fig17799175324612"></a>
-**LOS_WAITMODE_OR**: Read the event only when any of the events corresponding to **eventMask** occur. Otherwise, the task will be blocked, or an error code will be returned.
## Development Guidelines<a name="section44744471891"></a>
-**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.
### Available APIs<a name="section172373513919"></a>
**Clearing Events**
The events in the event set of the event control block can be cleared based on the specified mask. The mask **0** means to clear the event set; the mask **0xffff** means the opposite.
**Destroying Events**
The event control block can be destroyed to release resources.
**Figure 1** Event working mechanism for small systems
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p2334141425515"><aname="p2334141425515"></a><aname="p2334141425515"></a>Initializes an event control block.</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p1621275475517"><aname="p1621275475517"></a><aname="p1621275475517"></a>Reads a specified type of event, with the timeout period of a relative time period in ticks.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p10271958567"><aname="p10271958567"></a><aname="p10271958567"></a>Writes a specified type of event.</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p14862153525620"><aname="p14862153525620"></a><aname="p14862153525620"></a>Clears a specified type of event.</p>
</td>
</tr>
<trid="row1525316428553"><tdclass="cellrowborder"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p4253144265519"><aname="p4253144265519"></a><aname="p4253144265519"></a>Checking the event mask</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p13998115465617"><aname="p13998115465617"></a><aname="p13998115465617"></a>Returns whether the event input by the user meets the expectation based on the event ID, event mask, and read mode passed by the user.</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p32592615573"><aname="p32592615573"></a><aname="p32592615573"></a>Destroys a specified event control block.</p>
</td>
</tr>
</tbody>
</table>
### How to Develop<a name="section1118215161013"></a>
The typical event development process is as follows:
The typical event development process is as follows:
1. Initialize an event control block.
1. Initialize an event control block.
2. Block a read event control block.
3. Write related events.
2. Block a read event.
4. Wake up a blocked task, read the event, and check whether the event meets conditions.
5. Handle the event control block.
3. Write related events.
6. Destroy an event control block.
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**
>
> - For event read and write operations, the 25th bit (`0x02U << 24`) of the event is reserved and cannot be set.
>
> - Repeated writes of the same event are treated as one write.
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.
>- 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<a name="section5837165132911"></a>
1. Create the **Example_Event** task in the **Example_TaskEntry** task with a higher priority than the **Example_TaskEntry** task.
### Example Description<a name="section128221510145718"></a>
2. Run the **Example_Event** task to read event **0x00000001**. Task switching is triggered to execute the **Example_TaskEntry** task.
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, and 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.
3. Run the **Example_TaskEntry** task to write event **0x00000001**. Task switching is triggered to execute the **Example_Event** task.
1. Create the **Example\_Event** task in the **Example\_TaskEntry** task with a higher priority than the **Example\_TaskEntry** task.
4. The **Example_Event** task is executed.
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.
### Sample Code<a name="section71507479577"></a>
5. The **Example_TaskEntry** task is executed.
### Sample Code
The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **Example_EventEntry** function is called in **TestTaskEntry**.
The sample code is as follows:
The sample code is as follows:
...
@@ -149,28 +148,28 @@ The sample code is as follows:
...
@@ -149,28 +148,28 @@ The sample code is as follows:
#include "los_task.h"
#include "los_task.h"
#include "securec.h"
#include "securec.h"
/* Task ID*/
/* Task ID*/
UINT32 g_testTaskId;
UINT32 g_testTaskId;
/* Event control structure*/
/* Event control structure*/
EVENT_CB_S g_exampleEvent;
EVENT_CB_S g_exampleEvent;
/* Type of the wait event*/
/* Type of the wait event*/
#define EVENT_WAIT 0x00000001
#define EVENT_WAIT 0x00000001
#define EVENT_TIMEOUT 500
/* Example task entry function*/
/* Example task entry function*/
VOID Example_Event(VOID)
VOID Example_Event(VOID)
{
{
UINT32 event;
UINT32 event;
/* 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.*/
/* 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.*/
Semaphore is a mechanism for implementing inter-task communication. It implements synchronization between tasks or exclusive access to shared resources.
Semaphore is a mechanism used to implement synchronization between tasks or exclusive access to shared resources.
In the data structure of a semaphore, there is a value indicating the number of shared resources available. The value can be:
In the semaphore data structure, there is a value indicating the number of shared resources available. The value can be:
-**0**: The semaphore is unavailable. Tasks waiting for the semaphore may exist.
-**0**: The semaphore is unavailable. In this case, tasks waiting for the semaphore may exist.
- Positive number: The semaphore is available.
The semaphore for exclusive access is different from the semaphore for synchronization:
- Positive number: The semaphore is available.
- Semaphore used for exclusive access: The initial semaphore counter value \(non-zero\) indicates the number of shared resources available. The semaphore counter value must be acquired before a shared resource is used, and released when the resource is no longer required. 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.
The semaphore used for exclusive access to resources is different from the semaphore used for synchronization:
- Semaphore used for 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.
## Working Principles<a name="section118423019134"></a>
- Semaphore used for exclusive access: The initial semaphore counter value \(non-zero\) indicates the number of shared resources available. A semaphore must be acquired before a shared resource is used, and released when the resource is no longer required. When all shared resources are used, the semaphore counter is reduced to 0 and all tasks requiring the semaphore will be blocked. This ensures exclusive access to shared resources. In addition, if the number of shared resources is 1, a binary semaphore \(similar to the mutex mechanism\) is recommended.
- Semaphore used for synchronization: The initial semaphore counter value is **0**. A task without the semaphore will be blocked, and enters the Ready or Running state only when the semaphore is released by another task or an interrupt.
## Working Principles
**Semaphore Control Block**
**Semaphore Control Block**
```
```
/**
/**
* Data structure of the semaphore control block
* Data structure of the semaphore control block
*/
*/
typedef struct {
typedef struct {
UINT16 semStat; /* Semaphore status */
UINT16 semStat; /* Semaphore status */
UINT16 semType; /* Semaphore type*/
UINT16 semType; /* Semaphore type*/
UINT16 semCount; /* Semaphore count*/
UINT16 semCount; /* Semaphore count*/
UINT16 semId; /* Semaphore index*/
UINT16 semId; /* Semaphore ID */
LOS_DL_LIST semList; /* Mount the task blocked by the semaphore.*/
LOS_DL_LIST semList; /* List of blocked tasks */
} LosSemCB;
} LosSemCB;
```
```
...
@@ -36,102 +40,89 @@ typedef struct {
...
@@ -36,102 +40,89 @@ typedef struct {
Semaphore allows only a specified number of tasks to access a shared resource at a time. When the number of tasks accessing the resource reaches the limit, other tasks will be blocked until the semaphore is released.
Semaphore allows only a specified number of tasks to access a shared resource at a time. When the number of tasks accessing the resource reaches the limit, other tasks will be blocked until the semaphore is released.
- Semaphore initialization
- Semaphore initialization
Allocate memory for the semaphores (the number of semaphores is specified by the **LOSCFG_BASE_IPC_SEM_LIMIT** macro), set all semaphores to the unused state, and add them to a linked list.
- Semaphore creation
Obtain a semaphore from the linked list of unused semaphores and assign an initial value to the semaphore.
The system allocates memory for the semaphores configured \(you can configure the number of semaphores using the **LOSCFG\_BASE\_IPC\_SEM\_LIMIT** macro\), initializes all semaphores to be unused semaphores, and adds them to a linked list for the system to use.
- Semaphore request
- Semaphore creation
If the counter value is greater than 0 when a semaphore is requsted, the counter is decreased by 1 and a success message is returned. Otherwise, the task is blocked and added to the end of a task queue waiting for semaphores. The wait timeout period can be set.
The system obtains a semaphore from the linked list of unused semaphores and assigns an initial value to the semaphore.
- Semaphore release
- Semaphore request
If no task is waiting for the semaphore, the counter is incremented by 1. Otherwise, wake up the first task in the wait queue.
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 moves the task to the end of a task queue waiting for semaphores. The wait timeout period can be set.
- Semaphore deletion
- Semaphore release
Set a semaphore in use to the unused state and add it to the linked list of unused semaphores.
When a semaphore is released, if there is no task waiting for it, the counter value is increased by 1. Otherwise, the first task in the wait queue is woken up.
The following figure illustrates the semaphore working mechanism.
- Semaphore deletion
**Figure 1** Semaphore working mechanism for the small system
The system sets a semaphore in use to unused state and inserts it to the linked list of unused semaphores.
| LOS_SemPend | Requests a semaphore and sets a timeout period.|
<tbody><trid="row0415737175610"><tdclass="cellrowborder"rowspan="3"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p8866127195914"><aname="p8866127195914"></a><aname="p8866127195914"></a>Creating or deleting a semaphore</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p48623102592"><aname="p48623102592"></a><aname="p48623102592"></a>Creates a semaphore and returns the semaphore ID.</p>
</td>
1. Call **LOS_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS_BinarySemCreate**.
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p1886211011599"><aname="p1886211011599"></a><aname="p1886211011599"></a>Creates a binary semaphore. The maximum counter value is <strongid="b4125169919"><aname="b4125169919"></a><aname="b4125169919"></a>1</strong>.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p586261085913"><aname="p586261085913"></a><aname="p586261085913"></a>Deletes a semaphore.</p>
> As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts.
</td>
</tr>
<trid="row73651459105815"><tdclass="cellrowborder"rowspan="2"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p16927183515593"><aname="p16927183515593"></a><aname="p16927183515593"></a>Requesting or releasing a semaphore</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p555221518598"><aname="p555221518598"></a><aname="p555221518598"></a>Requests a specified semaphore and sets the timeout period.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p1555261595915"><aname="p1555261595915"></a><aname="p1555261595915"></a>Posts (releases) a semaphore.</p>
</td>
</tr>
</tbody>
</table>
### How to Develop<a name="section154261711141419"></a>
1. Call **LOS\_SemCreate** to create a semaphore. To create a binary semaphore, call **LOS\_BinarySemCreate**.
>As interrupts cannot be blocked, semaphores cannot be requested in block mode for interrupts.
### Development Example<a name="section658135571417"></a>
### Example Description<a name="section125244411653"></a>
This example implements the following:
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**.
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.\)
2. Enable **ExampleSemTask2** to enter sleep mode for 20 ticks after acquiring the semaphore. (When **ExampleSemTask2** is delayed, **ExampleSemTask1** is woken up.)
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.
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. After that, delete the semaphore.
The sample code can be compiled and verified in **./kernel/liteos_a/testsuites/kernel/src/osTest.c**. The **ExampleSem** function is called in **TestTaskEntry**.