Time management provides all time-related services for applications based on the system clock.
...
...
@@ -12,67 +11,79 @@ People use second or millisecond as the time unit, while the operating system us
The time management module of the OpenHarmony LiteOS-M kernel provides time conversion and statistics functions.
## Time Unit<a name="section97172532397"></a>
- Cycle
## 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.
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
## Available APIs
Tick is the basic time unit of the operating system and is determined by the number of ticks per second configured by the user.
The following table describes APIs available for OpenHarmony LiteOS-M time management. For more details about the APIs, see the API reference.
**Table 1** APIs of the time management module
## Available APIs<a name="section158501652121514"></a>
| API| Description|
| -------- | -------- |
| LOS_MS2Tick | Converts milliseconds into ticks.|
| LOS_Tick2MS | Converts ticks into milliseconds.|
| OsCpuTick2MS | Converts cycles into milliseconds. Two UINT32 values indicate the high-order and low-order 32 bits of the result value, respectively.|
| OsCpuTick2US | Converts cycles into microseconds. Two UINT32 values indicate the high-order and low-order 32 bits of the result value, respectively.|
The following table describes APIs available for the OpenHarmony LiteOS-M time management. For more details about the APIs, see the API reference.
**Table 2** APIs for time statistics
**Table 1** APIs of the time management module
| 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.|
| Category| API| Description|
| -------- | -------- | -------- |
| Time conversion| LOS_MS2Tick | Converts milliseconds into ticks.|
| | LOS_Tick2MS | Converts ticks into milliseconds.|
| | OsCpuTick2MS | Converts cycles into milliseconds. Two UINT32 values indicate the high-order and low-order 32 bits of the result value, respectively.|
| | OsCpuTick2US | Converts cycles into microseconds. Two UINT32 values indicate the high-order and low-order 32 bits of the result value, respectively.|
| Time statistics| 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_CurrNanosec |Obtains the number of nanoseconds since the system starts.|
| Delay management| LOS_UDelay |Performs busy waiting in μs, which can be preempted by a task with a higher priority.|
| | LOS_MDelay |Performs busy waiting in ms, which can be preempted by a task with a higher priority.|
## How to Develop<a name="section783435801510"></a>
## How to Develop
The typical development process of time management is as follows:
1. Complete board configuration and adaptation as required, and configure the system clock frequency \(**OS\_SYS\_CLOCK** in Hz and **LOSCFG\_BASE\_CORE\_TICK\_PER\_SECOND**\). The default value of **OS\_SYS\_CLOCK** varies with the hardware platform.
2. Call the clock conversion and statistics APIs.
1. Complete board configuration and adaptation as required, and configure the system clock frequency (**OS_SYS_CLOCK** in Hz and **LOSCFG_BASE_CORE_TICK_PER_SECOND**). The default value of **OS_SYS_CLOCK** varies with the hardware platform.
>- 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 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.
## Development Example<a name="section460018317164"></a>
## Development Example
### Example Description<a name="section127752801718"></a>
### Example Description
The following example describes basic time management methods, including:
- 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.
### Sample Code<a name="section321653551711"></a>
### Sample Code
Prerequisites
- The default value of **LOSCFG\_BASE\_CORE\_TICK\_PER\_SECOND** is **100**.
- The system clock frequency **OS\_SYS\_CLOCK** is configured.
- The default value of **LOSCFG_BASE_CORE_TICK_PER_SECOND** is **100**.
- The system clock frequency **OS_SYS_CLOCK** is configured.
The software timer is a software-simulated timer based on system tick interrupts. When the preset tick counter value has elapsed, the user-defined callback will be invoked. The timing precision is related to the cycle of the system tick clock.
...
...
@@ -8,144 +9,132 @@ Due to the limitation in hardware, the number of hardware timers cannot meet use
The software timer supports the following functions:
- Disabling the software timer using a macro
- Creating a software timer
- Starting a software timer
- Stopping a software timer
- Deleting a software timer
- Obtaining the number of remaining ticks of a software timer
- Disabling the software timer using a macro
## Working Principles
- Creating a software timer
The software timer is a system resource. When modules are initialized, a contiguous section of memory is allocated for software timers. The maximum number of timers supported by the system is configured by the **LOSCFG\_BASE\_CORE\_SWTMR\_LIMIT** macro in **los\_config.h**.
- Starting a software timer
Software timers use a queue and a task resource of the system. The software timers are triggered based on the First In First Out \(FIFO\) rule. A timer with a shorter value is always closer to the queue head than a timer with a longer value, and is preferentially triggered.
- Stopping a software timer
The software timer counts time in ticks. When a software timer is created and started, the OpenHarmony LiteOS-M kernel determines the timer expiry time based on the current system time \(in ticks\) and the timing interval set by the user, and adds the timer control structure to the global timing list.
- Deleting a software timer
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.
- Obtaining the number of remaining ticks of a software timer
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.
### Timer States
## Working Principles
- OS\_SWTMR\_STATUS\_UNUSED
The software timer is a system resource. When modules are initialized, a contiguous section of memory is allocated for software timers. The maximum number of timers supported by the system is configured by the **LOSCFG_BASE_CORE_SWTMR_LIMIT** macro in **los_config.h**.
The timer is not in use. When the timer module is initialized, all timer resources in the system are set to this state.
Software timers use a queue and a task resource of the system. The software timers are triggered based on the First In First Out (FIFO) rule. A timer with a shorter value is always closer to the queue head than a timer with a longer value, and is preferentially triggered.
The software timer counts time in ticks. When a software timer is created and started, the OpenHarmony LiteOS-M kernel determines the timer expiry time based on the current system time (in ticks) and the timing interval set by the user, and adds the timer control structure to the global timing list.
- OS\_SWTMR\_STATUS\_CREATED
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.
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.
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.
- OS\_SWTMR\_STATUS\_TICKING
### Timer States
The timer is running \(counting\). When **LOS\_SwtmrStart** is called for a newly created timer, the timer enters this state.
- 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.
The OpenHarmony LiteOS-M kernel provides three types of software timers:
The OpenHarmony LiteOS-M kernel provides the following types of software timers:
- One-shot timer: Once started, the timer is automatically deleted after triggering only one timer event.
- Periodic timer: This type of timer periodically triggers timer events until it is manually stopped.
- One-shot timer deleted by calling an API
- One-shot timer: Once started, the timer is automatically deleted after triggering only one timer event.
- Periodic timer: This type of timer periodically triggers timer events until it is manually stopped.
- One-shot timer deleted by calling an API
## Available APIs
The following table describes APIs available for the OpenHarmony LiteOS-M software timer module. For more details about the APIs, see the API reference.
<tbody><trid="row159539510586"><tdclass="cellrowborder"rowspan="2"valign="top"width="17.77177717771777%"headers="mcps1.2.4.1.1 "><pid="p1194410585810"><aname="p1194410585810"></a><aname="p1194410585810"></a>Creating or deleting timers</p>
<tdclass="cellrowborder"valign="top"width="58.44584458445845%"headers="mcps1.2.4.1.3 "><pid="p9944105175818"><aname="p9944105175818"></a><aname="p9944105175818"></a>Creates a software timer.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p39445585817"><aname="p39445585817"></a><aname="p39445585817"></a>Deletes a software timer.</p>
</td>
</tr>
<trid="row79531357589"><tdclass="cellrowborder"rowspan="2"valign="top"width="17.77177717771777%"headers="mcps1.2.4.1.1 "><pid="p139443595820"><aname="p139443595820"></a><aname="p139443595820"></a>Starting or stopping timers</p>
<tdclass="cellrowborder"valign="top"width="58.44584458445845%"headers="mcps1.2.4.1.3 "><pid="p1194415518581"><aname="p1194415518581"></a><aname="p1194415518581"></a>Starts a software timer.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p169441515816"><aname="p169441515816"></a><aname="p169441515816"></a>Stop a software timer.</p>
</td>
</tr>
<trid="row119525513581"><tdclass="cellrowborder"valign="top"width="17.77177717771777%"headers="mcps1.2.4.1.1 "><pid="p109442053586"><aname="p109442053586"></a><aname="p109442053586"></a>Obtaining remaining ticks of a software timer</p>
<tdclass="cellrowborder"valign="top"width="58.44584458445845%"headers="mcps1.2.4.1.3 "><pid="p39441257586"><aname="p39441257586"></a><aname="p39441257586"></a>Obtaining remaining ticks of a software timer</p>
</td>
</tr>
</tbody>
</table>
**Table 1** Software timer APIs
| API| 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.|
## How to Develop
The typical development process of software timers is as follows:
1. Configure the software timer.
- Check that **LOSCFG\_BASE\_CORE\_SWTMR** and **LOSCFG\_BASE\_IPC\_QUEUE** are set to **1**.
- Configure **LOSCFG\_BASE\_CORE\_SWTMR\_LIMIT**\(maximum number of software timers supported by the system\).
- Configure **OS\_SWTMR\_HANDLE\_QUEUE\_SIZE**\(maximum length of the software timer queue\).
1. Configure the software timer.
- Check that **LOSCFG_BASE_CORE_SWTMR** and **LOSCFG_BASE_IPC_QUEUE** are set to **1**.
- Configure **LOSCFG_BASE_CORE_SWTMR_LIMIT** (maximum number of software timers supported by the system).
- Configure **OS_SWTMR_HANDLE_QUEUE_SIZE** (maximum length of the software timer queue).
2. Call **LOS_SwtmrCreate** to create a software timer.
- Create a software timer with the specified timing duration, timeout handling function, and triggering mode.
- Return the function execution result (success or failure).
2. Call **LOS\_SwtmrCreate** to create a software timer.
- Create a software timer with the specified timing duration, timeout handling function, and triggering mode.
- Return the function execution result \(success or failure\).
3. Call **LOS_SwtmrStart** to start the software timer.
3. Call **LOS\_SwtmrStart** to start the software timer.
4. Call **LOS\_SwtmrTimeGet** to obtain the remaining number of ticks of the software timer.
5. Call **LOS\_SwtmrStop** to stop the software timer.
6. Call **LOS\_SwtmrDelete** to delete the software timer.
4. Call **LOS_SwtmrTimeGet** to obtain the remaining number of ticks of the software timer.
5. Call **LOS_SwtmrStop** to stop the software timer.
6. Call **LOS_SwtmrDelete** to delete the software timer.
> - Avoid too many operations in the callback function of the software timer. Do not use APIs or perform operations that may cause task suspension or blocking.
>
> - 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.
>
> - 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.
>- 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.
>- 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.
>- 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.
## Development Example
### Example Description
The following programming example demonstrates how to:
1. Create, start, delete, pause, and restart a software timer.
2. Use a one-shot software timer and a periodic software timer
1. Create, start, delete, pause, and restart a software timer.
2. Use a one-shot software timer and a periodic software timer
### Sample Code
Prerequisites
- In **los\_config.h**, **LOSCFG\_BASE\_CORE\_SWTMR** is enabled.
- In **los\_config.h**, **LOSCFG\_BASE\_CORE\_SWTMR\_ALIGN** is disabled. The sample code does not involve timer alignment.
- The maximum number of software timers supported by the system \(**LOSCFG\_BASE\_CORE\_SWTMR\_LIMIT**\) is configured.
- The maximum length of the software timer queue \(OS\_SWTMR\_HANDLE\_QUEUE\_SIZE\) is configured.
- In **los_config.h**, **LOSCFG_BASE_CORE_SWTMR** is enabled.
- In **los_config.h**, **LOSCFG_BASE_CORE_SWTMR_ALIGN** is disabled. The sample code does not involve timer alignment.
- The maximum number of software timers supported by the system (**LOSCFG_BASE_CORE_SWTMR_LIMIT**) is configured.
- The maximum length of the software timer queue (OS_SWTMR_HANDLE_QUEUE_SIZE) is configured.
The sample code is as follows:
```
#include "los_swtmr.h"
...
...
@@ -156,7 +145,7 @@ UINT32 g_timerCount2 = 0;
/* Task ID*/
UINT32 g_testTaskId01;
void Timer1_Callback(UINT32 arg) //Callback function 1
void Timer1_Callback(UINT32 arg) //Callback 1
{
UINT32 tick_last1;
g_timerCount1++;
...
...
@@ -164,7 +153,7 @@ void Timer1_Callback(UINT32 arg) //Callback function 1
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:
- Multiple tasks are supported.
- A task represents a thread.
- The preemptive scheduling mechanism is used for tasks. High-priority tasks can interrupt low-priority tasks. Low-priority tasks can be scheduled only after high-priority tasks are blocked or complete.
- Time slice round-robin is used to schedule tasks with the same priority.
- A total of 32 \(**0** to **31**\) priorities are defined. **0** is the highest priority, and **31** is the lowest.
### Task-related Concepts
**Task States**
A task has multiple states. After the system initialization is complete, the created tasks can compete for certain resources in the system according to the scheduling procedure regulated by the kernel.
A task can be in any of the following states:
- Ready: The task is in the ready queue, waiting for execution by a CPU.
- Running: The task is being executed.
- Blocked: The task is not in the ready queue. The task may be suspended, delayed, waiting for a semaphore, waiting to read from or write into a queue, or reading from or writing into an event.
- Dead: The task execution is complete and waiting for the system to reclaim resources.
**Task State Transitions**
**Figure 1** Task state transitions<aname="fig186971918162613"></a>
A task enters Ready state once created. When task switching occurs, the task with the highest priority in the Ready queue will be executed. The task being executed enters the Running state and is removed from the Ready queue.
- The preemptive scheduling mechanism is used for tasks. High-priority tasks can interrupt low-priority tasks. Low-priority tasks can be scheduled only after high-priority tasks are blocked or complete.
- Running → Blocked
-Time slice round-robin is used to schedule tasks with the same priority.
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.
- A total of 32 (**0** to **31**) priorities are defined. **0** is the highest priority, and **31** is the lowest.
- Blocked → Ready \(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.
### Task-related Concepts
- Ready → Blocked
**Task States**
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.
A task has multiple states. After the system initialization is complete, the created tasks can compete for certain resources in the system according to the scheduling procedure regulated by the kernel.
- Running → Ready
A task can be in any of the following states:
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.
- Ready: The task is in the ready queue, waiting for execution by a CPU.
- Running → Dead
-Running: The task is being executed.
When a running task is complete, it changes to the Dead state. The Dead state includes normal exit state as the task is complete and the Invalid state. For example, if a task is complete but is not automatically deleted, the task is in the Invalid state.
- Blocked: The task is not in the ready queue. The task may be suspended, delayed, waiting for a semaphore, waiting to read from or write into a queue, or reading from or writing into an event.
- Blocked → Dead
-Dead: The task execution is complete and waiting for the system to reclaim resources.
If an API is called to delete a blocked task, the task state change from Blocked to Dead.
A task enters Ready state once created. When task switching occurs, the task with the highest priority in the Ready queue will be executed. The task being executed enters the Running state and is removed from the Ready queue.
- 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)
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.
- 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.
- Running → Dead
When a running task is complete, it changes to the Dead state. The Dead state includes normal exit state as the task is complete and the Invalid state. For example, if a task is complete but is not automatically deleted, the task is in the Invalid state.
- Blocked → Dead
If an API is called to delete a blocked task, the task state change from Blocked to Dead.
**Task ID**
...
...
@@ -83,81 +86,84 @@ Resources, such as registers, used during the running of a task. When a task is
**Task Control Block**
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.
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.
**Task Switching**
Task switching involves actions, such as obtaining the task with the highest priority in the Ready queue, saving the context of the switched-out task, and restoring the context of the switched-in task.
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.
## 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
| Category| API| Description|
| -------- | -------- | -------- |
| Creating or deleting a task| LOS_TaskCreateOnly | Creates a task and suspends the task to disable scheduling of the task. To enable scheduling of the task, call **LOS_TaskResume** to make the task enter the Ready state.|
| | 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.|
| | LOS_TaskDelete | Deletes a task.|
| Controlling task status| LOS_TaskResume | Resumes a suspended task to place it in the Ready state.|
| | LOS_TaskSuspend | Suspends the specified task and performs task switching.|
| | LOS_TaskJoin | Suspends this task till the specified task is complete and the task control block resources are reclaimed.|
| | LOS_TaskDetach | Changes the task attribute from **joinable** to **detach**. After the task of the **detach** attribute is complete, the task control block resources will be automatically reclaimed.|
| | LOS_TaskDelay | Makes a task wait for a period of time (in ticks) and releases CPU resources. When the delay time expires, the task enters the Ready state again. The input parameter is the number of ticks.|
| | LOS_Msleep | Converts the input number of milliseconds into number of ticks, and use the result to call **LOS_TaskDelay**.|
| | 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.|
| | LOS_TaskUnlock | Unlocks task scheduling.|
| | LOS_Schedule | Triggers task scheduling|
| Controlling task priority| LOS_CurTaskPriSet | Sets the priority for the current task.|
| | LOS_TaskPriSet | Sets the priority for a specified task.|
| | LOS_TaskPriGet | Obtains the priority of a specified task.|
| Obtaining Job information| LOS_CurTaskIDGet | Obtains the ID of the current task.|
| | LOS_NextTaskIDGet | Obtains the ID of the task with the highest priority in the Ready queue.|
| | LOS_NewTaskIDGet | Same as **LOS_NextTaskIDGet**.|
| | LOS_CurTaskNameGet | Obtains the name of the current task.|
| | LOS_TaskNameGet | Obtains the name of a specified task.|
| | LOS_TaskStatusGet | Obtains the state of a specified task.|
| | 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.|
| | 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.|
| 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.|
| 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.|
## How to Develop
The typical development process of the task module is as follows:
1. Use **LOS\_TaskLock** to lock task scheduling and prevent high-priority tasks from being scheduled.
2. Use **LOS\_TaskCreate** to create a task.
3. Use **LOS\_TaskUnlock** to unlock task scheduling so that tasks can be scheduled by priority.
4. Use **LOS\_TaskDelay** to delay a task.
5. Use **LOS\_TaskSuspend** to suspend a task.
6. Use **LOS\_TaskResume** to resume the suspended task.
>- Running idle tasks reclaims the TCBs and stacks in the to-be-recycled linked list.
>- The task name is a pointer without memory space allocated. When setting the task name, do not assign the local variable address to the task name pointer.
>- The task stack size is 8-byte aligned. Follow the "nothing more and nothing less" principle while determining the task stack size.
>- A running task cannot be suspended if task scheduling is locked.
>- Idle tasks and software timer tasks cannot be suspended or deleted.
>- In an interrupt handler or when a task is locked, the operation of calling **LOS\_TaskDelay** fails.
>- Locking task scheduling does not disable interrupts. Tasks can still be interrupted while task scheduling is locked.
>- Locking task scheduling must be used together with unlocking task scheduling.
>- Task scheduling may occur while a task priority is being set.
>- The maximum number of tasks that can be set for the operating system is the total number of tasks of the operating system, not the number of tasks available to users. For example, if the system software timer occupies one more task resource, the number of task resources available to users decreases by one.
>- **LOS\_CurTaskPriSet** and **LOS\_TaskPriSet** cannot be used in interrupts or used to modify the priorities of software timer tasks.
>- If the task corresponding to the task ID sent to **LOS\_TaskPriGet** has not been created or the task ID exceeds the maximum number of tasks, **-1** will be returned.
>- Resources such as a mutex or a semaphore allocated to a task must have been released before the task is deleted.
1. Use **LOS_TaskLock** to lock task scheduling and prevent high-priority tasks from being scheduled.
2. Use **LOS_TaskCreate** to create a task.
3. Use **LOS_TaskUnlock** to unlock task scheduling so that tasks can be scheduled by priority.
4. Use **LOS_TaskDelay** to delay a task.
5. Use **LOS_TaskSuspend** to suspend a task.
6. Use **LOS_TaskResume** to resume the suspended task.
> - Running idle tasks reclaims the TCBs and stacks in the to-be-recycled linked list.
>
> - The task name is a pointer without memory space allocated. When setting the task name, do not assign the local variable address to the task name pointer.
>
> - The task stack size is 8-byte aligned. Follow the "nothing more and nothing less" principle while determining the task stack size.
>
> - A running task cannot be suspended if task scheduling is locked.
>
> - Idle tasks and software timer tasks cannot be suspended or deleted.
>
> - In an interrupt handler or when a task is locked, the operation of calling **LOS_TaskDelay** fails.
>
> - Locking task scheduling does not disable interrupts. Tasks can still be interrupted while task scheduling is locked.
>
> - Locking task scheduling must be used together with unlocking task scheduling.
>
> - Task scheduling may occur while a task priority is being set.
>
> - The maximum number of tasks that can be set for the operating system is the total number of tasks of the operating system, not the number of tasks available to users. For example, if the system software timer occupies one more task resource, the number of task resources available to users decreases by one.
>
> - **LOS_CurTaskPriSet** and **LOS_TaskPriSet** cannot be used in interrupts or used to modify the priorities of software timer tasks.
>
> - If the task corresponding to the task ID sent to **LOS_TaskPriGet** has not been created or the task ID exceeds the maximum number of tasks, **-1** will be returned.
>
> - Resources such as a mutex or a semaphore allocated to a task must have been released before the task is deleted.
## Development Example
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:
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.
In small devices with limited hardware resources, dynamic algorithm deployment capability is required to solve the problem that multiple algorithms cannot be deployed at the same time. The LiteOS-M kernel uses the Executable and Linkable Format \(ELF\) loading because it is easy to use and compatible with a wide variety of platforms. The LiteOS-M provides APIs similar to **dlopen** and **dlsym**. Apps can load and unload required algorithm libraries by using the APIs provided by the dynamic loading module. As shown in the following figure, the app obtains the corresponding information output through the API required by the third-party algorithm library. The third-party algorithm library depends on the basic APIs provided by the kernel, such as **malloc**. After the app loads the API and relocates undefined symbols, it can call the API to complete the function. The dynamic loading component supports only the Arm architecture. In addition, the signature and source of the shared library to be loaded must be verified to ensure system security.
In small devices with limited hardware resources, dynamic algorithm deployment capability is required to allow multiple algorithms to be deployed at the same time. The LiteOS-M kernel uses the Executable and Linkable Format (ELF) loading because it is easy to use and compatible with a wide variety of platforms.
The LiteOS-M provides APIs similar to **dlopen** and **dlsym**. Apps can load and unload required algorithm libraries by using the APIs provided by the dynamic loading module. As shown in the following figure, the app obtains the corresponding information output through the API required by the third-party algorithm library. The third-party algorithm library depends on the basic APIs provided by the kernel, such as **malloc**. After the app loads the API and relocates undefined symbols, it can call the API to complete the function.
The dynamic loading component supports only the Arm architecture. In addition, the signature and source of the shared library to be loaded must be verified to ensure system security.
The kernel needs to proactively expose the API required by the dynamic library when the shared library calls a kernel API, as shown in the following figure. This mechanism compiles the symbol information to the specified section and calls the **SYM\_EXPORT** macro to export information of the specified symbol. The symbol information is described in the structure **SymInfo**. Its members include the symbol name and symbol address information. The macro **SYM\_EXPORT** imports the symbol information to the **.sym.\*** section by using the **\_\_attribute\_\_** compilation attribute.
The kernel needs to proactively expose the API required by the dynamic library when the shared library calls a kernel API, as shown in the following figure. This mechanism compiles the symbol information to the specified section and calls the **SYM_EXPORT** macro to export information of the specified symbol. The symbol information is described in the structure **SymInfo**, which includes the symbol name and address information. The macro **SYM_EXPORT** imports the symbol information to the **.sym.*** section by using **__attribute__**.
During the loading process, the LOAD section to be loaded to the memory is obtained based on the ELF file handle and the section offset of the program header table. Generally, there are two sections: read-only section and read-write section. You can run the **readelf -l** command to view the LOAD section information of the ELF file. The physical memory is requested according to the related alignment attributes. Then, a code section or a data segment is written into the memory based on the loading base address and an offset of each section.
The **LOAD** section to be loaded to the memory can be obtained based on the ELF file handle and the section offset of the program header table. Generally, there are two sections: read-only and read-write. You can run the **readelf -l** command to view the LOAD section information of the ELF file. The physical memory is requested according to the related alignment attributes. Then, a code section or a data segment is written into the memory based on the loading base address and an offset of each section.
```
$ readelf -l lib.so
...
...
@@ -43,8 +55,7 @@ There are 4 program headers, starting at offset 52
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
EXIDX 0x000760 0x00000760 0x00000760 0x00008 0x00008 R 0x4
LOAD 0x000000 0x00000000 0x00000000 0x0076c 0x0076c R E 0x10000
A relocation table is obtained by using a **.dynamic** section of the ELF file. Each entry that needs to be relocated in the table is traversed. Then, the symbol is searched, based on the symbol name that needs to be relocated, in the shared library and the exported symbol table provided by the kernel. The relocation information is updated based on the symbol found.
When compiling a shared library, you can add **-fPIC**\(a compilation option\) to compile location-independent code. The shared library file type is **ET\_DYN**, which can be loaded to any valid address range.
When compiling a shared library, you can add **-fPIC**(a compilation option) to compile location-independent code. The shared library file type is **ET_DYN**, which can be loaded to any valid address range.
4.**-z max-page-size=4**: sets the number of alignment bytes of the loadable sections in the binary file to **4**. This setting saves memory and can be used for a dynamic library.
5.**-mcpu=** specifies the CPU architecture.
-**-nostdlib**: Do not use the lib library in the compiler when linking.
-**-nostartfiles**: Do not use the startup files in the compiler when linking.
-**-z max-page-size=4**: sets the number of alignment bytes of the loadable sections in the binary file to **4**. This setting saves memory and can be used for a dynamic library.
-**-mcpu=** specifies the CPU architecture.
## Constraints
- Applications cannot be loaded. Only shared libraries can be loaded.
- The shared library to be loaded cannot depend on the libc library or other shared libraries in the compiler. It can depend only on the external APIs provided by the kernel (provided by the exported symbol table).
- This feature depends on the cross compiler and file system.
File Allocation Table \(FAT\) is a file system developed for personal computers. It consists of the DOS Boot Record \(DBR\) region, FAT region, and Data region. Each entry in the FAT region records information about the corresponding cluster in the storage device. The cluster information includes whether the cluster is used, number of the next cluster of the file, whether the file ends with the cluster. The FAT file system supports multiple formats, such as FAT12, FAT16, and FAT32. The numbers 12, 16, and 32 indicate the number of bits per cluster within the FAT, respectively. The FAT file system supports multiple media, especially removable media \(such as USB flash drives, SD cards, and removable hard drives\). The FAT file system ensures good compatibility between embedded devices and desktop systems \(such as Windows and Linux\) and facilitates file management.
File Allocation Table (FAT) is a file system developed for personal computers. It consists of the DOS Boot Record (DBR) region, FAT region, and Data region. Each entry in the FAT region records information about the corresponding cluster in the storage device. The cluster information includes whether the cluster is used, number of the next cluster of the file, whether the file ends with the cluster.
The FAT file system supports multiple formats, such as FAT12, FAT16, and FAT32. The numbers 12, 16, and 32 indicate the number of bits per cluster within the FAT, respectively. The FAT file system supports multiple media, especially removable media (such as USB flash drives, SD cards, and removable hard drives). The FAT file system ensures good compatibility between embedded devices and desktop systems (such as Windows and Linux) and facilitates file management.
The OpenHarmony kernel supports FAT12, FAT16, and FAT32 file systems. These file systems require a tiny amount of code to implement, use less resources, support a variety of physical media, and are tailorable and compatible with Windows and Linux systems. They also support identification of multiple devices and partitions. The kernel supports multiple partitions on hard drives and allows creation of the FAT file system on the primary partition and logical partition.
## Development Guidelines
### Adaptation of Drivers
The use of the FAT file system requires support from the underlying MultiMediaCard \(MMC\) drivers. To run FatFS on a board with an MMC storage device, you must:
### Driver Adaptation
The use of the FAT file system requires support from the underlying MultiMedia Card (MMC) drivers. To run FatFS on a board with an MMC storage device, you must:
1. Implement the **disk\_status**, **disk\_initialize**, **disk\_read**, **disk\_write**, and **disk\_ioctl** APIs to adapt to the embedded MMC \(eMMC\) drivers on the board.
1. Implement the **disk_status**, **disk_initialize**, **disk_read**, **disk_write**, and **disk_ioctl** APIs to adapt to the embedded MMC (eMMC) drivers on the board.
2. Add the **fs_config.h** file with information such as **FS_MAX_SS** (maximum sector size of the storage device) and **FF_VOLUME_STRS** (partition names) configured.
The following is an example:
2. Add the **fs\_config.h** file with information such as **FS\_MAX\_SS**\(maximum sector size of the storage device\) and **FF\_VOLUME\_STRS**\(partition names\) configured. The following is an example:
>- Note the following when managing FatFS files and directories:
> - A file cannot exceed 4 GB.
> - **FAT\_MAX\_OPEN\_FILES** specifies the maximum number files you can open at a time, and **FAT\_MAX\_OPEN\_DIRS** specifies the maximum number of folders you can open at a time.
> - Root directory management is not supported. File and directory names start with the partition name. For example, **user/testfile** indicates the file or directory **testfile** in the **user** partition.
> - To open a file multiple times, use **O\_RDONLY** \(read-only mode\). **O\_RDWR** or **O\_WRONLY** \(writable mode\) can open a file only once.
> - The read and write pointers are not separated. If a file is open in **O\_APPEND** mode, the read pointer is also at the end of the file. If you want to read the file from the beginning, you must manually set the position of the read pointer.
> - File and directory permission management is not supported.
> - The **stat** and **fstat** APIs do not support query of the modification time, creation time, and last access time. The Microsoft FAT protocol does not support time before A.D. 1980.
>- Note the following when mounting and unmounting FatFS partitions:
> - Partitions can be mounted with the read-only attribute. When the input parameter of the **mount** function is **MS\_RDONLY**, all APIs with the write attribute, such as **write**, **mkdir**, **unlink**, and **open** with **non-O\_RDONLY** attributes, will be rejected.
> - You can use the **MS\_REMOUNT** flag with **mount** to modify the permission for a mounted partition.
> - Before unmounting a partition, ensure that all directories and files in the partition are closed.
> - You can use **umount2** with the **MNT\_FORCE** parameter to forcibly close all files and folders and unmount the partition. However, this may cause data loss. Therefore, exercise caution when running **umount2**.
>- The FAT file system supports re-partitioning and formatting of storage devices using **fatfs\_fdisk** and **fatfs\_format**.
> - If a partition is mounted before being formatted using **fatfs\_format**, you must close all directories and files in the partition and unmount the partition first.
> - Before calling **fatfs\_fdisk**, ensure that all partitions in the device are unmounted.
> - Using **fatfs\_fdisk** and **fatfs\_format** may cause data loss. Exercise caution when using them.
> Note the following when managing FatFS files and directories:
> - A file cannot exceed 4 GB.
> - **FAT\_MAX\_OPEN\_FILES** specifies the maximum number files you can open at a time, and **FAT\_MAX\_OPEN\_DIRS** specifies the maximum number of folders you can open at a time.
> - Root directory management is not supported. File and directory names start with the partition name. For example, **user/testfile** indicates the file or directory **testfile** in the **user** partition.
> - To open a file multiple times, use **O_RDONLY** (read-only mode). **O_RDWR** or **O_WRONLY** (writable mode) can open a file only once.
> - The read and write pointers are not separated. If a file is open in **O_APPEND** mode, the read pointer is also at the end of the file. If you want to read the file from the beginning, you must manually set the position of the read pointer.
> - File and directory permission management is not supported.
> - The **stat** and **fstat** APIs do not support query of the modification time, creation time, and last access time. The Microsoft FAT protocol does not support time before A.D. 1980.
>
> Note the following when mounting and unmounting FatFS partitions:
> - Partitions can be mounted with the read-only attribute. When the input parameter of the **mount** function is **MS_RDONLY**, all APIs with the write attribute, such as **write**, **mkdir**, **unlink**, and **open** with **non-O_RDONLY** attributes, will be rejected.
> - You can use the **MS_REMOUNT** flag with **mount** to modify the permission for a mounted partition.
> - Before unmounting a partition, ensure that all directories and files in the partition are closed.
> - You can use **umount2** with the **MNT_FORCE** parameter to forcibly close all files and folders and unmount the partition. However, this may cause data loss. Therefore, exercise caution when running **umount2**.
>
> The FAT file system supports re-partitioning and formatting of storage devices using **fatfs_fdisk** and **fatfs_format**.
> - If a partition is mounted before being formatted using **fatfs_format**, you must close all directories and files in the partition and unmount the partition first.
> - Before calling **fatfs_fdisk**, ensure that all partitions in the device are unmounted.
> - Using **fatfs_fdisk** and **fatfs_format** may cause data loss. Exercise caution when using them.
## Development Example
### Example Description
This example implements the following:
1. Create the **user/test** directory.
2. Create the **file.txt** file in the **user/test** directory.
3. Write "Hello OpenHarmony!" at the beginning of the file.
4. Save the update of the file to the device.
5. Set the offset to the beginning of the file.
6. Read the file.
7. Close the file.
8. Delete the file.
9. Delete the directory.
1. Create the **user/test** directory.
2. Create the **file.txt** file in the **user/test** directory.
3. Write **Hello OpenHarmony!** at the beginning of the file.
4. Save the file to a device.
5. Set the offset to the start position of the file.
6. Read the file.
7. Close the file.
8. Delete the file.
9. Delete the directory.
### Sample Code
Prerequisites
**Prerequisites**
-The MMC device partition is mounted to the **user** directory.
The MMC device partition is mounted to the **user** directory.
The sample code is as follows:
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "sys/stat.h"
#include "fcntl.h"
#include "unistd.h"
```
#include <stdio.h>
#include <string.h>
#include "sys/stat.h"
#include "fcntl.h"
#include "unistd.h"
#define LOS_OK 0
#define LOS_NOK -1
#define LOS_OK 0
#define LOS_NOK -1
int FatfsTest(void)
{
int FatfsTest(void)
{
int ret;
int fd = -1;
ssize_t len;
...
...
@@ -88,14 +102,14 @@ int FatfsTest(void)
char writeBuf[20] = "Hello OpenHarmony!";
char readBuf[20] = {0};
/* Create the user/test directory.*/
/* Create the user/test directory.*/
ret = mkdir(dirName, 0777);
if (ret != LOS_OK) {
printf("mkdir failed.\n");
return LOS_NOK;
}
/* Create the file user/test/file.txt and make it readable and writable.*/
/* Create a readable and writable file named file.txt in the user/test/ directory. */
fd = open(fileName, O_RDWR | O_CREAT, 0777);
if (fd < 0) {
printf("open file failed.\n");
...
...
@@ -109,21 +123,21 @@ int FatfsTest(void)
return LOS_NOK;
}
/* Save the update of the file to the device.*/
/* Save the file to a storage device. */
ret = fsync(fd);
if (ret != LOS_OK) {
printf("fsync failed.\n");
return LOS_NOK;
}
/* Move the read/write pointer to the file header. */
/* Move the read/write pointer to the beginning of the file. */
off = lseek(fd, 0, SEEK_SET);
if (off != 0) {
printf("lseek failed.\n");
return LOS_NOK;
}
/* Read the file content, with the same size as readBuf, to readBuf.*/
/* Read the file content with the length of readBuf to readBuf. */
len = read(fd, readBuf, sizeof(readBuf));
if (len != strlen(writeBuf)) {
printf("read file failed.\n");
...
...
@@ -138,14 +152,14 @@ int FatfsTest(void)
return LOS_NOK;
}
/*Delete the file user/test/file.txt.*/
/* Delete the file file.txt from the user/test directory. */
ret = unlink(fileName);
if (ret != LOS_OK) {
printf("unlink failed.\n");
return LOS_NOK;
}
/*Delete the user/test directory.*/
/* Delete the user/test directory. */
ret = rmdir(dirName);
if (ret != LOS_OK) {
printf("rmdir failed.\n");
...
...
@@ -153,14 +167,15 @@ int FatfsTest(void)
}
return LOS_OK;
}
```
}
```
### Verification
The development is successful if the return result is as follows:
LittleFS is a small file system designed for flash. By combining the log-structured file system and the copy-on-write \(COW\) file system, LittleFS stores metadata in log structure and data in the COW structure. This special storage empowers LittleFS high power-loss resilience. LittleFS uses the statistical wear leveling algorithm when allocating COW data blocks, effectively prolonging the service life of flash devices. LittleFS is designed for small-sized devices with limited resources, such as ROM and RAM. All RAM resources are allocated through a buffer with the fixed size \(configurable\). That is, the RAM usage does not grow with the file system.
LittleFS is a small file system designed for flash. By combining the log-structured file system and the copy-on-write (COW) file system, LittleFS stores metadata in log structure and data in the COW structure. This special storage empowers LittleFS high power-loss resilience. LittleFS uses the statistical wear leveling algorithm when allocating COW data blocks, effectively prolonging the service life of flash devices. LittleFS is designed for small-sized devices with limited resources, such as ROM and RAM. All RAM resources are allocated through a buffer with the fixed size (configurable). That is, the RAM usage does not grow with the file system.
LittleFS is a good choice when you look for a flash file system that is power-cut resilient and has wear leveling support on a small device with limited resources.
## Development Guidelines
When porting LittleFS to a new hardware device, you need to declare **lfs\_config**:
When porting LittleFS to a new hardware device, you need to declare **lfs_config**:
**.read**, **.prog**, **.erase**, and **.sync** correspond to the read, write, erase, and synchronization APIs at the bottom layer of the hardware platform, respectively.
**.read**, **.prog**, **.erase**, and **.sync** correspond to the read, write, erase, and synchronization APIs at the bottom layer of the hardware platform, respectively.
**read\_size** indicates the number of bytes read each time. You can set it to a value greater than the physical read unit to improve performance. This value determines the size of the read cache. However, if the value is too large, more memory is consumed.
**read_size** indicates the number of bytes read each time. You can set it to a value greater than the physical read unit to improve performance. This value determines the size of the read cache. However, if the value is too large, more memory is consumed.
**prog\_size** indicates the number of bytes written each time. You can set it to a value greater than the physical write unit to improve performance. This value determines the size of the write cache and must be an integral multiple of **read\_size**. However, if the value is too large, more memory is consumed.
**prog_size** indicates the number of bytes written each time. You can set it to a value greater than the physical write unit to improve performance. This value determines the size of the write cache and must be an integral multiple of **read_size**. However, if the value is too large, more memory is consumed.
**block\_size**: indicates the number of bytes in each erase block. The value can be greater than that of the physical erase unit. However, a smaller value is recommended because each file occupies at least one block. The value must be an integral multiple of **prog\_size**.
**block_size**: indicates the number of bytes in each erase block. The value can be greater than that of the physical erase unit. However, a smaller value is recommended because each file occupies at least one block. The value must be an integral multiple of **prog_size**.
**block\_count** indicates the number of blocks that can be erased, which depends on the capacity of the block device and the size of the block to be erased \(**block\_size**\).
**block_count** indicates the number of blocks that can be erased, which depends on the capacity of the block device and the size of the block to be erased (**block_size**).
## Sample Code
The sample code is as follows:
## Sample Code
The sample code is as follows:
```
#include "lfs.h"
#include "stdio.h"
...
...
@@ -89,11 +92,12 @@ int main(void) {
}
```
### Verification
**Verification**
The development is successful if the return result is as follows:
The OpenHarmony LiteOS-M kernel is a lightweight operating system \(OS\) kernel designed for the IoT field. It features small size, low power consumption, and high performance. The LiteOS-M kernel has simple code structure, including the minimum function set, kernel abstraction layer \(KAL\), optional components, and project directory. It supports the Hardware Driver Foundation \(HDF\), which provides unified driver standards and access mode for device vendors to simplify porting of drivers and allow one-time development for multi-device deployment.
The OpenHarmony LiteOS-M kernel is a lightweight operating system (OS) kernel designed for the IoT field. It features small size, low power consumption, and high performance. The LiteOS-M kernel has simple code structure, including the minimum function set, kernel abstraction layer (KAL), optional components, and project directory. It supports the Hardware Driver Foundation (HDF), which provides unified driver standards and access mode for device vendors to simplify porting of drivers and allow one-time development for multi-device deployment.
The OpenHarmony LiteOS-M kernel architecture consists of the hardware layer and hardware-irrelevant layers, as shown in the figure below. The hardware layer is classified based on the compiler toolchain and chip architecture, and provides a unified Hardware Abstraction Layer (HAL) interface to improve hardware adaptation and facilitate the expansion of various types of AIoT hardware and compilation toolchains. The other modules are irrelevant to the hardware. The basic kernel module provides basic kernel capabilities. The extended modules provide capabilities of components, such as the network and file systems, as well as exception handling and debug tools. The KAL provides unified standard APIs.
The OpenHarmony LiteOS-M kernel architecture consists of the hardware layer and hardware-irrelevant layers, as shown in the figure below. The hardware layer is classified based on the compiler toolchain and chip architecture, and provides a unified Hardware Abstraction Layer \(HAL\) interface to improve hardware adaptation and facilitate the expansion of various types of AIoT hardware and compilation toolchains. The other modules are irrelevant to the hardware. The basic kernel module provides basic kernel capabilities. The extended modules provide capabilities of components, such as the network and file systems, as well as exception handling and debug tools. The KAL provides unified standard APIs.
The CPU architecture includes two layers: general architecture definition layer and specific architecture definition layer. The former provides interfaces supported and implemented by all architectures. The latter is specific to an architecture. For a new architecture to be added, the general architecture definition layer must be implemented first and the architecture-specific functions can be implemented at the specific architecture definition layer.
LiteOS-M supports mainstream architectures, such as ARM Cortex-M3, ARM Cortex-M4, ARM Cortex-M7, ARM Cortex-M33, and RISC-V. If you need to expand the CPU architecture, see [Chip Architecture Adaptation](../porting/porting-chip-kernel-overview.md#section137431650339).
### Working Principles
Configure the system clock and number of ticks per second in the **target\_config.h** file of the development board. Configure the task, memory, inter-process communication \(IPC\), and exception handling modules based on service requirements. When the system boots, the modules are initialized based on the configuration. The kernel startup process includes peripheral initialization, system clock configuration, kernel initialization, and OS boot, as shown in the figure below.
LiteOS-M supports mainstream architectures, such as ARM Cortex-M3, ARM Cortex-M4, ARM Cortex-M7, ARM Cortex-M33, and RISC-V. If you need to expand the CPU architecture, see [Chip Architecture Adaptation](../porting/porting-chip-kernel-overview.md).
## Working Principles
In the **target\_config.h** file of the development board, configure the system clock and number of ticks per second, and configure the task, memory, inter-process communication (IPC), and exception handling modules based on service requirements. When the system boots, the modules are initialized based on the configuration. The kernel startup process includes peripheral initialization, system clock configuration, kernel initialization, and OS boot, as shown in the figure below.
Similar to a mutex, a read-write lock \(RW lock\) can be used to synchronize tasks in the same process. Different from a mutex, an RW lock allows concurrent access for read operations and exclusive access for write operations.
Similar to a mutex, a read-write lock (RW lock) can be used to synchronize tasks in the same process. Different from a mutex, an RW lock allows concurrent access for read operations and exclusive access for write operations.
An RW lock has three states: locked in read mode, locked in write mode, and unlocked.
Observe the following rules when using RW locks:
- If there is no lock in write mode in the protected area, any task can add a lock in read mode.
- A lock in write mode can be added only when the protected area is in the unlocked state.
- If there is no lock in write mode in the protected area, any task can add a lock in read mode.
- A lock in write mode can be added only when the protected area is in the unlocked state.
In a multi-task environment, multiple tasks may access the same shared resource. A lock in read mode allows access to a protected area in shared mode, and a lock in a write mode allows exclusive access to the shared resource.
This sharing-exclusive manner is suitable for a multi-task environment where the data read operations are far more than data write operations. It can improve multi-task concurrency of the application.
## Working Principles<a name="section1239111562720"></a>
## Working Principles
How does an RW lock implement lock in read mode and lock in write mode to control read/write access of multiple tasks?
- If task A acquires the lock in write mode for the first time, other tasks cannot acquire or attempt to acquire the lock in read mode.
- If task A acquires the lock in read mode, the RW lock count increments by 1 when a task acquires or attempts to acquire the lock in read mode.
## Development Guidelines<a name="section11643194275"></a>
### Available APIs<a name="section15335332122717"></a>
<tbody><trid="row37115291166"><tdclass="cellrowborder"rowspan="2"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.1 "><pid="p1795312108911"><aname="p1795312108911"></a><aname="p1795312108911"></a>Creating and deleting an RW lock</p>
<tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.3 "><pid="p171112291967"><aname="p171112291967"></a><aname="p171112291967"></a>Creates an RW lock.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p137111129965"><aname="p137111129965"></a><aname="p137111129965"></a>Deletes the specified RW lock.</p>
</td>
</tr>
<trid="row5711192912616"><tdclass="cellrowborder"rowspan="2"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.1 "><pid="p86087143910"><aname="p86087143910"></a><aname="p86087143910"></a>Requesting a lock in read mode</p>
<tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.3 "><pid="p1271110291969"><aname="p1271110291969"></a><aname="p1271110291969"></a>Requests the specified lock in read mode.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p107118291660"><aname="p107118291660"></a><aname="p107118291660"></a>Attempts to request the specified lock in read mode.</p>
</td>
</tr>
<trid="row189551130172817"><tdclass="cellrowborder"rowspan="2"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.1 "><pid="p7951153082815"><aname="p7951153082815"></a><aname="p7951153082815"></a>Requesting a lock in write mode</p>
<tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.3 "><pid="p11951183013281"><aname="p11951183013281"></a><aname="p11951183013281"></a>Requests the specified lock in write mode.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p5951123092819"><aname="p5951123092819"></a><aname="p5951123092819"></a>Attempts to request the specified lock in write mode.</p>
</td>
</tr>
<trid="row1642820328301"><tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.1 "><pid="p1542823210305"><aname="p1542823210305"></a><aname="p1542823210305"></a>Releasing an RW lock</p>
<tdclass="cellrowborder"valign="top"width="33.33333333333333%"headers="mcps1.2.4.1.3 "><pid="p43187342311"><aname="p43187342311"></a><aname="p43187342311"></a>Checks the validity of an RW lock.</p>
</td>
</tr>
</tbody>
</table>
### How to Develop<a name="section14774114882714"></a>
- If task A acquires the lock in write mode for the first time, other tasks cannot acquire or attempt to acquire the lock in read mode.
- If task A acquires the lock in read mode, the RW lock count increments by 1 when a task acquires or attempts to acquire the lock in read mode.
## Development Guidelines
### Available APIs
**Table 1** APIs of the RW lock module
| API| Description|
| -------- | -------- |
| LOS_RwlockInit| Creates an RW lock.|
| LOS_RwlockDestroy| Deletes an RW lock.|
| LOS_RwlockRdLock| Requests the specified lock in read mode.|
| LOS_RwlockTryRdLock| Attempts to request a lock in read mode.|
| LOS_RwlockWrLock| Requests the specified lock in write mode.|
| LOS_RwlockTryWrLock| Attempts to request a lock in write mode.|
| LOS_RwlockUnLock| Releases the specified RW lock.|
| LOS_RwlockIsValid| Checks the validity of an RW lock.|
### How to Develop
The typical development process is as follows:
1. Call **LOS\_RwlockInit** to create an RW lock.
1. Call **LOS_RwlockInit** to create an RW lock.
2. Call **LOS\_RwlockRdLock** to request a lock in read mode or call **LOS\_RwlockWrLock** to request a lock in write mode.
2. Call **LOS_RwlockRdLock** to request a lock in read mode or call **LOS_RwlockWrLock** to request a lock in write mode.
If a lock in read mode is requested:
If a lock in read mode is requested:
- If the lock is not held, the read task can acquire the lock.
- If the lock is held, the read task acquires the lock and is executed based on the task priority.
- If the lock in write mode is held by another task, the task cannot acquire the lock until the lock in write mode is released.
- If the lock is not held, the read task can acquire the lock.
If a lock in write mode is requested:
- If the lock is held, the read task acquires the lock and is executed based on the task priority.
- If the lock is not held or if the task that holds the lock in read mode is the one that requests the lock in write mode, the task acquires the lock in write mode immediately.
- If the lock already has a lock in read mode and the read task has a higher priority, the current task is suspended until the lock in read mode is released.
- If the lock in write mode is held by another task, the task cannot acquire the lock until the lock in write mode is released.
3. There are three types of locks in read mode and write mode: non-block mode, permanent block mode, and scheduled block mode. The difference lies in the task suspension time.
If a lock in write mode is requested:
- If the lock is not held or if the task that holds the lock in read mode is the one that requests the lock in write mode, the task acquires the lock in write mode immediately.
4. Call **LOS\_RwlockUnLock** to release an RW lock.
- If the lock already has a lock in read mode and the read task has a higher priority, the current task is suspended until the lock in read mode is released.
- If tasks are blocked by the specified RW lock, the task with the highest priority is woken up, enters the Ready state, and is scheduled.
3. There are three types of locks in read mode and write mode: non-block mode, permanent block mode, and scheduled block mode. The difference lies in the task suspension time.
- If no task is blocked by the specified RW lock, the RW lock is released.
4. Call **LOS_RwlockUnLock** to release an RW lock.
5. Call **LOS\_RwlockDestroy** to delete an RW lock.
- If tasks are blocked by the specified RW lock, the task with the highest priority is woken up, enters the Ready state, and is scheduled.
- If no task is blocked by the specified RW lock, the RW lock is released.
>- The RW lock cannot be used in the interrupt service program.
>- When using the LiteOS-A kernel, the OpenHarmony must ensure real-time task scheduling and avoid long-time task blocking. Therefore, an RW lock must be released as soon as possible after use.
>- When an RW lock is held by a task, the task priority cannot be changed by using APIs such as **LOS\_TaskPriSet**.
5. Call **LOS_RwlockDestroy** to delete an RW lock.
> - The RW lock cannot be used in the interrupt service program.
>
> - The LiteOS-A kernel used in the RTOS must ensure real-time task scheduling and avoid long-time task blocking. Therefore, RW locks must be released as soon as possible after use.
>
> - When an RW lock is held by a task, the task priority cannot be changed by using APIs, such as **LOS_TaskPriSet**.
File Allocation Table \(FAT\) is a file system developed for personal computers. It consists of the DOS Boot Record \(DBR\) region, FAT region, and Data region. Each entry in the FAT region records information about the corresponding cluster in the storage device. The cluster information includes whether the cluster is used, the number of the next cluster of the file, and whether the file ends with the cluster. The FAT file system supports multiple formats, such as FAT12, FAT16, and FAT32. The numbers 12, 16, and 32 indicate the number of bits per cluster within the FAT, and also restrict the maximum file size in the system. The FAT file system supports multiple media, especially removable media \(such as USB flash drives, SD cards, and removable hard drives\). The FAT file system ensures good compatibility between embedded devices and desktop systems \(such as Windows and Linux\) and facilitates file management.
File Allocation Table (FAT) is a file system developed for personal computers. It consists of the DOS Boot Record (DBR) region, FAT region, and Data region. Each entry in the FAT region records information about the corresponding cluster in the storage device. The cluster information includes whether the cluster is used, number of the next cluster of the file, whether the file ends with the cluster. The FAT file system supports multiple formats, such as FAT12, FAT16, and FAT32. The numbers 12, 16, and 32 indicate the number of bits per cluster within the FAT, and also restrict the maximum file size in the system. The FAT file system supports multiple media, especially removable media (such as USB flash drives, SD cards, and removable hard drives). The FAT file system ensures good compatibility between embedded devices and desktop systems (such as Windows and Linux) and facilitates file management.
The OpenHarmony kernel supports FAT12, FAT16, and FAT32 file systems. These file systems require a tiny amount of code to implement, use less resources, support a variety of physical media, and are tailorable and compatible with Windows and Linux systems. They also support identification of multiple devices and partitions. The kernel supports multiple partitions on hard drives and allows creation of the FAT file system on the primary partition and logical partition.
## Working Principles<a name="section10796155213381"></a>
## Working Principles
This document does not include the FAT design and physical layout. You can find a lot of reference on the Internet.
The OpenHarmony LiteOS-A kernel uses block cache \(Bcache\) to improve FAT performance. When read and write operations are performed, Bcache caches the sectors close to the read and write sectors to reduce the number of I/Os and improve performance. The basic cache unit of Bcache is block. The size of each block is the same. By default, there are 28 blocks, and each block caches data of 64 sectors. When the Bcache dirty block rate \(number of dirty sectors/total number of sectors\) reaches the threshold, writeback is triggered and cached data is written back to disks. You can manually call **sync** and **fsync** to write data to disks if you want. Some FAT APIs \(such as **close** and **umount**\) may also trigger writeback operations. However, you are advised not to use them to trigger writeback.
The OpenHarmony LiteOS-A kernel uses block cache (Bcache) to improve FAT performance. When read and write operations are performed, Bcache caches the sectors close to the read and write sectors to reduce the number of I/Os and improve performance. The basic cache unit of Bcache is block. The size of each block is the same. By default, there are 28 blocks, and each block caches data of 64 sectors. When the Bcache dirty block rate (number of dirty sectors/total number of sectors) reaches the threshold, writeback is triggered and cached data is written back to disks. You can manually call **sync** and **fsync** to write data to disks if you want. Some FAT APIs (such as **close** and **umount**) may also trigger writeback operations. However, you are advised not to use them to trigger writeback.
## Development Guidelines<a name="section144094483919"></a>
## Development Guidelines
### How to Develop<a name="section139086116394"></a>
**How to Develop**
The development process involves mounting partitions, managing files and directories, and unmounting partitions.
The device name of the SD card or MMC is **mmcblk\[x\]p\[y\]**, and the file system type is **vfat**.
The device name of the SD card or MMC is **mmcblk[x]p[y]**, and the file system type is**vfat**.
>- The size of a single FAT file cannot be greater than 4 GiB.
>- When there are two SD card slots, the first card inserted is card 0, and that inserted later is card 1.
>- When multi-partition is enabled and there are multiple partitions, the device node **/dev/mmcblk0** \(primary device\) registered by card 0 and **/dev/mmcblk0p0** \(secondary device\) are the same device. In this case, you cannot perform operations on the primary device.
>- Before removing an SD card, close the open files and directories and unmount the related nodes. Otherwise, SD card exceptions or memory leaks may occur.
>- Before performing the **format** operation, unmount the mount point.
>- After the Bcache feature takes effect, note the following:
> - When **MS\_NOSYNC** is carried in the **mount** function, FAT does not proactively write the content in the cache back to the storage device. The FAT-related APIs **open**, **close**, **unlink**, **rename**, **mkdir**, **rmdir**, and **truncate** do not automatically perform the **sync** operation, which improves the operation speed. However, the upper layer must actively invoke the **sync** operation to synchronize data. Otherwise, data loss may occur.
> - Bcache provides scheduled writeback. After **LOSCFG\_FS\_FAT\_CACHE\_SYNC\_THREAD** is enabled in **menuconfig**, the OpenHarmony kernel creates a scheduled task to write the Bcache data back to disks. By default, the kernel checks the dirty block rate in the Bcache every 5 seconds. If the dirty block rate exceeds 80%, the **sync** operation will be performed to write all dirty data in the Bcache to disks. You can call **LOS\_SetSyncThreadPrio**, **LOS\_SetSyncThreadInterval**, and **LOS\_SetDirtyRatioThreshold** to set the task priority, flush interval, and dirty block rate threshold, respectively.
> - The cache has 28 blocks by default, and each block has 64 sectors.
> - The size of a single FAT file cannot be greater than 4 GiB.
>
> - When there are two SD card slots, the first card inserted is card 0, and that inserted later is card 1.
>
> - When multi-partition is enabled and there are multiple partitions, the device node **/dev/mmcblk0** (primary device) registered by card 0 and **/dev/mmcblk0p0** (secondary device) are the same device. In this case, you cannot perform operations on the primary device.
>
> - Before removing an SD card, close the open files and directories and unmount the related nodes. Otherwise, SD card exceptions or memory leaks may occur.
>
> - Before performing the **format** operation, unmount the mount point.
>
> - After the Bcache feature takes effect, note the following:
> - When **MS_NOSYNC** is carried in the **mount** function, FAT does not proactively write the content in the cache back to the storage device. The FAT-related APIs **open**, **close**, **unlink**, **rename**, **mkdir**, **rmdir**, and **truncate** do not automatically perform the **sync** operation, which improves the operation speed. However, the upper layer must actively invoke the **sync** operation to synchronize data. Otherwise, data loss may occur.
>
> - Bcache provides scheduled writeback. After **LOSCFG_FS_FAT_CACHE_SYNC_THREAD** is enabled in **menuconfig**, the OpenHarmony kernel creates a scheduled task to write the Bcache data back to disks. By default, the kernel checks the dirty block rate in the Bcache every 5 seconds. If the dirty block rate exceeds 80%, the **sync** operation will be performed to write all dirty data in the Bcache to disks. You can call **LOS_SetSyncThreadPrio**, **LOS_SetSyncThreadInterval**, and **LOS_SetDirtyRatioThreshold** to set the task priority, flush interval, and dirty block rate threshold, respectively.
> - The cache has 28 blocks by default, and each block has 64 sectors.
NFS allows you to share files across hosts and OSs over a network. You can treat NFS as a file system service, which is equivalent to folder sharing in the Windows OS to some extent.
Network File System (NFS) allows you to share files across hosts and OSs over a network. You can treat NFS as a file system service, which is equivalent to folder sharing in the Windows OS to some extent.
## Working Principles<a name="section165621321194618"></a>
The NFS of the OpenHarmony LiteOS-A kernel acts as an NFS client. The NFS client can mount the directory shared by a remote NFS server to the local machine and run the programs and shared files without occupying the storage space of the current system. To the local machine, the directory on the remote server is like its disk.
## Development Guidelines<a name="section7454935184611"></a>
1. Create an NFS server.
The following uses the Ubuntu OS as an example to describe how to configure an NFS server.
- Install the NFS server software.
Set the download source of the Ubuntu OS when the network connection is normal.
```
sudo apt-get install nfs-kernel-server
```
## Working Principles
- Create a directory for mounting and assign full permissions for the directory.
```
mkdir -p /home/sqbin/nfs
sudo chmod 777 /home/sqbin/nfs
```
- Configure and start the NFS server.
Append the following line in the **/etc/exports** file:
```
/home/sqbin/nfs *(rw,no_root_squash,async)
```
**/home/sqbin/nfs** is the root directory shared by the NFS server.
Start the NFS server.
The NFS of the OpenHarmony LiteOS-A kernel acts as an NFS client. The NFS client can mount the directory shared by a remote NFS server to the local machine and run the programs and shared files without occupying the storage space of the current system. To the local machine, the directory on the remote server is like its disk.
```
sudo /etc/init.d/nfs-kernel-server start
```
Restart the NFS server.
## Development Guidelines
```
sudo /etc/init.d/nfs-kernel-server restart
```
1. Create an NFS server.
1. Configure the board as an NFS client.
The following uses the Ubuntu OS as an example to describe how to configure an NFS server.
In this section, the NFS client is a device running the OpenHarmony kernel.
- Install the NFS server software.
- Set the hardware connection.
Set the download source of the Ubuntu OS when the network connection is normal.
Connect the OpenHarmony kernel device to the NFS server. Set their IP addresses in the same network segment. For example, set the IP address of the NFS server to **10.67.212.178/24** and set the IP address of the OpenHarmony kernel device to **10.67.212.3/24**. Note that the preceding IP addresses are private IP addresses used as examples. You need to use your actual IP addresses.
```
sudo apt-get install nfs-kernel-server
```
You can run the **ifconfig** command to check the OpenHarmony kernel device's IP address.
- Create a directory for mounting and assign full permissions for the directory.
- Start the network and ensure that the network between the board and NFS server is normal.
```
mkdir -p /home/sqbin/nfs
sudo chmod 777 /home/sqbin/nfs
```
Start the Ethernet or another type of network, and then run **ping** to check whether the network connection to the server is normal.
- Configure and start the NFS server.
```
OHOS # ping 10.67.212.178
[0]Reply from 10.67.212.178: time=1ms TTL=63
[1]Reply from 10.67.212.178: time=0ms TTL=63
[2]Reply from 10.67.212.178: time=1ms TTL=63
[3]Reply from 10.67.212.178: time=1ms TTL=63
--- 10.67.212.178 ping statistics ---
4 packets transmitted, 4 received, 0 loss
```
Append the following line in the **/etc/exports** file:
Initialize the NFS client.
```
/home/sqbin/nfs *(rw,no_root_squash,async)
```
**/home/sqbin/nfs** is the root directory shared by the NFS server.
Start the NFS server.
```
OHOS # mkdir /nfs
OHOS # mount 10.67.212.178:/home/sqbin/nfs /nfs nfs 1011 1000
```
```
sudo /etc/init.d/nfs-kernel-server start
```
Restart the NFS server.
If the following information is displayed, the NFS client is initialized.
```
sudo /etc/init.d/nfs-kernel-server restart
```
```
OHOS # mount 10.67.212.178:/home/sqbin/nfs /nfs nfs 1011 1000
Mount nfs on 10.67.212.178:/home/sqbin/nfs, uid:1011, gid:1000
Mount nfs finished.
```
2. Configure the board as an NFS client.
This command mounts the **/home/sqbin/nfs** directory on the NFS server whose IP address is 10.67.212.178 to the **/nfs** directory on the OpenHarmony kernel device.
In this section, the NFS client is a device running the OpenHarmony kernel.
>This section assumes that the NFS server is available, that is, the **/home/sqbin/nfs** directory on the NFS server 10.67.212.178 is accessible.
>The **mount** command format is as follows:
>```
>mount <SERVER_IP:SERVER_PATH> <CLIENT_PATH> nfs
>```
>- **SERVER\_IP** indicates the IP address of the server.
>- **SERVER\_PATH** indicates the path of the shared directory on the NFS server.
>- **CLIENT\_PATH** indicates the NFS path on the local device.
>- **nfs** indicates the path to which the remote shared directory is mounted on the local device.
>Replace the parameters as required.
>If you do not want to restrict the NFS access permission, set the permission of the NFS root directory to **777** on the Linux CLI.
>```
>chmod -R 777 /home/sqbin/nfs
>```
>The NFS client setting is complete, and the NFS file system has been mounted.
- Set the hardware connection.
1. Share files using NFS.
Connect the OpenHarmony kernel device to the NFS server. Set their IP addresses in the same network segment. For example, set the IP address of the NFS server to **10.67.212.178/24** and the IP address of the OpenHarmony kernel device to
**10.67.212.3/24**. Note that this IP address is an intranet private IP address. Use the actual IP address.
Create the **dir** directory on the NFS server and save the directory. Run the **ls** command in the OpenHarmony kernel.
You can run the **ifconfig** command to check the OpenHarmony kernel device's IP address.
```
OHOS # ls /nfs
```
- Start the network and ensure that the network between the board and NFS server is normal.
The following information is returned from the serial port:
Start the Ethernet or another type of network, and then run **ping** to check whether the network connection to the server is normal.
```
OHOS # ls /nfs
Directory /nfs:
drwxr-xr-x 0 u:0 g:0 dir
```
The **dir** directory created on the NFS server has been synchronized to the **/nfs** directory on the client \(OpenHarmony kernel system\).
```
OHOS # ping 10.67.212.178
[0]Reply from 10.67.212.178: time=1ms TTL=63
[1]Reply from 10.67.212.178: time=0ms TTL=63
[2]Reply from 10.67.212.178: time=1ms TTL=63
[3]Reply from 10.67.212.178: time=1ms TTL=63
--- 10.67.212.178 ping statistics ---
4 packets transmitted, 4 received, 0 loss
```
Initialize the NFS client.
Similarly, you can create files and directories on the client \(OpenHarmony kernel system\) and access them from the NFS server.
```
OHOS # mkdir /nfs
OHOS # mount 10.67.212.178:/home/sqbin/nfs /nfs nfs 1011 1000
```
If the following information is displayed, the NFS client is initialized.
>Currently, the NFS client supports some NFS v3 specifications. Therefore, the NFS client is not fully compatible with all types of NFS servers. You are advised to use the Linux NFS server to perform the development.
```
OHOS # mount 10.67.212.178:/home/sqbin/nfs /nfs nfs 1011 1000
Mount nfs on 10.67.212.178:/home/sqbin/nfs, uid:1011, gid:1000
Mount nfs finished.
```
This command mounts the **/home/sqbin/nfs** directory on the NFS server (IP address: 10.67.212.178) to the **/nfs** directory on the OpenHarmony kernel device.
> This example assumes that the NFS server is available, that is, the **/home/sqbin/nfs** directory on the NFS server 10.67.212.178 is accessible.
>
> The **mount** command format is as follows:
>
> ```
> mount <SERVER_IP:SERVER_PATH> <CLIENT_PATH> nfs
> ```
>
> **SERVER_IP** indicates the IP address of the server; **SERVER_PATH** indicates the path of the shared directory on the NFS server; **CLIENT_PATH** indicates the NFS path on the local device; **nfs** indicates the path to which the remote shared directory is mounted on the local device. Replace the parameters as required.
>
> If you do not want to restrict the NFS access permission, set the permission of the NFS root directory to **777** on the Linux CLI.
>
> ```
> chmod -R 777 /home/sqbin/nfs
> ```
>
> The NFS client setting is complete, and the NFS file system is mounted.
3. Share files using NFS.
Create the **dir** directory on the NFS server. Run the **ls** command in the OpenHarmony kernel.
```
OHOS # ls /nfs
```
The following information is returned from the serial port:
```
OHOS # ls /nfs
Directory /nfs:
drwxr-xr-x 0 u:0 g:0 dir
```
The **dir** directory created on the NFS server has been synchronized to the **/nfs** directory on the client (OpenHarmony kernel system). Similarly, you can create files and directories on the client (OpenHarmony kernel system) and access them from the NFS server.
> Currently, the NFS client supports some NFS v3 specifications. Therefore, the NFS client is not fully compatible with all types of NFS servers. You are advised to use the Linux NFS server to perform the development.
The proc filesystem \(procfs\) is a virtual file system that displays process or other system information in a file-like structure. It is more convenient to obtain system information in file operation mode than API calling mode.
The proc filesystem (procfs) is a virtual file system that displays process or other system information in a file-like structure. It is more convenient to obtain system information in file operation mode than API calling mode.
## Working Principles<a name="section479762916408"></a>
In the OpenHarmony kernel, procfs is automatically mounted to the **/proc** directory during startup. Only the kernel module can create file nodes to provide the query service.
## Working Principles
## Development Guidelines<a name="section1221174524014"></a>
In the OpenHarmony kernel, procfs is automatically mounted to the **/proc** directory during startup. Only the kernel module can create file nodes to provide the query service.
To create a procfs file, you need to use **ProcMkdir** to create a directory and use **CreateProcEntry** to create a file. The development of the file node function is to hook the read and write functions to the file created by **CreateProcEntry**. When the procfs file is read or written, the hooked functions will be called to implement custom functions.
### Development Example<a name="section52016575401"></a>
## Development Guidelines
The following describes how to create the **/proc/hello/world** file to implement the following functions:
To create a procfs file, you need to use **ProcMkdir** to create a directory and use **CreateProcEntry** to create a file. The development of the file node function is to hook the read and write functions to the file created by **CreateProcEntry**. When the procfs file is read or written, the hooked functions will be called to implement custom functions.
1. Create a file in **/proc/hello/world**.
### Development Example
The following describes how to create the **/proc/hello/world** file to implement the following functions:
1. Create a file in **/proc/hello/world**.
2. Read the file. When the file is read, "HelloWorld!" is returned.
3. Write the file and print the data written in the file.
Ramfs is a RAM-based file system whose size can be dynamically adjusted. Ramfs does not have a backing store. Directory entries and page caches are allocated when files are written into ramfs. However, data is not written back to any other storage medium. This means that data will be lost after a power outage.
## Working Principles<a name="section1859711263447"></a>
Ramfs stores all files in RAM, and read/write operations are performed in RAM. Ramfs is generally used to store temporary data or data that needs to be frequently modified, such as the **/tmp** and **/var** directories. Using ramfs reduces the read/write loss of the memory and improves the data read/write speed.
## Development Guidelines<a name="section163554380448"></a>
## Basic Concepts
Ramfs is a RAM-based file system whose size can be dynamically adjusted. Ramfs does not have a backing store. Directory entries and page caches are allocated when files are written into ramfs. However, data is not written back to any other storage medium. This means that data will be lost after a power outage.
## Working Principles
Ramfs stores all files in RAM, and read/write operations are performed in RAM. Ramfs is generally used to store temporary data or data that needs to be frequently modified, such as the **/tmp** and **/var** directories. Using ramfs reduces the read/write loss of the memory and improves the data read/write speed.
## Development Guidelines<a name="section13408945163812"></a>
# Shell Command Development
You can perform the following operations to add shell commands:
1. Include the following header files:
```
#include "shell.h"
#include "shcmd.h"
```
2. Register commands. You can register commands either statically or dynamically when the system is running. In most cases, static registration is widely used by common system commands, and dynamic registration is widely used by user commands.
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p998573514457"><a name="p998573514457"></a><a name="p998573514457"></a>Specifies the global variable name passed in static registration. Note that the name cannot be the same as other symbol names in the system.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p119859355458"><a name="p119859355458"></a><a name="p119859355458"></a>Specifies the command type, which can be any of the following:</p>
<a name="ul11135144114816"></a><a name="ul11135144114816"></a><ul id="ul11135144114816"><li><p id="p21351144488"><a name="p21351144488"></a><a name="p21351144488"></a><strong id="b189416548121"><a name="b189416548121"></a><a name="b189416548121"></a>CMD_TYPE_EX</strong>: does not support standard command parameters and will mask the command keywords you entered. For example, if you enter <strong id="b1940315357339"><a name="b1940315357339"></a><a name="b1940315357339"></a>ls /ramfs</strong>, only <strong id="b67371541123316"><a name="b67371541123316"></a><a name="b67371541123316"></a>/ramfs</strong> will be passed to the registration function, and <strong id="b1145320313411"><a name="b1145320313411"></a><a name="b1145320313411"></a>ls</strong> will be masked.</p>
</li><li><p id="p21353410482"><a name="p21353410482"></a><a name="p21353410482"></a><strong id="b15922145203418"><a name="b15922145203418"></a><a name="b15922145203418"></a>CMD_TYPE_STD</strong>: supports standard command parameters. All the characters you entered will be passed to the registration function after being parsed.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p11986735144514"><a name="p11986735144514"></a><a name="p11986735144514"></a>Specifies the command keyword, which is the name used to access a shell function.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p11986535144516"><a name="p11986535144516"></a><a name="p11986535144516"></a>Specifies the maximum number of input parameters in the execution function to be called. This parameter is not supported currently.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p398683574515"><a name="p398683574515"></a><a name="p398683574515"></a>Specifies the address of the execution function, that is, the function executed by the command.</p>
2. Add options to the **build/mk/liteos\_tables\_ldflags.mk** file.
For example, when registering the **ls** command, add **-uls\_shellcmd** to the **build/mk/liteos\_tables\_ldflags.mk** file. **-u** is followed by the first parameter of **SHELLCMD\_ENTRY**.
2. Dynamic registration:
The prototype of the function to register is as follows:
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p174461339496"><a name="p174461339496"></a><a name="p174461339496"></a>Specifies the command type, which can be any of the following:</p>
<a name="ul1244773317496"></a><a name="ul1244773317496"></a><ul id="ul1244773317496"><li><p id="p1844719331495"><a name="p1844719331495"></a><a name="p1844719331495"></a><strong id="b1596594718"><a name="b1596594718"></a><a name="b1596594718"></a>CMD_TYPE_EX</strong>: does not support standard command parameters and will mask the command keywords you entered. For example, if you enter <strong id="b134411664"><a name="b134411664"></a><a name="b134411664"></a>ls /ramfs</strong>, only <strong id="b384234630"><a name="b384234630"></a><a name="b384234630"></a>/ramfs</strong> will be passed to the registration function, and <strong id="b1102747310"><a name="b1102747310"></a><a name="b1102747310"></a>ls</strong> will be masked.</p>
</li><li><p id="p20447143315498"><a name="p20447143315498"></a><a name="p20447143315498"></a><strong id="b97295345"><a name="b97295345"></a><a name="b97295345"></a>CMD_TYPE_STD</strong>: supports standard command parameters. All the characters you entered will be passed to the registration function after being parsed.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p18447833124914"><a name="p18447833124914"></a><a name="p18447833124914"></a>Specifies the command keyword, which is the name used to access a shell function.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p8447233184914"><a name="p8447233184914"></a><a name="p8447233184914"></a>Specifies the maximum number of input parameters in the execution function to be called. This parameter is not supported currently. The default value is <strong id="b1425195414484"><a name="b1425195414484"></a><a name="b1425195414484"></a>XARGS(0xFFFFFFFF)</strong>.</p>
<td class="cellrowborder" valign="top" width="79.09%" headers="mcps1.2.3.1.2 "><p id="p44471533184912"><a name="p44471533184912"></a><a name="p44471533184912"></a>Specifies the address of the execution function, that is, the function executed by the command.</p>
>The command keyword must be unique. That is, two different commands cannot share the same command keyword. Otherwise, only one command is executed.
>When executing user commands sharing the same keyword, the shell executes only the first command in the **help** commands.
```
#include "shell.h"
#include "shcmd.h"
```
3. Use the following function prototype to add built-in commands:
2. Register commands.
```
UINT32 osShellCmdLs(UINT32 argc, CHAR **argv)
```
You can register commands either statically or dynamically (with the system running). Generally, common system commands are registered statically, and user commands are registered dynamically.
<td class="cellrowborder" valign="top" width="75.58%" headers="mcps1.2.3.1.2 "><p id="p1844625885112"><a name="p1844625885112"></a><a name="p1844625885112"></a>Specifies the number of parameters in the shell command.</p>
<td class="cellrowborder" valign="top" width="75.58%" headers="mcps1.2.3.1.2 "><p id="p11446958105119"><a name="p11446958105119"></a><a name="p11446958105119"></a>Specifies a pointer array, where each element points to a string. You can determine whether to pass the command keyword to the registration function by specifying the command type.</p>
</td>
</tr>
</tbody>
</table>
1. Register a command using a macro.
4. Enter the shell command in either of the following methods:
- Enter the shell command in a serial port tool.
The prototype of the macro is as follows:
- Enter the shell command in the Telnet tool. For details, see [telnet](kernel-small-debug-shell-net-telnet.md).
| l | Specifies the global variable name passed in static registration. Note that the name cannot be the same as other symbol names in the system.|
| cmdType | Specifies the command type, which can be any of the following:<br>**CMD_TYPE_EX**: does not support standard command parameters and will mask the command keywords you entered. For example, if you enter **ls /ramfs**, only **/ramfs** will be passed to the registration function and **ls** will be masked.<br>**CMD_TYPE_STD**: supports standard command parameters. All the characters you entered will be passed to the registration function after being parsed. |
| cmdKey | Specifies the command keyword, which is the name used to access a shell function.|
| paraNum | Specifies the maximum number of input parameters in the execution function to be called. This parameter is not supported currently.|
| cmdHook | Specifies the address of the execution function, that is, the function executed by the command.|
2. Add options to the **build/mk/liteos_tables_ldflags.mk** file.
For example, when registering the **ls** command, add **-uls_shellcmd** to the **build/mk/liteos_tables_ldflags.mk** file. **-u** is followed by the first parameter of **SHELLCMD_ENTRY**.
- Dynamic registration
The prototype of the function to register is as follows:
| cmdType | Specifies the command type, which can be any of the following:<br>**CMD_TYPE_EX**: does not support standard command parameters and will mask the command keywords you entered. For example, if you enter **ls /ramfs**, only **/ramfs** will be passed to the registration function, and **ls** will be masked.<br>**CMD_TYPE_STD**: supports standard command parameters. All the characters you entered will be passed to the registration function after being parsed.|
| cmdKey | Specifies the command keyword, which is the name used to access a shell function.|
| paraNum | Specifies the maximum number of input parameters in the execution function to be called. This parameter is not supported currently. The default value is **XARGS(0xFFFFFFFF)**.|
| cmdHook | Specifies the address of the execution function, that is, the function executed by the command.|
> The command keyword must be unique. That is, two different commands cannot share the same command keyword. Otherwise, only one command is executed. When executing user commands sharing the same keyword, the shell executes only the first command in the **help** commands.
3. Use the following function prototype to add built-in commands:
```
UINT32 osShellCmdLs(UINT32 argc, CHAR **argv)
```
**Table 3** osShellCmdLs parameters
| Parameter| Description|
| -------- | -------- |
| argc | Specifies the number of parameters in the shell command.|
| argv | Specifies a pointer array, where each element points to a string. You can determine whether to pass the command keyword to the registration function by specifying the command type.|
4. Enter the shell command in either of the following methods:
- Enter the shell command using a serial port tool.
- Enter the shell command using the Telnet tool. For details about how to use Telnet, see [telnet](../kernel/kernel-small-debug-shell-net-telnet.md).
When the system does not respond, you can use the magic key to check whether the system is locked and interrupted \(the magic key also does not respond\) or view the system task running status.
When the system does not respond, you can use the magic key function to check whether the system is suspended by an interrupt lock (the magic key also does not respond) or view the system task running status.
If an interrupt is responded, you can use the magic key to check the task CPU usage \(**cpup**\) and find out the task with the highest CPU usage. Generally, the task with a higher priority preempts the CPU resources.
If interrupts are responded, you can use the magic key to check the task CPU usage (**cpup**) and find out the task with the highest CPU usage. Generally, the task with a higher priority preempts the CPU resources.
## How to Use<a name="section3305151511559"></a>
1. Configure the macro **LOSCFG\_ENABLE\_MAGICKEY**.
## How to Use
The magic key depends on the **LOSCFG\_ENABLE\_MAGICKEY** macro. Before using the magic key, select **Enable MAGIC KEY** on **menuconfig**.
1. Configure the macro **LOSCFG_ENABLE_MAGICKEY**.
The magic key depends on the **LOSCFG_ENABLE_MAGICKEY** macro. Before using the magic key, select **Enable MAGIC KEY** (**Debug** ---> **Enable MAGIC KEY**) on **menuconfig**. The magic key cannot be used if this option is disabled.
The magic key cannot be used if this macro is disabled.
>On **menuconfig**, you can move the cursor to **LOSCFG\_ENABLE\_MAGICKEY** and type a question mark \(?\) to view help information.
When the UART or USB-to-virtual serial port is connected, press **Ctrl+R**. If "Magic key on" is displayed, the magic key is enabled. To disable it, press **Ctrl+R** again. If "Magic key off" is displayed, the magic key is disabled.
2. Press **Ctrl+R** to enable the magic key.
The functions of the magic key are as follows:
When the UART or USB-to-virtual serial port is connected, press **Ctrl+R**. If "Magic key on" is displayed, the magic key is enabled.
-**Ctrl+Z**: displays help information about the related magic keys.
To disable the magic key, press **Ctrl+R** again. If "Magic key off" is displayed, the magic key is disabled.
-**Ctrl+T**: displays task information.
You can use the magic key combinations as follows:
-**Ctrl+Z**: displays help information about the related magic keys.
-**Ctrl+T**: displays task information.
-**Ctrl+P**: allows the system to proactively enter the panic state. After the panic-related information is printed, the system is suspended.
-**Ctrl+E**: Checks the integrity of the memory pool. If an error is detected, the system displays an error message. If no error is detected, the system displays "system memcheck over, all passed!".
>If magic key is enabled, when special characters need to be entered through the UART or USB-to-virtual serial port, avoid using characters the same as the magic keys. Otherwise, the magic key may be triggered by mistake, causing errors in the original design.
-**Ctrl+P**: allows the system to proactively enter the panic state. After the panic-related information is printed, the system is suspended.
-**Ctrl+E**: Checks the integrity of the memory pool. If an error is detected, the system displays an error message. If no error is detected, the system displays "system memcheck over, all passed!".
> If magic key is enabled, when special characters need to be entered through the UART or USB-to-virtual serial port, avoid using characters the same as the magic keys. Otherwise, the magic key may be triggered by mistake, causing errors in the original design.
The shell provided by the OpenHarmony kernel supports commonly used debugging commands. You can also add and customize commands to the shell of the OpenHarmony kernel to address your service needs. The common debugging commands include the following:
- System commands: commands used to query information, such as system tasks, semaphores, system software timers, CPU usage, and interrupts.
- File commands: commands used to manage files and directories, such as **ls** and **cd**.
-System commands: commands used to query information, such as system tasks, semaphores, system software timers, CPU usage, and interrupts.
- Network commands: commands used to query the IP addresses of other devices connected to the development board, querying the IP address of the local device, testing network connectivity, and setting the access point \(AP\) and station \(STA\) modes of the development board.
-File commands: commands used to manage files and directories, such as **ls** and **cd**.
For details about how to add a command, see [Shell Command Development Guidelines](kernel-small-debug-shell-guide.md) and [Shell Command Programming Example](kernel-small-debug-shell-build.md).
- Network commands: commands used to query the IP addresses of other devices connected to the development board, querying the IP address of the local device, testing network connectivity, and setting the access point (AP) and station (STA) modes of the development board.
For details about the process of adding commands, see [Shell Command Development](../kernel/kernel-small-debug-shell-guide.md) and [Shell Command Programming Example](../kernel/kernel-small-debug-shell-build.md).
## Important Notes
Note the following when using the shell:
**Precautions**
- You can use the **exec** command to run executable files.
- The shell supports English input by default. To delete the Chinese characters entered in UTF-8 format, press the backspace key for three times.
Note the following when using shell:
- When entering shell commands, file names, and directory names, you can press **Tab** to enable automatic completion. If there are multiple completions, multiple items are printed based on the same characters they have. If more than 24 lines of completions are available, the system displays the message "Display all num possibilities?\(y/n\)", asking you to determine whether to print all items. You can enter **y** to print all items or enter **n** to exit the printing. If more than 24 lines are printed after your selection, the system displays "--More--". In this case, you can press **Enter** to continue the printing or press **q**\(or **Ctrl+c**\) to exit.
-You can use the **exec** command to run executable files.
- The shell working directory is separated from the system working directory. You can run commands such as **cd** and **pwd** on the shell to perform operations on the shell working directory, and run commands such as **chdir** and **getcwd** to perform operations on the system working directory. Pay special attention when an input parameter in a file system operation command is a relative path.
-The shell supports English input by default. To delete the Chinese characters entered in UTF-8 format, press the backspace key for three times.
- Before using network shell commands, you need to call the **tcpip\_init** function to initialize the network and set up the Telnet connection. By default, the kernel does not call **tcpip\_init**.
-When entering shell commands, file names, and directory names, you can press **Tab** to enable automatic completion. If there are multiple completions, multiple items are printed based on the same characters they have. If more than 24 lines of completions are available, the system displays the message "Display all num possibilities?(y/n)", asking you to determine whether to print all items. You can enter **y** to print all items or enter **n** to exit the printing. If more than 24 lines are printed after your selection, the system displays "--More--". In this case, you can press **Enter** to continue the printing or press **q** (or **Ctrl+c**) to exit.
- You are not advised to run shell commands to perform operations on device files in the **/dev** directory, which may cause unexpected results.
-The shell working directory is separated from the system working directory. You can run commands such as **cd** and **pwd** on the shell to perform operations on the shell working directory, and run commands such as **chdir** and **getcwd** to perform operations on the system working directory. Pay special attention when an input parameter in a file system operation command is a relative path.
- The shell functions do not comply with the POSIX standards and are used only for debugging.
>The shell functions are used for debugging only and can be enabled only in the Debug version \(by enabling the **LOSCFG\_DEBUG\_VERSION** configuration item using **menuconfig**\).
- Before using network shell commands, you need to call the **tcpip_init** function to initialize the network and set up the Telnet connection. By default, the kernel does not call **tcpip_init**.
- You are not advised to run shell commands to perform operations on device files in the **/dev** directory, which may cause unexpected results.
- The shell functions do not comply with the POSIX standards and are used only for debugging.
> The shell functions are used for debugging only and can be enabled only in the Debug version (by enabling **LOSCFG_DEBUG_VERSION** using **menuconfig**).
The kernel startup process consists of the assembly startup and C language startup, as shown in the following figure. The assembly startup involves the following operations: initializing CPU settings, disabling dCache/iCache, enabling the FPU and NEON, setting the MMU to establish the virtual-physical address mapping, setting the system stack, clearing the BSS segment, and calling the main function of the C language. The C language startup involves the following operations: starting the OsMain function and starting scheduling. As shown in the following figure, the OsMain function is used for basic kernel initialization and architecture- and board-level initialization. The kernel startup framework leads the initialization process. The right part of the figure shows the phase in which external modules can register with the kernel startup framework and starts. [Table 1](#table38544719428) describes each phase.
<pid="p13865183210552"><aname="p13865183210552"></a><aname="p13865183210552"></a>The initialization is architecture-independent. The board and subsequent modules initialize the pure software modules on which they depend.</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p6864470423"><aname="p6864470423"></a><aname="p6864470423"></a>Early initialization of the architecture.</p>
<pid="p118192355598"><aname="p118192355598"></a><aname="p118192355598"></a>The initialization is architecture-dependent. Subsequent modules initialize the modules on which they depend. It is recommended that functions not required for startup be placed at <strongid="b13751321192318"><aname="b13751321192318"></a><aname="b13751321192318"></a>LOS_INIT_LEVEL_ARCH</strong>.</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p118531052143510"><aname="p118531052143510"></a><aname="p118531052143510"></a>Early initialization of the platform.</p>
<pid="p666132195816"><aname="p666132195816"></a><aname="p666132195816"></a>The initialization depends on the board platform and drivers. Subsequent modules initialize the modules on which they depend. It is recommended that functions required for startup be placed at <strongid="b44971429202712"><aname="b44971429202712"></a><aname="b44971429202712"></a>LOS_INIT_LEVEL_PLATFORM</strong>.
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p2862471421"><aname="p2862471421"></a><aname="p2862471421"></a>Kernel module initialization before memory initialization.</p>
<pid="p989110481520"><aname="p989110481520"></a><aname="p989110481520"></a>Initialize the modules that need to be enabled before memory initialization.</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p1186114715427"><aname="p1186114715427"></a><aname="p1186114715427"></a>Initialization after the basic memory is ready.</p>
<pid="p26441930165910"><aname="p26441930165910"></a><aname="p26441930165910"></a>After memory initialization, initialize the modules that need to be enabled and do not depend on inter-process communication (IPC) and system processes.</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p1086104719427"><aname="p1086104719427"></a><aname="p1086104719427"></a>Late initialization of the architecture.</p>
<pid="p556511281688"><aname="p556511281688"></a><aname="p556511281688"></a>The initialization is related to the architecture extension functions. Subsequent modules initialize the modules on which they depend.</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p65519915524"><aname="p65519915524"></a><aname="p65519915524"></a>Late initialization of the platform.</p>
<pid="p187247164213"><aname="p187247164213"></a><aname="p187247164213"></a>The initialization depends on the board platform and drivers. Subsequent modules initialize the modules on which they depend.</p>
<pid="p138046651010"><aname="p138046651010"></a><aname="p138046651010"></a>Example: initialization of the driver kernel abstraction layer (MMC and MTD)</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p81509525436"><aname="p81509525436"></a><aname="p81509525436"></a>Initialization of the kernel basic modules.</p>
<pid="p763134221115"><aname="p763134221115"></a><aname="p763134221115"></a>Initialize the basic modules that can be detached from the kernel.</p>
<tdclass="cellrowborder"valign="top"width="64.42%"headers="mcps1.2.3.1.2 "><pid="p6968155513438"><aname="p6968155513438"></a><aname="p6968155513438"></a>Initialization of the kernel extended modules.</p>
<pid="p669712304124"><aname="p669712304124"></a><aname="p669712304124"></a>Initialize the extended modules that can be detached from the kernel.</p>
<pid="p7600114618125"><aname="p7600114618125"></a><aname="p7600114618125"></a>Example: initialization of system call, ProcFS, Futex, HiLog, HiEvent, and LiteIPC</p>
<pid="p1657587184419"><aname="p1657587184419"></a><aname="p1657587184419"></a>Create kernel tasks (kernel tasks and software timer tasks).</p>
<pid="p55485297219"><aname="p55485297219"></a><aname="p55485297219"></a>Example: creation of the resident resource reclaiming task, SystemInit task, and CPU usage statistics task.</p>
### Example Description<a name="section1045483642518"></a>
## Kernel Startup Process
The kernel startup process consists of the assembly startup and C language startup, as shown in the following figure.
The assembly startup involves the following operations: initializing CPU settings, disabling dCache/iCache, enabling the FPU and NEON, setting the MMU to establish the virtual-physical address mapping, setting the system stack, clearing the BSS segment, and calling the main function of the C language.
The C language startup involves the following operations: starting the **OsMain** function and starting scheduling. As shown in the following figure, the **OsMain** function is used for basic kernel initialization and architecture- and board-level initialization. The kernel startup framework leads the initialization process. The right part of the figure shows the phase in which external modules can register with the kernel startup framework and starts. The table below describes each phase.
| LOS_INIT_LEVEL_EARLIEST | Earliest initialization.<br>The initialization is architecture-independent. The board and subsequent modules initialize the pure software modules on which they depend.<br>Example: trace module|
| LOS_INIT_LEVEL_ARCH_EARLY | Early initialization of the architecture.<br>The initialization is architecture-dependent. Subsequent modules initialize the modules on which they depend. It is recommended that functions not required for startup be placed at **LOS_INIT_LEVEL_ARCH**.|
| LOS_INIT_LEVEL_PLATFORM_EARLY | Early initialization of the platform.<br>The initialization depends on the board platform and drivers. Subsequent modules initialize the modules on which they depend. It is recommended that functions required for startup be placed at **LOS_INIT_LEVEL_PLATFORM**.<br>Example: UART module|
| LOS_INIT_LEVEL_KMOD_PREVM | Kernel module initialization before memory initialization.<br>Initialize the modules that need to be enabled before memory initialization.|
| LOS_INIT_LEVEL_VM_COMPLETE | Initialization after the basic memory is ready.<br>After memory initialization, initialize the modules that need to be enabled and do not depend on inter-process communication (IPC) and system processes.<br>Example: shared memory function|
| LOS_INIT_LEVEL_ARCH | Late initialization of the architecture.<br>The initialization is related to the architecture extension functions. Subsequent modules initialize the modules on which they depend.|
| LOS_INIT_LEVEL_PLATFORM | Late initialization of the platform.<br>The initialization depends on the board platform and drivers. Subsequent modules initialize the modules on which they depend.<br>Example: initialization of the driver kernel abstraction layer (MMC and MTD)|
| LOS_INIT_LEVEL_KMOD_BASIC | Initialization of the kernel basic modules.<br>Initialize the basic modules that can be detached from the kernel.<br>Example: VFS initialization|
| LOS_INIT_LEVEL_KMOD_EXTENDED | Initialization of the kernel extended modules.<br>Initialize the extended modules that can be detached from the kernel.<br>Example: initialization of system call, ProcFS, Futex, HiLog, HiEvent, and LiteIPC|
| LOS_INIT_LEVEL_KMOD_TASK | Kernel task creation.<br>Create kernel tasks (kernel tasks and software timer tasks).<br>Example: creation of the resident resource reclaiming task, SystemInit task, and CPU usage statistics task|
## Programming Example
**Example Description**
Add a kernel module and register the initialization function of the module to the kernel startup process through the kernel startup framework, so as to complete the module initialization during the kernel initialization process.
**Sample Code**
```
/* Header file of the kernel startup framework */
#include "los_init.h"
...
...
@@ -110,8 +57,11 @@ unsigned int OsSampleModInit(void)
According to the information displayed during the system startup, the kernel has called the initialization function of the registered module during the startup to initialize the module.
>Modules at the same level cannot depend on each other. It is recommended that a new module be split based on the preceding startup phase and be registered and started as required.
>You can view the symbol table in the **.rodata.init.kernel.\*** segment of the **OHOS\_Image.map** file generated after the build is complete, so as to learn about the initialization entry of each module that has been registered with the kernel startup framework and check whether the newly registered initialization entry has taken effect.
> Modules at the same level cannot depend on each other. It is recommended that a new module be split based on the preceding startup phase and be registered and started as required.
>
> You can view the symbol table in the **.rodata.init.kernel.*** segment of the **OHOS_Image.map** file generated after the build is complete, so as to learn about the initialization entry of each module that has been registered with the kernel startup framework and check whether the newly registered initialization entry has taken effect.