The Cortex Microcontroller Software Interface Standard \([CMSIS](https://developer.arm.com/tools-and-software/embedded/cmsis)\) is a vendor-independent hardware abstraction layer for microcontrollers based on Arm Cortex processors. Of the CMSIS components, the Real Time Operating System \(RTOS\) defines a set of universal and standardized APIs to reduce the dependency of application developers on specific RTOS and facilitate software porting and reuse. The CMSIS provides CMSIS-RTOS v1 and CMSIS-RTOS v2. The OpenHarmony LiteOS-M supports only the implementation of CMSIS-RTOS v2.
## Development Guidelines
### Available APIs
The following table describes CMSIS-RTOS v2 APIs. For more details about the APIs, see the API reference.
**Table 1** CMSIS-RTOS v2 APIs
| Category| API| Description|
| -------- | -------- | -------- |
| Kernel information and control| osKernelGetInfo | Obtains RTOS kernel information.|
| | osKernelGetState | Obtains the current RTOS kernel status.|
| | osKernelGetSysTimerCount | Obtains the RTOS kernel system timer count.|
| | osKernelGetSysTimerFreq | Obtains the RTOS kernel system timer frequency.|
| | osKernelInitialize | Initializes the RTOS kernel.|
| | osKernelLock | Locks the RTOS kernel scheduler.|
| | osKernelUnlock | Unlocks the RTOS kernel scheduler.|
| | osKernelRestoreLock | Restores the RTOS kernel scheduler to the locked state.|
| | osKernelGetTickCount | Obtains the RTOS kernel tick count.|
| | osKernelGetTickFreq | Obtains the RTOS kernel tick frequency.|
| Thread management| osThreadDetach | Detaches a thread (to reclaim the thread storage when the thread terminates).|
| | osThreadEnumerate | Enumerates active threads (not been implemented yet).|
| | osThreadExit | Terminates a running thread.|
| | osThreadGetCount | Obtains the number of active threads.|
| | osThreadGetId | Obtains the ID of the running thread.|
| | osThreadGetName | Obtains the thread name.|
| | osThreadGetPriority | Obtains the current thread priority.|
| | osThreadGetStackSize | Obtains the thread stack size.|
| | osThreadGetStackSpace | Obtains the available stack space for a thread based on the stack waterline record during execution.|
| | osThreadGetState | Obtains the current thread status.|
| | osThreadJoin | Waits for the specified thread to terminate.|
| | osThreadNew | Creates a thread and adds it to active threads.|
| | osThreadResume | Resumes the execution of a thread.|
| | osThreadSetPriority | Changes the priority of a thread.|
| | osThreadSuspend | Suspends a thread.|
| | osThreadTerminate | Terminates a thread.|
| | osThreadYield | Passes control to the next thread in the ready state.|
| Thread flag| osThreadFlagsSet | Sets flags for a thread.|
| | osThreadFlagsClear | Clears the specified flags for the running thread.|
| | osThreadFlagsGet | Obtains the current flags of the running thread.|
| | osThreadFlagsWait | Waits for one or more thread flags of the running thread to signal.|
| Event flag| osEventFlagsGetName | Obtains the names of the event flags (not implemented yet).|
| | osEventFlagsNew | Creates and initializes event flags.|
| | osEventFlagsDelete | Deletes event flags.|
| | osEventFlagsSet | Sets event flags.|
| | osEventFlagsClear | Clears event flags.|
| | osEventFlagsGet | Obtains the current event flags.|
| | osEventFlagsWait | Waits for one or more event flags to be signaled.|
| General waiting functions| osDelay | Waits for timeout (time delay).|
| | osDelayUntil | Waits until the specified time.|
| Timer management| osTimerDelete | Deletes a timer.|
| | osTimerGetName | Obtains the timer name (not implemented yet).|
| | osTimerIsRunning | Checks whether a timer is running.|
| | osTimerNew | Creates and initializes a timer.|
| | osTimerStart | Starts or restarts a timer.|
| | osTimerStop | Stops a timer.|
| Mutex management| osMutexAcquire | Acquires a mutex or times out (if locked).|
| | osMutexDelete | Deletes a mutex.|
| | osMutexGetName | Obtains the mutex name (not implemented yet).|
| | osMutexGetOwner | Obtains the thread that acquires the mutex.|
| | osMutexNew | Creates and initializes a mutex.|
| | osMutexRelease | Releases the mutex obtained using **osMutexAcquire**.|
| Semaphore| osSemaphoreAcquire | Obtains the semaphore token or times out if no token is available.|
| | osSemaphoreDelete | Deletes a semaphore.|
| | osSemaphoreGetCount | Obtains the token count of the current semaphore.|
| | osSemaphoreGetName | Obtains the name of a semaphore (not implemented yet).|
| | osSemaphoreNew | Creates and initializes a semaphore.|
| | osSemaphoreRelease | Releases semaphore tokens till the initial maximum count.|
| Memory pool| osMemoryPoolAlloc | Allocates a memory block from the memory pool.|
| | osMemoryPoolDelete | Deletes a memory pool object.|
| | osMemoryPoolFree | Releases the allocated memory block to the memory pool.|
| | osMemoryPoolGetBlockSize | Obtains the memory block size in the memory pool.|
| | osMemoryPoolGetCapacity | Obtains the maximum number of memory blocks in the memory pool.|
| | osMemoryPoolGetCount | Obtains the number of used memory blocks in the memory pool.|
| | osMemoryPoolGetName | Obtains the memory pool name.|
| | osMemoryPoolGetSpace | Obtains the number of available memory blocks in the memory pool.|
| | osMemoryPoolNew | Creates and initializes a memory pool.|
| Message queue| osMessageQueueDelete | Deletes a message queue.|
| | osMessageQueueGet | Obtain a message from the queue or times out if the queue is empty.|
| | osMessageQueueGetCapacity | Obtains the maximum number of messages in the message queue.|
| | osMessageQueueGetCount | Obtains the number of queued messages in the message queue.|
| | osMessageQueueGetMsgSize | Obtains the maximum message size in the memory pool.|
| | osMessageQueueGetName | Obtains the message queue name (not implemented yet).|
| | osMessageQueueGetSpace | Obtains the number of slots available for messages in the message queue.|
| | osMessageQueueNew | Creates and initializes a message queue.|
| | osMessageQueuePut | Puts the message into the queue or times out if the queue is full.|
| | osMessageQueueReset | Initialized the message queue to the empty state (not implemented yet).|
### How to Develop
The CMSIS-RTOS v2 component can be provided as a library \(shown in the figure\) or source code. By adding the CMSIS-RTOS v2 component \(typically configuration files\), you can implement RTOS capabilities on CMSIS-based applications. You only need to include the **cmsis\_os2.h** header file. RTOS APIs can then be called to process RTOS kernel-related events. You do not need to recompile the source code when the kernel is replaced.
The RTOS object control block definition needs to be called for static object allocation. The implementation-specific header file \(**os\_xx.h** in the following figure\) provides access to such control block definitions. In the OpenHarmony LiteOS-M kernel, the header files whose names start with **los\_** provide the definitions of the kernel.
The OpenHarmony kernel uses the **musl libc** library and self-developed APIs and supports the Portable Operating System Interface \(POSIX\). You can develop components and applications working on the kernel based on the POSIX.
## Development Guidelines
### Available APIs
**Table 1** Available APIs
| Category| Header File| API| Description|
| -------- | -------- | -------- | -------- |
| process | #include <stdlib.h> | void abort(void); | Terminates the thread.|
| | #include <assert.h> | void assert(scalar expression); | Terminates the thread if the assertion is false.|
| | #include <pthread.h> | int pthread_cond_destroy(pthread_cond_t *cond); | Destroys a condition variable.|
| | #include <pthread.h> | int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); | Initializes a condition variable.|
| | #include <pthread.h> | int pthread_condattr_getpshared(const pthread_condattr_t *attr, int *shared); | Obtains condition variable attributes. Currently, only the attributes of **PTHREAD_PROCESS_PRIVATE** can be obtained.|
| | #include <pthread.h> | int pthread_condattr_setpshared(pthread_condattr_t *attr, int shared); | Sets a condition variable attribute.|
| | #include <pthread.h> | int pthread_condattr_getclock(const pthread_condattr_t *attr, clockid_t *clock); | Obtains the thread clock.|
| | #include <pthread.h> | int pthread_condattr_destroy(pthread_condattr_t *attr); | Destroys a condition variable and invalidates the attribute object.|
| | #include <pthread.h> | int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime); | Waits for the condition.|
| | #include <pthread.h> | int pthread_condattr_init(pthread_condattr_t *attr); | Initializes the condition variable attribute.|
| | #include <pthread.h> | int pthread_mutex_unlock(pthread_mutex_t *mutex); | Unlocks a mutex.|
| | #include <pthread.h> | int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); | Creates a thread.|
| | #include <pthread.h> | int pthread_join(pthread_t thread, void **retval); | Waits for a thread to terminate.|
| | #include <pthread.h> | pthread_t pthread_self(void); | Obtains the ID of the current thread.|
| | #include <pthread.h> | int pthread_getschedparam(pthread_t thread, int *policy, struct sched_param *param); | Obtains the scheduling policy and parameters of a thread.|
| | #include <pthread.h> | int pthread_setschedparam(pthread_t thread, int policy, const struct sched_param *param); | Sets a scheduling policy and parameters for a thread.|
| | #include <pthread.h> | int pthread_setschedprio(pthread_t thread, int prio); | Sets the thread priority.|
| | #include <pthread.h> | int pthread_setcancelstate(int state, int *oldState); | Sets the cancel switch for the thread.|
| | #include <pthread.h> | int pthread_setcanceltype(int type, int *oldType); | Sets the thread cancel type.|
| | #include <pthread.h> | int pthread_cancel(pthread_t thread); | Cancels a thread. Currently, a thread can be cancelled only by setting the **PTHREAD_CANCEL_ASYNCHRONOUS** status and then calling **pthread_cancel**.|
| | #include <pthread.h> | int pthread_equal(pthread_t thread1, pthread_t thread2); | Checks whether the two thread IDs are equal.|
| | #include <pthread.h> | int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *outType); | Obtains the mutex type attribute.|
| | #include <pthread.h> | int pthread_mutex_init(pthread_mutex_t *__restrict m, const pthread_mutexattr_t *__restrict a); | Initializes a mutex.|
| | #include <pthread.h> | int pthread_mutex_lock(pthread_mutex_t *m); | Locks a mutex.|
| | #include <pthread.h> | int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *absTimeout); | Requests a mutex (blocked only within the specified period).|
| | #include <pthread.h> | int pthread_mutex_trylock(pthread_mutex_t *m); | Attempts to lock a mutex.|
| | #include <pthread.h> | int pthread_mutex_destroy(pthread_mutex_t *m); | Destroys a mutex.|
| | #include <pthread.h> | int pthread_attr_init(pthread_attr_t *attr); | Initializes a thread attribute object.|
| | #include <pthread.h> | int pthread_attr_destroy(pthread_attr_t *attr); | Destroys a thread attribute object.|
| | #include <pthread.h> | int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize); | Obtains the stack size of a thread attribute object.|
| | #include <pthread.h> | int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); | Sets the stack size for a thread attribute object.|
| | #include <pthread.h> | int pthread_attr_setstack(pthread_attr_t *attr, void *stackAddr, size_t stackSize); | Sets the stack attribute for a thread attribute object (not implemented yet).|
| | #include <pthread.h> | int pthread_attr_getstack(const pthread_attr_t *attr, void **stackAddr, size_t *stackSize); | Obtains the stack of a thread attribute object (not implemented yet).|
| | #include <pthread.h> | int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); | Obtains scheduling parameter attributes of a thread attribute object.|
| | #include <pthread.h> | int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); | Sets scheduling parameter attributes for a thread attribute object.|
| | #include <pthread.h> | int pthread_getname_np(pthread_t pthread, char *name, size_t len); | Obtains the thread name.|
| | #include <pthread.h> | int pthread_setname_np(pthread_t pthread, const char *name); | Sets the thread name.|
| | #include <pthread.h> | int pthread_cond_broadcast(pthread_cond_t *c); | Unblocks all threads that are currently blocked on the condition variable **cond**.|
| | #include <pthread.h> | int pthread_cond_signal(pthread_cond_t *c); | Unblocks a thread.|
| | #include <pthread.h> | int pthread_cond_wait(pthread_cond_t *__restrict c, pthread_mutex_t *__restrict m); | Waits for the condition.|
| | #include <sys/stat.h> | int stat(const char *restrict path, struct stat *restrict buf); | Obtains file information.|
| | #include <unistd.h> | int unlink(const char *pathname); | Deletes a file.|
| | #include <fcntl.h | int open(const char *path, int oflags, ...); | Opens a file. If the file does not exist, create a file and open it.|
| | #include <nistd.h> | int close(int fd); | Closes a file.|
| | #include <stdio.h> | int rename(const char *oldpath, const char *newpath); | Renames a file.|
| | #include <dirent.h> | DIR *opendir(const char *dirname); | Opens the specified directory.|
| | #include <dirent.h> | int closedir(DIR *dir); | Closes the specified directory.|
| | #include <sys/mount.h> | int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); | Mounts a file system.|
| | #include <sys/mount.h> | int umount(const char *target); | Unmounts a file system.|
| | #include <sys/mount.h> | int umount2(const char *target, int flag); | Unmounts a file system.|
| | #include <sys/stat.h> | int fsync(int fd); | Synchronizes the file associated with the specified file descriptor to the storage device.|
| | #include <sys/stat.h> | int mkdir(const char *pathname, mode_t mode); | Creates a directory.|
| | #include <unistd.h> | int rmdir(const char *path); | Deletes a directory.|
| | #include <sys/stat.h> | int fstat(int fd, struct stat *buf); | Obtains file status.|
| | #include <sys/statfs.h> | int statfs(const char *path, struct statfs *buf); | Obtains the file system information for a file in a specified path.|
| time | #include <sys/time.h> | int gettimeofday(struct timeval *tv, struct timezone *tz); | Obtains the time. Currently, time zone is not supported, and the return value of **tz** is empty.|
| | #include <time.h> | struct tm *gmtime(const time_t *timep); | Converts the date and time to broken-down time or ASCII.|
| | #include <time.h> | struct tm *localtime(const time_t *timep); | Obtains the local time.|
| | #include <time.h> | struct tm *localtime_r(const time_t *timep, struct tm *result); | Obtains the local time.|
| | #include <time.h> | time_t mktime(struct tm *tm); | Converts the date and time to broken-down time or ASCII.|
| | #include <time.h> | size_t strftime(char *s, size_t max, const char *format,const struct tm *tm); | Formats the date and time.|
| | #include <sys/times.h> | int timer_getoverrun(timer_t timerID); | Obtains the number of timer timeout times.|
| | #include <unistd.h> | int usleep(useconds_t usec); | Goes to hibernation, in microseconds.|
| | #include <time.h> | int nanosleep(const struct timespec *tspec1, struct timespec *tspec2); | Suspends the current thread till the specified time.|
| | #include <time.h> | int clock_gettime(clockid_t id, struct timespec *tspec); | Obtains the clock time.|
| | #include <time.h> | int timer_create(clockid_t id, struct sigevent *__restrict evp, timer_t *__restrict t); | Creates a timer for a thread.|
| | #include <time.h> | int timer_delete(timer_t t); | Deletes the timer for a thread.|
| | #include <time.h> | int timer_settime(timer_t t, int flags, const struct itimerspec *__restrict val, struct itimerspec *__restrict old); | Sets a timer for a thread.|
| | #include <time.h> | time_t time (time_t *t); | Obtains the time.|
| | #include <time.h> | char *strptime(const char *s, const char *format, struct tm *tm); | Converts the time string into the time **tm** structure.|
| util | #include <stdlib.h> | int atoi(const char *nptr); | Converts the string pointed to by **nptr** into an integer (**int** type).|
| | #include <stdlib.h> | long atol(const char *nptr); | Converts the string pointed to by **nptr** into a long Integer (**long** type).|
| | #include <stdlib.h> | long long atoll(const char *nptr); | Converts the string pointed to by **nptr** into a long long Integer (**long long** type).|
| | #include <ctype.h> | int isalnum(int c); | Checks whether the passed character is alphanumeric.|
| | #include <ctype.h> | int isascii(int c); | Checks whether the passed character is an ASCII character.|
| | #include <ctype.h> | int isdigit(int c); | Checks whether the passed character is a digit.|
| | #include <ctype.h> | int islower(int c); | Checks whether the passed character is in lowercase.|
| | #include <ctype.h> | int isprint(int c); | Checks whether the passed character is printable, including spaces.|
| | #include <ctype.h> | int isspace(int c); | Checks whether the passed character is a white-space character.|
| | #include <ctype.h> | int isupper(int c); | Checks whether the passed character is in uppercase.|
| | #include <ctype.h> | int isxdigit(int c); | Checks whether the passed character is a hexadecimal number.|
| | #include <stdlib.h> | long int random (void); | Generates a pseudo-random number.|
| | #include <stdlib.h> | void srandom(unsigned int seed); | Initializes the random number generator.|
| | #include <ctype.h> | int tolower(int c); | Converts the given letter to lowercase.|
| | #include <ctype.h> | int toupper(int c); | Converts the given letter to uppercase.|
| | #include <stdarg.h> | type va_arg(va_list ap, type); | Retrieves the next argument in the parameter list with **type**. |
| | #include <stdarg.h> | void va_start(va_list ap, last); | Defines the beginning of the list of variable arguments.|
| | #include <string.h> | char *strchr(const char *s, int c); | Searches for the first occurrence of a character in a string.|
| | #include <string.h> | int strcmp(const char *s1, const char *s2); | Compares two strings.|
| | #include <string.h> | size_t strcspn(const char *s, const char *reject); | Obtains the length of the initial segment of the string **s** which does not contain any of bytes in the string **reject**.|
| | #include <string.h> | char *strdup(const char *s); | Copies a string to a new position.|
| | #include <string.h> | size_t strlen(const char *s); | Obtains the length of a string.|
| | #include <strings.h> | int strncasecmp(const char *s1, const char *s2, size_t n); | Compares the bytes of the specified length in two strings, ignoring case.|
| | #include <strings.h> | int strcasecmp(const char *s1, const char *s2); | Compares two strings, ignoring case.|
| | #include <string.h> | int strncmp(const char *s1, const char *s2, size_t n); | Compares the bytes of the specified length in two strings.|
| | #include <string.h> | char *strrchr(const char *s, int c); | Searches for the last occurrence of a character in a string.|
| | #include <string.h> | char *strstr(const char *haystack, const char *needle); | Searches for the specified substring in a string.|
| | #include <stdlib.h> | long int strtol(const char *nptr, char **endptr, int base); | Converts the string pointed to by **nptr** into a **long int** value according to the given **base**.|
| | #include <stdlib.h> | unsigned long int strtoul(const char *nptr, char **endptr, int base); | Converts the string pointed to by **nptr** into an unsigned **long int** value according to the given **base**.|
| | #include <stdlib.h> | unsigned long long int strtoull(const char *nptr, char **endptr,int base); | Converts the string pointed to by **nptr** into an unsigned **long long int** value according to the given **base**.|
| | #include <regex.h> | int regcomp(regex_t *preg, const char *regex, int cflags); | Compiles a regular expression.|
| | #include <regex.h> | int regexec(const regex_t *preg, const char *string, size_t nmatch,regmatch_t pmatch[], int eflags); | Executes the compiled regular expression.|
| | #include <stdio.h> | int fseek(FILE *stream, long offset, int whence); | Sets the position of the stream pointer.|
| | #include <stdio.h> | long ftell(FILE *stream); | Obtains the position of the stream pointer.|
| | #include <stdio.h> | size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream); | Writes data to a stream.|
| | #include <stdio.h> | void perror(const char *s); | Prints system error information.|
| | #include <stdio.h> | void rewind(FILE *stream); | Sets the position to the beginning of the file of the specified stream.|
| | #include <unistd.h> | ssize_t write(int fd, const void *buf, size_t size); | Writes data a file.|
| | #include <unistd.h> | ssize_t read(int fd, void *buf, size_t size); | Reads data from a file.|
| net | #include <sys/socket.h> | void freeaddrinfo(struct addrinfo *res); | Releases the dynamic memory allocated using **getaddrinfo**.|
| | #include <sys/socket.h> | int getaddrinfo(const char *restrict nodename,const char *restrict servname,const struct addrinfo *restrict hints,struct addrinfo **restrict res); | Obtains a list of IP addresses and port numbers for the specified host and service.|
| | #include <sys/socket.h> | int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen,char *restrict node, socklen_t nodelen, char *restrict service,socklen_t servicelen, int flags); | Converts a **socketaddr** structure to a pair of host name and service strings.|
| | #include <net/if.h> | unsigned int if_nametoindex(const char *ifname); | Obtains the index based on the network port name.|
| | #include <arpa/inet.h> | in_addr_t inet_addr(const char *cp); | Converts the network host address in dotted decimal notation to binary format.|
| | #include <arpa/inet.h> | char *inet_ntoa(struct in_addr in); | Converts the network host address in binary format to dotted decimal notation.|
| | #include <arpa/inet.h> | const char *inet_ntop(int af, const void *src,char *dst, socklen_t size); | Converts the network address in binary format to text.|
| | #include <arpa/inet.h> | int inet_pton(int af, const char *src, void *dst); | Converts the network address in standard text format to numeric binary format.|
| | #include <sys/socket.h> | int listen(int sockfd, int backlog); | Listens for connections on a socket.|
| | #include <sys/socket.h> | ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); | Receives a message from a socket. Currently, only the scenario with **iov** of **1** is supported and ancillary messages are not supported.|
| | #include <sys/socket.h> | ssize_t send(int sockfd, const void *buf, size_t len, int flags); | Sends a message on a socket.|
| | #include <sys/socket.h> | ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); | Sends a message on a socket. Ancillary messages are not supported.|
| | #include <sys/socket.h> | ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); | Sends a message on a socket.|
| | #include <sys/socket.h> | int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen); | Sets options associated with a socket.|
| mem | #include <string.h> | int memcmp(const void *s1, const void *s2, size_t n); | Compares successive elements from two arrays until it finds elements that are different.|
| | #include <string.h> | void *memcpy(void *dest, const void *src, size_t n); | Copies *n* bytes from the source memory area pointed to by **src** to the destination memory area pointed to by **dest**.|
| | #include <stdlib.h> | void free(void *ptr); | Release the memory space pointed to by **ptr**.|
| IPC | #include <semaphore.h> | int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout); | Locks the semaphore referenced by **sem** as in the **sem_wait()** function.|
| | #include <semaphore.h> | int sem_destroy(sem_t *sem); | Destroys the specified unnamed semaphore.|
| | #include <semaphore.h> | int sem_trywait(sem_t *sem); | Attempts to apply for a semaphore.|
| | #include <semaphore.h> | int sem_init(sem_t *sem, int pshared, unsigned int value); | Creates and initializes an unnamed semaphore.|
| | #include <semaphore.h> | int sem_post(sem_t *sem); | Increments the semaphore count by 1.|
| | #include <semaphore.h> | int sem_wait(sem_t *sem); | Obtains the semaphore.|
| | #include <mqueue.h> | mqd_t mq_open(const char *mqName, int openFlag, ...); | Opens an existing message queue with the specified name or creates a message queue.|
| | #include <mqueue.h> | int mq_close(mqd_t personal); | Closes a message queue with the specified descriptor.|
| | #include <mqueue.h> | int mq_unlink(const char *mqName); | Deletes the message queue of the specified name.|
| | #include <mqueue.h> | int mq_send(mqd_t personal, const char *msg, size_t msgLen, unsigned int msgPrio); | Puts a message with the specified content and length into a message queue.|
| | #include <mqueue.h> | ssize_t mq_receive(mqd_t personal, char *msg, size_t msgLen, unsigned int *msgPrio); | Deletes the oldest message from a message queue and puts it in the buffer pointed to by **msg_ptr**.|
| | #include <mqueue.h> | int mq_timedsend(mqd_t personal, const char *msg, size_t msgLen, unsigned int msgPrio, const struct timespec *absTimeout) | Puts a message with the specified content and length into a message queue at the specified time.|
| | #include <mqueue.h> | ssize_t mq_timedreceive(mqd_t personal, char *msg, size_t msgLen, unsigned int *msgPrio, const struct timespec *absTimeout); | Obtains a message with the specified content and length from a message queue.|
| | #include <mqueue.h> | int mq_setattr(mqd_t mqdes, const struct mq_attr *__restrict newattr, struct mq_attr *__restrict oldattr); | Sets the message queue attributes specified by the descriptor.|
| version | #include <libc.h> | const char *libc_get_version_string(void); | Obtains the libc version string.|
| | #include <libc.h> | int libc_get_version(void); | Obtains the libc version.|
### Important Notes
Error codes
| C Name | Value | Description |
| -------- | -------- | -------- |
| ENOERR | 0 | Success |
| EPERM | 1 | Operation not permitted |
| ENOENT | 2 | No such file or directory |
| ESRCH | 3 | No such process |
| EINTR | 4 | Interrupted system call |
| EIO | 5 | I/O error |
| ENXIO | 6 | No such device or address |
| E2BIG | 7 | Arg list too long |
| ENOEXEC | 8 | Exec format error |
| EBADF | 9 | Bad file number |
| ECHILD | 10 | No child processes |
| EAGAIN | 11 | Try again |
| ENOMEM | 12 | Out of memory |
| EACCES | 13 | Permission denied |
| EFAULT | 14 | Bad address |
| ENOTBLK | 15 | Block device required |
| EBUSY | 16 | Device or resource busy |
| EEXIST | 17 | File exists |
| EXDEV | 18 | Cross-device link |
| ENODEV | 19 | No such device |
| ENOTDIR | 20 | Not a directory |
| EISDIR | 21 | Is a directory |
| EINVAL | 22 | Invalid argument |
| ENFILE* | 23 | File table overflow |
| EMFILE | 24 | Too many open files |
| EFBIG | 27 | File too large |
| ENOSPC | 28 | No space left on device |
| ESPIPE | 29 | Illegal seek |
| EROFS | 30 | Read-only file system |
| EMLINK | 31 | Too many links |
| EDOM | 33 | Math argument out of domain |
| ERANGE | 34 | Math result not representable |
| EDEADLK | 35 | Resource deadlock would occur |
| ENAMETOOLONG | 36 | Filename too long |
| ENOLCK | 37 | No record locks available |
| ENOSYS | 38 | Function not implemented |
| ENOTEMPTY | 39 | Directory not empty |
| ELOOP | 40 | Too many symbolic links encountered |
| ENOMSG | 42 | No message of desired type |
| EIDRM | 43 | Identifier removed |
| ELNRNG | 48 | Link number out of range |
| EBADR | 53 | Invalid request descriptor |
| EBADRQC | 56 | Invalid request code |
| ENOSTR | 60 | Device not a stream |
| ENODATA | 61 | No data available |
| ETIME | 62 | Timer expired |
| EPROTO | 71 | Protocol error |
| EBADMSG | 74 | Not a data message |
| EOVERFLOW | 75 | Value too large for defined data type |
| EMSGSIZE | 90 | Message too long |
### Development Example
Demo:
Creates a thread, transfers the information in the parent thread to the child thread, and prints the transferred information and the thread ID in the child thread.
```
#include <stdio.h>
#include <pthread.h>
pthread_t ntid;
void *ThreadFn(void *arg)
{
pthread_t tid;
while(1) {
tid = pthread_self();
printf("\n++++++++++++++ %s %s tid = %d ++++++++++++++\n", (char*)arg, __FUNCTION__, tid);
The [Cortex Microcontroller Software Interface Standard (CMSIS)](https://developer.arm.com/tools-and-software/embedded/cmsis) is a vendor-independent hardware abstraction layer for microcontrollers based on Arm Cortex processors. Of the CMSIS components, the Real Time Operating System (RTOS) defines a set of universal and standardized APIs to reduce the dependency of application developers on specific RTOS and facilitate software porting and reuse. The CMSIS provides CMSIS-RTOS v1 and CMSIS-RTOS v2. The OpenHarmony LiteOS-M supports only the implementation of CMSIS-RTOS v2.
### Development Guidelines
#### Available APIs
The following tables describe CMSIS-RTOS v2 APIs. For more details about the APIs, see the API reference.
**Table 1** APIs for obtaining kernel information and controlling the kernel
| osKernelGetTickCount | Obtains the RTOS kernel tick count.|
| osKernelGetTickFreq | Obtains the RTOS kernel tick frequency.|
**Table 2** APIs for thread management
| API| Description|
| -------- | -------- |
| osThreadDetach | Detaches a thread to reclaim the thread storage when the thread terminates (not implemented yet).|
| osThreadEnumerate | Enumerates active threads (not implemented yet).|
| osThreadExit | Terminates a running thread.|
| osThreadGetCount | Obtains the number of active threads.|
| osThreadGetId | Obtains the ID of the running thread.|
| osThreadGetName | Obtains the thread name.|
| osThreadGetPriority | Obtains the current thread priority.|
| osThreadGetStackSize | Obtains the thread stack size.|
| osThreadGetStackSpace | Obtains the available stack space for a thread based on the stack waterline record during execution.|
| osThreadGetState | Obtains the current thread status.|
| osThreadJoin | Waits for the specified thread to terminate (not implemented yet).|
| osThreadNew | Creates a thread and adds it to active threads.|
| osThreadResume | Resumes the execution of a thread.|
| osThreadSetPriority | Changes the priority of a thread.|
| osThreadSuspend | Suspends a thread.|
| osThreadTerminate | Terminates a thread.|
| osThreadYield | Passes control to the next thread in the ready state.|
**Table 3** APIs for thread flags
| API| Description|
| -------- | -------- |
| osThreadFlagsSet | Sets flags for a thread (not implemented yet).|
| osThreadFlagsClear | Clears the specified flags for the running thread (not implemented yet).|
| osThreadFlagsGet | Obtains the current flags of the running thread (not implemented yet).|
| osThreadFlagsWait | Waits for one or more thread flags of the running thread to signal (not implemented yet).|
**Table 4** APIs for event flags
| API| Description|
| -------- | -------- |
| osEventFlagsGetName | Obtains the event flag names (not implemented yet).|
| osEventFlagsNew | Creates and initializes event flags.|
| osEventFlagsDelete | Deletes event flags.|
| osEventFlagsSet | Sets event flags.|
| osEventFlagsClear | Clears event flags.|
| osEventFlagsGet | Obtains the current event flags.|
| osEventFlagsWait | Waits for one or more event flags to be signaled.|
**Table 5** General wait functions
| API| Description|
| -------- | -------- |
| osDelay | Waits for timeout (time delay).|
| osDelayUntil | Waits until the specified time.|
**Table 6** APIs for timer management
| API| Description|
| -------- | -------- |
| osTimerDelete | Deletes a timer.|
| osTimerGetName | Obtains the timer name (not implemented yet).|
| osTimerIsRunning | Checks whether a timer is running.|
| osTimerNew | Creates and initializes a timer.|
| osTimerStart | Starts or restarts a timer.|
| osTimerStop | Stops a timer.|
**Table 7** APIs for mutex management
| API| Description|
| -------- | -------- |
| osMutexAcquire | Acquires a mutex or times out (if locked).|
| osMutexDelete | Deletes a mutex.|
| osMutexGetName | Obtains the mutex name (not implemented yet).|
| osMutexGetOwner | Obtains the thread that acquires the mutex.|
| osMutexNew | Creates and initializes a mutex.|
| osMutexRelease | Releases the mutex obtained using **osMutexAcquire**.|
**Table 8** APIs for semaphore management
| API| Description|
| -------- | -------- |
| osSemaphoreAcquire | Obtains the semaphore token or times out if no token is available.|
| osSemaphoreDelete | Deletes a semaphore.|
| osSemaphoreGetCount | Obtains the token count of the current semaphore.|
| osSemaphoreGetName | Obtains the name of a semaphore (not implemented yet).|
| osSemaphoreNew | Creates and initializes a semaphore.|
| osSemaphoreRelease | Releases semaphore tokens till the initial maximum count.|
**Table 9** APIs for memory pool management
| API| Description|
| -------- | -------- |
| osMemoryPoolAlloc | Allocates a memory block from the memory pool.|
| osMemoryPoolDelete | Deletes a memory pool object.|
| osMemoryPoolFree | Releases the allocated memory block to the memory pool.|
| osMemoryPoolGetBlockSize | Obtains the memory block size in the memory pool.|
| osMemoryPoolGetCapacity | Obtains the maximum number of memory blocks in the memory pool.|
| osMemoryPoolGetCount | Obtains the number of used memory blocks in the memory pool.|
| osMemoryPoolGetName | Obtains the memory pool name.|
| osMemoryPoolGetSpace | Obtains the number of available memory blocks in the memory pool.|
| osMemoryPoolNew | Creates and initializes a memory pool.|
**Table 10** APIs for message queues
| API| Description|
| -------- | -------- |
| osMessageQueueDelete | Deletes a message queue.|
| osMessageQueueGet | Obtain a message from the queue or times out if the queue is empty.|
| osMessageQueueGetCapacity | Obtains the maximum number of messages in the message queue.|
| osMessageQueueGetCount | Obtains the number of queued messages in the message queue.|
| osMessageQueueGetMsgSize | Obtains the maximum message size in the memory pool.|
| osMessageQueueGetName | Obtains the message queue name (not implemented yet).|
| osMessageQueueGetSpace | Obtains the number of slots available for messages in the message queue.|
| osMessageQueueNew | Creates and initializes a message queue.|
| osMessageQueuePut | Puts the message into the queue or times out if the queue is full.|
| osMessageQueueReset | Resets the message queue to the initial empty state (not implemented yet).|
#### How to Develop
The CMSIS-RTOS v2 component can be provided as a library (shown in the figure) or source code. By adding the CMSIS-RTOS v2 component (typically configuration files), you can implement RTOS capabilities on CMSIS-based applications. You only need to include the **cmsis_os2.h** header file. RTOS APIs can then be called to process RTOS kernel-related events. You do not need to recompile the source code when the kernel is replaced.
The RTOS object control block definition needs to be called for static object allocation. The implementation header file (**os_xx.h** in the following figure) provides access to such control block definitions. In the OpenHarmony LiteOS-M kernel, the header files whose names start with **los_** provide the definitions of the kernel.
osThreadNew(app_main, NULL, NULL); // Create the main thread of the application.
osKernelStart(); // Start to execute the thread.
for (;;) {}
}
```
## POSIX
### Basic Concepts
The OpenHarmony kernel uses the **musl libc** library and self-developed APIs and supports the Portable Operating System Interface (POSIX). You can develop components and applications working on the kernel based on the POSIX.
### Development Guidelines
#### Available APIs
**Table 1** APIs for process management
| Header File| API| Description|
| -------- | -------- | -------- |
| \#include <stdlib.h> | void abort(void); | Terminates the thread.|
| \#include <assert.h> | void assert(scalar expression); | Terminates the thread if the assertion is false.|
| \#include <pthread.h> | int pthread_cond_destroy(pthread_cond_t *cond); | Destroys a condition variable.|
| \#include <pthread.h> | int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t \*restrict attr); | Initializes a condition variable.|
| \#include <pthread.h> | int pthread_cond_timedwait(pthread_cond_t \*restrict cond, pthread_mutex_t \*restrict mutex, const struct timespec \*restrict abstime); | Waits for the condition.|
| \#include <pthread.h> | int pthread_condattr_init(pthread_condattr_t \*attr); | Initializes the condition variable attribute.|
| \#include <pthread.h> | int pthread_mutex_unlock(pthread_mutex_t \*mutex); | Unlocks a mutex.|
| \#include <pthread.h> | int pthread_create(pthread_t \*thread, const pthread_attr_t \*attr, void \*(\*start_routine)(void \*), void \*arg); | Creates a thread.|
| \#include <pthread.h> | int pthread_join(pthread_t thread, void \*\*retval); | Waits for a thread to terminate.|
| \#include <pthread.h> | pthread_t pthread_self(void); | Obtains the ID of the current thread.|
| \#include <pthread.h> | int pthread_getschedparam(pthread_t thread, int \*policy, struct sched_param \*param); | Obtains the scheduling policy and parameters of a thread.|
| \#include <pthread.h> | int pthread_setschedparam(pthread_t thread, intpolicy, const struct sched_param \*param); | Sets a scheduling policy and parameters for a thread.|
| \#include <pthread.h> | int pthread_mutex_init(pthread_mutex_t \*__restrict m, const pthread_mutexattr_t \*__restrict a); | Initializes a mutex.|
| \#include <pthread.h> | int pthread_mutex_lock(pthread_mutex_t \*m); | Locks a mutex.|
| \#include <pthread.h> | int pthread_mutex_trylock(pthread_mutex_t \*m); | Attempts to lock a mutex.|
| \#include <pthread.h> | int pthread_mutex_destroy(pthread_mutex_t \*m); | Destroys a mutex.|
| \#include <pthread.h> | int pthread_attr_init(pthread_attr_t \*attr); | Initializes a thread attribute object.|
| \#include <pthread.h> | int pthread_attr_destroy(pthread_attr_t \*attr); | Destroys a thread attribute object.|
| \#include <pthread.h> | int pthread_attr_getstacksize(const pthread_attr_t \*attr, size_t \*stacksize); | Obtains the stack size of a thread attribute object.|
| \#include <pthread.h> | int pthread_attr_setstacksize(pthread_attr_t \*attr, size_t stacksize); | Sets the stack size for a thread attribute object.|
| \#include <pthread.h> | int pthread_attr_getschedparam(const pthread_attr_t \*attr, struct sched_param \*param); | Obtains scheduling parameter attributes of a thread attribute object.|
| \#include <pthread.h> | int pthread_attr_setschedparam(pthread_attr_t \*attr, const struct sched_param \*param); | Sets scheduling parameter attributes for a thread attribute object.|
| \#include <pthread.h> | int pthread_getname_np(pthread_t pthread, char\*name, size_t len); | Obtains the thread name.|
| \#include <pthread.h> | int pthread_setname_np(pthread_t pthread, constchar \*name); | Sets the thread name.|
| \#include <pthread.h> | int pthread_cond_broadcast(pthread_cond_t \*c); | Unblocks all threads that are currently blocked on the condition variable **cond**.|
| \#include <pthread.h> | int pthread_cond_signal(pthread_cond_t \*c); | Unblocks a thread.|
| \#include <pthread.h> | int pthread_cond_wait(pthread_cond_t \*__restrictc, pthread_mutex_t \*__restrict m); | Waits for the condition.|
| \#include <sys/stat.h> | int stat(const char \*restrict path, struct stat \*restrict buf); | Obtains file information.|
| \#include <unistd.h> | int unlink(const char \*pathname); | Deletes a file.|
| \#include <fcntl.h | int open(const char \*path, int oflags, ...); | Opens a file. If the file does not exist, create a file and open it.|
| \#include <nistd.h> | int close(int fd); | Closes a file.|
| \#include <stdio.h> | int rename(const char \*oldpath, const char \*newpath); | Renames a file.|
| \#include <dirent.h> | DIR \*opendir(const char \*dirname); | Opens the specified directory.|
| \#include <dirent.h> | int closedir(DIR \*dir); | Closes the specified directory.|
| \#include <sys/mount.h> | int mount(const char \*source, const char \*target, const char \*filesystemtype, unsigned long mountflags, const void \*data); | Mounts a file system.|
| \#include <sys/mount.h> | int umount(const char \*target); | Unmounts a file system.|
| \#include <sys/mount.h> | int umount2(const char \*target, int flag); | Unmounts a file system.|
| \#include <sys/stat.h> | int fsync(int fd); | Synchronizes the file associated with the specified file descriptor to the storage device.|
| \#include <sys/stat.h> | int mkdir(const char \*pathname, mode_t mode); | Creates a directory.|
| \#include <unistd.h> | int rmdir(const char \*path); | Deletes a directory.|
| \#include <sys/stat.h> | int fstat(int fd, struct stat \*buf); | Obtains file status.|
| \#include <sys/statfs.h> | int statfs(const char \*path, struct statfs \*buf); | Obtains the file system information for a file in a specified path.|
**Table 3** APIs for time management
| Header File| API| Description|
| -------- | -------- | -------- |
| \#include <sys/time.h> | int gettimeofday(struct timeval \*tv, struct timezone \*tz); | Obtains the time. Currently, time zone is not supported, and the return value of **tz** is empty.|
| \#include <time.h> | struct tm \*gmtime(const time_t \*timep); | Converts the date and time to broken-down time or ASCII.|
| \#include <time.h> | struct tm \*localtime(const time_t \*timep); | Obtains the local time.|
| \#include <time.h> | struct tm \*localtime_r(const time_t \*timep, struct tm \*result); | Obtains the local time.|
| \#include <time.h> | time_t mktime(struct tm \*tm); | Converts the date and time to broken-down time or ASCII.|
| \#include <time.h> | size_t strftime(char \*s, size_t max, const char \*format,const struct tm \*tm); | Formats the date and time.|
| \#include <unistd.h> | int usleep(useconds_t usec); | Goes to hibernation, in microseconds.|
| \#include <time.h> | int nanosleep(const struct timespec \*tspec1, structtimespec \*tspec2); | Suspends the current thread till the specified time.|
| \#include <time.h> | int clock_gettime(clockid_t id, struct timespec \*tspec); | Obtains the clock time.|
| \#include <time.h> | int timer_create(clockid_t id, struct sigevent \*__restrict evp, timer_t \*__restrict t); | Creates a timer for a thread.|
| \#include <time.h> | int timer_delete(timer_t t); | Deletes the timer for a thread.|
| \#include <time.h> | int timer_settime(timer_t t, int flags, const structitimerspec \*__restrict val, struct itimerspec \*__restrict old); | Sets a timer for a thread.|
| \#include <time.h> | time_t time (time_t \*t); | Obtains the time.|
| \#include <time.h> | char \*strptime(const char \*s, const char \*format, struct tm \*tm); | Converts the time string into the time **tm** structure.|
**Table 4** APIs for util
| Header File| API| Description|
| -------- | -------- | -------- |
| \#include <stdlib.h> | int atoi(const char \*nptr); | Converts the string pointed to by **nptr** into an integer (**int** type).|
| \#include <stdlib.h> | long atol(const char \*nptr); | Converts the string pointed to by **nptr** into a long Integer (long type).|
| \#include <stdlib.h> | long long atoll(const char \*nptr); | Converts the string pointed to by **nptr** into a long long Integer (long long type).|
| \#include <ctype.h> | int isalnum(int c); | Checks whether the passed character is alphanumeric.|
| \#include <ctype.h> | int isascii(int c); | Checks whether the passed character is an ASCII character.|
| \#include <ctype.h> | int isdigit(int c); | Checks whether the passed character is a digit.|
| \#include <ctype.h> | int islower(int c); | Checks whether the passed character is in lowercase.|
| \#include <ctype.h> | int isprint(int c); | Checks whether the passed character is printable, including spaces.|
| \#include <ctype.h> | int isspace(int c); | Checks whether the passed character is a white-space character.|
| \#include <ctype.h> | int isupper(int c); | Checks whether the passed character is in uppercase.|
| \#include <ctype.h> | int isxdigit(int c); | Checks whether the passed character is a hexadecimal number.|
| \#include <stdlib.h> | long int random (void); | Generates a pseudo-random number.|
| \#include <stdlib.h> | void srandom(unsigned int seed); | Initializes the random number generator.|
| \#include <ctype.h> | int tolower(int c); | Converts the given letter to lowercase.|
| \#include <ctype.h> | int toupper(int c); | Converts the given letter to uppercase.|
| \#include <stdarg.h> | type va_arg(va_list ap, type); | Retrieves the next argument in the parameter list with **type**. |
| \#include <stdarg.h> | void va_start(va_list ap, last); | Defines the beginning of the list of variable arguments.|
| \#include <string.h> | char \*strchr(const char \*s, int c); | Searches for a character in a string.|
| \#include <string.h> | int strcmp(const char \*s1, const char \*s2); | Compares two strings.|
| \#include <string.h> | size_t strcspn(const char \*s, const char \*reject); | Obtains the length of the initial segment of the string **s** which does not contain any of bytes in the string **reject**.|
| \#include <string.h> | char \*strdup(const char \*s); | Copies a string to a new position.|
| \#include <string.h> | size_t strlen(const char \*s); | Obtains the length of a string.|
| \#include <strings.h> | int strncasecmp(const char \*s1, const char \*s2, size_t n); | Compares the bytes of the specified length in two strings, ignoring case.|
| \#include <strings.h> | int strcasecmp(const char \*s1, const char \*s2); | Compares two strings, ignoring case.|
| \#include <string.h> | int strncmp(const char \*s1, const char \*s2, size_t n); | Compares the bytes of the specified length in two strings.|
| \#include <string.h> | char \*strrchr(const char \*s, int c); | Searches for the last occurrence of a character in a string.|
| \#include <string.h> | char \*strstr(const char \*haystack, const char \*needle); | Searches for the specified substring in a string.|
| \#include <stdlib.h> | long int strtol(const char \*nptr, char \*\*endptr, int base); | Converts the string pointed to by **nptr** into a **long int** value according to the given **base**.|
| \#include <stdlib.h> | unsigned long int strtoul(const char \*nptr, char\*\*endptr, int base); | Converts the string pointed to by **nptr** into an unsigned long integer.|
| \#include <stdlib.h> | unsigned long long int strtoull(const char \*nptr,char \*\*endptr, int base); | Converts the string pointed to by **nptr** into an unsigned long long integer.|
| \#include <regex.h> | int regcomp(regex_t \*preg, const char \*regex,int cflags); | Compiles a regular expression.|
| \#include <regex.h> | int regexec(const regex_t \*preg, const char \*string, size_t nmatch, regmatch_t pmatch[], int eflags); | Executes the compiled regular expression.|
| \#include <stdio.h> | int fseek(FILE \*stream, long offset, int whence); | Sets the position of the stream pointer.|
| \#include <stdio.h> | long ftell(FILE \*stream); | Obtains the position of the stream pointer.|
| \#include <stdio.h> | size_t fwrite(const void \*ptr, size_t size, size_tnmemb,FILE \*stream); | Writes data to a stream.|
| \#include <stdio.h> | void perror(const char \*s); | Prints system error information.|
| \#include <stdio.h> | void rewind(FILE \*stream); | Sets the position to the beginning of the file of the specified stream.|
| \#include <unistd.h> | ssize_t write(int fd, const void \*buf, size_t size); | Writes data a file.|
| \#include <unistd.h> | ssize_t read(int fd, void \*buf, size_t size); | Reads data from a file.|
**Table 7** APIs for network
| Header File| API| Description|
| -------- | -------- | -------- |
| \#include <sys/socket.h> | void freeaddrinfo(struct addrinfo \*res); | Releases the dynamic memory allocated using **getaddrinfo**.|
| \#include <sys/socket.h> | int getaddrinfo(const char \*restrict nodename, constchar \*restrict servname, const struct addrinfo \*restricthints, struct addrinfo \*\*restrict res); | Obtains a list of IP addresses and port numbers for the specified host and service.|
| \#include <sys/socket.h> | int getnameinfo(const struct sockaddr \*restrict sa, socklen_t salen, char \*restrict node, socklen_t nodelen, char \*restrict service, socklen_t servicelen, int flags); | Converts a **socketaddr** structure to a pair of host name and service strings.|
| \#include <net/if.h> | unsigned int if_nametoindex(const char \*ifname); | Obtains the index based on the network port name.|
| \#include <arpa/inet.h> | in_addr_t inet_addr(const char \*cp); | Converts the network host address in dotted decimal notation to binary format.|
| \#include <arpa/inet.h> | char \*inet_ntoa(struct in_addr in); | Converts the network host address in binary format to dotted decimal notation.|
| \#include <arpa/inet.h> | const char \*inet_ntop(int af, const void \*src, char \*dst, socklen_t size); | Converts the network address in standard text format to numeric binary format.|
| \#include <arpa/inet.h> | int inet_pton(int af, const char \*src, void \*dst); | Converts the network address in standard text format to numeric binary format.|
| \#include <sys/socket.h> | int listen(int sockfd, int backlog); | Listens for connections on a socket.|
| \#include <sys/socket.h> | ssize_t recvmsg(int sockfd, struct msghdr \*msg, int flags); | Receives a message from a socket. Currently, only the scenario with **iov** of **1** is supported and ancillary messages are not supported.|
| \#include <sys/socket.h> | ssize_t send(int sockfd, const void \*buf, size_t len, int flags); | Sends a message on a socket.|
| \#include <sys/socket.h> | ssize_t sendmsg(int sockfd, const struct msghdr \*msg, int flags); | Sends a message on a socket. Ancillary messages are not supported.|
| \#include <sys/socket.h> | ssize_t sendto(int sockfd, const void \*buf, size_t len, intflags,const struct sockaddr \*dest_addr, socklen_t addrlen); | Sends a message on a socket.|
| \#include <sys/socket.h> | int setsockopt(int sockfd, int level, int optname,constvoid \*optval, socklen_t optlen); | Sets options associated with a socket.|
**Table 8** APIs for memory management
| Header File| API| Description|
| -------- | -------- | -------- |
| \#include <string.h> | int memcmp(const void \*s1, const void \*s2, size_t n); | Compares successive elements from two arrays until it finds elements that are different.|
| \#include <string.h> | void \*memcpy(void \*dest, const void \*src, size_t n); | Copies *n* bytes from the source memory area pointed to by **src** to the destination memory area pointed to by **dest**.|
| \#include <stdlib.h> | void free(void \*ptr); | Release the memory space pointed to by **ptr**.|
**Table 9** APIs for IPC
| Header File| API| Description|
| -------- | -------- | -------- |
| \#include <semaphore.h> | int sem_timedwait(sem_t \*sem, const struct timespec \*abs_timeout); | Locks the semaphore referenced by **sem** as in the **sem_wait()** function.|
| \#include <semaphore.h> | int sem_destroy(sem_t \*sem); | Destroys the specified unnamed semaphore.|
| \#include <semaphore.h> | int sem_init(sem_t \*sem, int pshared, unsigned int value); | Creates and initializes an unnamed semaphore.|
| \#include <semaphore.h> | int sem_post(sem_t \*sem); | Increments the semaphore count by 1.|
| \#include <semaphore.h> | int sem_wait(sem_t \*sem); | Obtains the semaphore.|
| \#include <mqueue.h> | mqd_t mq_open(const char \*mqName, int openFlag, ...); | Opens an existing message queue with the specified name or creates a message queue.|
| \#include <mqueue.h> | int mq_close(mqd_t personal); | Closes a message queue with the specified descriptor.|
| \#include <mqueue.h> | int mq_unlink(const char \*mqName); | Deletes the message queue of the specified name.|
| \#include <mqueue.h> | int mq_send(mqd_t personal, const char \*msg,size_t msgLen, unsigned int msgPrio); | Puts a message with the specified content and length into a message queue.|
| \#include <mqueue.h> | ssize_t mq_receive(mqd_t personal, char \*msg,size_t msgLen, unsigned int \*msgPrio); | Deletes the oldest message from a message queue and puts it in the buffer pointed to by **msg_ptr**.|
| \#include <mqueue.h> | int mq_timedsend(mqd_t personal, const char\*msg, size_t msgLen, unsigned int msgPrio, const struct timespec \*absTimeout) | Puts a message with the specified content and length into a message queue at the specified time.|
| \#include <mqueue.h> | ssize_t mq_timedreceive(mqd_t personal, char\*msg, size_t msgLen, unsigned int \*msgPrio, const struct timespec \*absTimeout); | Obtains a message with the specified content and length from a message queue.|
| \#include <mqueue.h> | int mq_setattr(mqd_t mqdes, const struct mq_attr \*__restrict newattr, struct mq_attr \*__restrict oldattr); | Sets the message queue attributes specified by the descriptor.|
| \#include <libc.h> | const char \*libc_get_version_string(void); | Obtains the libc version string.|
| \#include <libc.h> | int libc_get_version(void); | Obtains the libc version.|
#### Error Codes
The table below lists common error codes.
| Error Code | Value| Description|
| -------- | -------- | -------- |
| ENOERR | 0 | Success |
| EPERM | 1 | Operation not permitted |
| ENOENT | 2 | No such file or directory |
| ESRCH | 3 | No such process |
| EINTR | 4 | Interrupted system call |
| EIO | 5 | I/O error |
| ENXIO | 6 | No such device or address |
| E2BIG | 7 | Arg list too long |
| ENOEXEC | 8 | Exec format error |
| EBADF | 9 | Bad file number |
| ECHILD | 10 | No child processes |
| EAGAIN | 11 | Try again |
| ENOMEM | 12 | Out of memory |
| EACCES | 13 | Permission denied |
| EFAULT | 14 | Bad address |
| ENOTBLK | 15 | Block device required |
| EBUSY | 16 | Device or resource busy |
| EEXIST | 17 | File exists |
| EXDEV | 18 | Cross-device link |
| ENODEV | 19 | No such device |
| ENOTDIR | 20 | Not a directory |
| EISDIR | 21 | Is a directory |
| EINVAL | 22 | Invalid argument |
| ENFILE\* | 23 | File table overflow |
| EMFILE | 24 | Too many open files |
| EFBIG | 27 | File too large |
| ENOSPC | 28 | No space left on device |
| ESPIPE | 29 | Illegal seek |
| EROFS | 30 | Read-only file system |
| EMLINK | 31 | Too many links |
| EDOM | 33 | Math argument out of domain |
| ERANGE | 34 | Math result not representable |
| EDEADLK | 35 | Resource deadlock would occur |
| ENAMETOOLONG | 36 | Filename too long |
| ENOLCK | 37 | No record locks available |
| ENOSYS | 38 | Function not implemented |
| ENOTEMPTY | 39 | Directory not empty |
| ELOOP | 40 | Too many symbolic links encountered |
| ENOMSG | 42 | No message of desired type |
| EIDRM | 43 | Identifier removed |
| ELNRNG | 48 | Link number out of range |
| EBADR | 53 | Invalid request descriptor |
| EBADRQC | 56 | Invalid request code |
| ENOSTR | 60 | Device not a stream |
| ENODATA | 61 | No data available |
| ETIME | 62 | Timer expired |
| EPROTO | 71 | Protocol error |
| EBADMSG | 74 | Not a data message |
| EOVERFLOW | 75 | Value too large for defined data type |
| EMSGSIZE | 90 | Message too long |
#### Development Example
Example:
Creates a thread, transfers the information in the parent thread to the child thread, and prints the transferred information and the thread ID in the child thread.
```
#include <stdio.h>
#include <pthread.h>
pthread_t ntid;
void *ThreadFn(void *arg)
{
pthread_t tid;
while(1) {
tid = pthread_self();
printf("\n++++++++++++++ %s %s tid = %d ++++++++++++++\n", (char*)arg, __FUNCTION__, tid);
Memory management, one of the core modules of the OS, manages the memory resources of the system. Memory management primarily involves initializing, allocating, and releasing memory.
While the OS is running, the memory management module manages the memory usage of users and the OS by allocating and releasing memory. This helps achieve the optimal memory usage and usage efficiency and minimize memory fragments.
The OpenHarmony LiteOS-M kernel memory management involves static and dynamic memory management, and provides functions such as memory initialization, allocation, and release.
- Dynamic memory: memory blocks of user-specified size allocated in the dynamic memory pool.
- Advantage: Resources are allocated on demand.
- Disadvantage: Fragments may occur in the memory pool.
- Static memory: memory blocks of the fixed size \(preset during initialization\) allocated in the static memory pool.
- Advantage: Memory is allocated and released efficiently, and there is no fragment in the static memory pool.
- Disadvantage: Only the memory blocks of the fixed size can be allocated. Memory cannot be allocated on demand.
Dynamic memory management allows memory blocks of any size to be allocated from a large contiguous memory \(memory pool or heap memory\) configured in the system based on user demands when memory resources are sufficient. The memory block can be released for further use when not required. Compared with static memory management, dynamic memory management allows memory allocation on demand but causes fragmentation of memory.
The dynamic memory of the OpenHarmony LiteOS-M has optimized the memory space partitioning based on the Two-Level Segregate Fit \(TLSF\) algorithm to achieve higher performance and minimize fragmentation. The figure below shows the core algorithm of the dynamic memory.
**Figure 1** Dynamic memory algorithm for mini systems
Multiple free lists are used for management based on the size of the free memory block. The free memory blocks are divided into two parts: \[4, 127\] and \[2<sup>7</sup>, 2<sup>31</sup>\], as indicated by the size class in the above figure.
1. The memory in the range of \[4, 127\]\(lower part in the above figure) is divided into 31 parts. The size of the memory block corresponding to each part is a multiple of 4 bytes. Each part corresponds to a free list and a bit that indicates whether the free list is empty. The value **1** indicates that the free list is not empty. There are 31 bits corresponding to the 31 memory parts in the range of \[4, 127\].
2. The memory greater than 127 bytes is managed in power of two increments. The size of each range is \[2^n, 2^\(n+1\)-1\], where n is an integer in \[7, 30\]. This range is divided into 24 parts, each of which is further divided into 8 second-level \(L2\) ranges, as shown in Size Class and Size SubClass in the upper part of the above figure. Each L2 range corresponds to a free list and a bit that indicates whether the free list is empty. There are a total of 192 \(24 x 8\) L2 ranges, corresponding to 192 free lists and 192 bits.
For example, insert 40-byte free memory to a free list. The 40-byte free memory corresponds to the 10th free list in the range of \[40, 43\], and the 10th bit indicates the use of the free list. The system inserts the 40-byte free memory to the 10th free list and determines whether to update the bitmap flag. When 40-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If 580-byte free memory needs to be inserted to a free list, the 580-byte free memory corresponds to the 47th \(31 + 2 x 8\) free list in L2 range \[2^9, 2^9+2^6\], and the 47th bit indicates the use of the free list. The system inserts the 580-byte free memory to the 47th free list and determines whether to update the bitmap flag. When 580-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If the corresponding free list is empty, the system checks for a free list meeting the requirements in a larger memory range. In actual application, the system can locate the free list that meets the requirements at a time.
The figure below shows the memory management structure.
**Figure 2** Dynamic memory management structure for mini systems
The memory pool header contains the memory pool information, bitmap flag array, and free list array. The memory pool information includes the start address of the memory pool, total size of the heap memory, and attributes of the memory pool. The bitmap flag array consists of seven 32-bit unsigned integers. Each bit indicates whether the free list is inserted with free memory block nodes. The free list contains information about 223 free memory head nodes. The free memory head node information contains a memory node header and information about the previous and next nodes in the free list.
- Memory pool nodes
There are three types of nodes: free node, used node, and end node. Each memory node maintains the size and use flag of the memory node and a pointer to the previous memory node in the memory pool. The free nodes and used nodes have a data area, but the end node has no data area.
The off-chip physical memory needs to be used because the on-chip RAMs of some chips cannot meet requirements. The OpenHarmony LiteOS-M kernel can logically combine multiple discontiguous memory regions so that users are unaware of the discontiguous memory regions in the underlying layer. The OpenHarmony LiteOS-M kernel memory module inserts discontiguous memory regions into a free list as free memory nodes and marks the discontiguous parts as virtual memory nodes that have been used. In this way, the discontinuous memory regions are logically combined as a unified memory pool. The figure below shows how the discontiguous memory regions are logically integrated.
**Figure 3** Integrating discontiguous memory regions
The discontiguous memory regions are integrated into a unified memory pool as follows:
1. Call **LOS\_MemInit** to initialize the first memory region of multiple discontiguous memory regions.
2.<aname="li26042441209"></a>Obtain the start address and length of the next memory region, and calculate the **gapSize** between the current memory region and its previous memory region. The **gapSize** is considered as a used virtual node.
3. Set the size of the end node of the previous memory region to the sum of **gapSize** and **OS\_MEM\_NODE\_HEAD\_SIZE**.
4.<aname="li10604194419014"></a>Divide the current memory region into a free memory node and an end node, insert the free memory node to the free list, and set the link relationship between the nodes.
5. Repeat [2](#li26042441209) to [4](#li10604194419014) to integrate more discontiguous memory regions.
## Development Guidelines
### When to Use
Dynamic memory management allocates and manages memory resources requested by users dynamically. It is a good choice when users need memory blocks of different sizes. You can call the dynamic memory allocation function of the OS to request a memory block of the specified size. You can call the dynamic memory release function to release the memory at any time.
### Available APIs
The following table describes APIs available for OpenHarmony LiteOS-M dynamic memory management. For more details about the APIs, see the API reference.
<tbody><trid="row0415737175610"><tdclass="cellrowborder"rowspan="2"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p6485848217"><aname="p6485848217"></a><aname="p6485848217"></a>Initializing or deleting a memory pool</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p94857483110"><aname="p94857483110"></a><aname="p94857483110"></a>Initializes a dynamic memory pool of the specified size.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p154851348113"><aname="p154851348113"></a><aname="p154851348113"></a>Deletes a memory pool. It is valid only when <strongid="b68821856145419"><aname="b68821856145419"></a><aname="b68821856145419"></a>LOSCFG_MEM_MUL_POOL</strong> is enabled.</p>
</td>
</tr>
<trid="row1187514443616"><tdclass="cellrowborder"rowspan="4"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p19661710214"><aname="p19661710214"></a><aname="p19661710214"></a>Allocating or releasing dynamic memory</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p4661715214"><aname="p4661715214"></a><aname="p4661715214"></a>Allocates memory of the specified size from the dynamic memory pool.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p1569175218"><aname="p1569175218"></a><aname="p1569175218"></a>Releases the memory allocated from the specified dynamic memory.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p56181718219"><aname="p56181718219"></a><aname="p56181718219"></a>Re-allocates a memory block of the required size and copies data from the original block to the newly allocated bock. If the new memory block is successfully allocated, the original memory block will be released.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p26171714214"><aname="p26171714214"></a><aname="p26171714214"></a>Allocates the memory of the specified size and aligned based on the specified bytes from a dynamic memory pool.</p>
</td>
</tr>
<trid="row28531740101112"><tdclass="cellrowborder"rowspan="4"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p39818810129"><aname="p39818810129"></a><aname="p39818810129"></a>Obtaining memory pool information</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p129820881212"><aname="p129820881212"></a><aname="p129820881212"></a>Obtains the total size of the specified dynamic memory pool.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p1598889124"><aname="p1598889124"></a><aname="p1598889124"></a>Obtains the total memory usage of the specified dynamic memory pool.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p139815820126"><aname="p139815820126"></a><aname="p139815820126"></a>Obtains the memory structure information of the specified memory pool, including the free memory, used memory, number of free memory blocks, number of used memory blocks, and maximum size of the free memory block.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p998148101218"><aname="p998148101218"></a><aname="p998148101218"></a>Prints information about all initialized memory pools in the system, including the start address, size, total free memory, used memory, maximum size of the free memory block, number of free memory blocks, and number of used memory blocks of each memory pool. It is valid only when <strongid="b899016549611"><aname="b899016549611"></a><aname="b899016549611"></a>LOSCFG_MEM_MUL_POOL</strong> is enabled.</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p437618158141"><aname="p437618158141"></a><aname="p437618158141"></a>Prints the size and number of free memory blocks in the specified memory pool.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p19976124812146"><aname="p19976124812146"></a><aname="p19976124812146"></a>Prints the size and number of used memory blocks in the specified memory pool.</p>
</td>
</tr>
<trid="row0715201211155"><tdclass="cellrowborder"valign="top"width="12.85128512851285%"headers="mcps1.2.4.1.1 "><pid="p13599202711513"><aname="p13599202711513"></a><aname="p13599202711513"></a>Checking memory pool integrity</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p15644611153"><aname="p15644611153"></a><aname="p15644611153"></a>Checks the integrity of the specified memory pool. It is valid only when <strongid="b1215338788"><aname="b1215338788"></a><aname="b1215338788"></a>LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK</strong> is enabled.</p>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p20532149635"><aname="p20532149635"></a><aname="p20532149635"></a>Logically integrates multiple discontiguous memory regions into a unified memory pool. It is valid only when <strongid="b139814570571"><aname="b139814570571"></a><aname="b139814570571"></a>LOSCFG_MEM_MUL_REGIONS</strong> is enabled. If the memory pool pointer parameter <strongid="b193777151040"><aname="b193777151040"></a><aname="b193777151040"></a>pool</strong> is empty, initialize the first of the multiple memory regions in the memory pool and insert other memory regions as free nodes. If <strongid="b12330104011612"><aname="b12330104011612"></a><aname="b12330104011612"></a>pool</strong> is not empty, insert the multiple memory regions into the specified memory pool as free nodes.</p>
>- The dynamic memory module manages memory through control block structures, which consume extra memory. Therefore, the actual memory space available to users is less than the value of **OS\_SYS\_MEM\_SIZE**.
>- The **LOS\_MemAllocAlign** and **LOS\_MemMallocAlign** APIs consume extra memory for memory alignment, which may cause memory loss. When the memory used for alignment is freed up, the lost memory will be reclaimed.
>- The discontiguous memory regions passed by the **LosMemRegion** array to the **LOS\_MemRegionsAdd** API must be sorted in ascending order by memory start address in memory regions, and the memory regions cannot overlap.
### How to Develop
The typical development process of dynamic memory is as follows:
1. Call the **LOS\_MemInit** API to initialize a memory pool.
After a memory pool is initialized, a memory pool control header and end node will be generated, and the remaining memory is marked as free nodes. The end node is the last node in the memory pool, and its size is **0**.
1. Call the **LOS\_MemAlloc** API to allocate dynamic memory of any size.
The system checks whether the dynamic memory pool has free memory blocks greater than the requested size. If yes, the system allocates a memory block and returns the pointer to the memory block. If no, the system returns NULL. If the memory block allocated is greater than the requested size, the system splits the memory block and inserts the remaining memory block to the free list.
1. Call the **LOS\_MemFree** API to release dynamic memory.
The released memory block can be reused. When **LOS\_MemFree** is called, the memory block will be reclaimed and marked as free nodes. When memory blocks are reclaimed, adjacent free nodes are automatically merged.
### Development Example
This example implements the following:
1. Initialize a dynamic memory pool.
2. Allocate a memory block from the dynamic memory pool.
The static memory is a static array. The block size in the static memory pool is set during initialization and cannot be changed after initialization.
The static memory pool consists of a control block **LOS\_MEMBOX\_INFO** and several memory blocks **LOS\_MEMBOX\_NODE** of the same size. The control block is located at the head of the memory pool and used for memory block management. It contains the memory block size \(**uwBlkSize**\), number of memory blocks \(**uwBlkNum**\), number of allocated memory blocks \(**uwBlkCnt**\), and free list \(**stFreeList**\). Memory is allocated and released by block size. Each memory block contains the pointer **pstNext** that points to the next memory block.
**Figure 1** Static memory
![](figures/static-memory.png"static-memory")
## Development Guidelines
### When to Use
Use static memory allocation to obtain memory blocks of the fixed size. When the memory is no longer required, release the static memory.
### Available APIs
The following table describes APIs available for OpenHarmony LiteOS-M static memory management. For more details about the APIs, see the API reference.
<tdclass="cellrowborder"valign="top"width="61.33613361336133%"headers="mcps1.2.4.1.3 "><pid="p5990113174414"><aname="p5990113174414"></a><aname="p5990113174414"></a>Initialize a static memory pool and sets the start address, total size, and size of each memory block based on the input parameters.</p>
<tdclass="cellrowborder"valign="top"width="61.33613361336133%"headers="mcps1.2.4.1.3 "><pid="p18367184916448"><aname="p18367184916448"></a><aname="p18367184916448"></a>Clears the memory blocks allocated from the static memory pool.</p>
</td>
</tr>
<trid="row1187514443616"><tdclass="cellrowborder"rowspan="2"valign="top"width="16.19161916191619%"headers="mcps1.2.4.1.1 "><pid="p64541711458"><aname="p64541711458"></a><aname="p64541711458"></a>Allocating or releasing static memory</p>
<tdclass="cellrowborder"valign="top"width="61.33613361336133%"headers="mcps1.2.4.1.3 "><pid="p5454177164520"><aname="p5454177164520"></a><aname="p5454177164520"></a>Allocates a memory block from a specified static memory pool.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p545415704514"><aname="p545415704514"></a><aname="p545415704514"></a>Releases a memory block allocated from the static memory pool.</p>
</td>
</tr>
<trid="row19101718144518"><tdclass="cellrowborder"rowspan="2"valign="top"width="16.19161916191619%"headers="mcps1.2.4.1.1 "><pid="p15927427144615"><aname="p15927427144615"></a><aname="p15927427144615"></a>Obtaining or printing static memory pool information</p>
<tdclass="cellrowborder"valign="top"width="61.33613361336133%"headers="mcps1.2.4.1.3 "><pid="p139271327114620"><aname="p139271327114620"></a><aname="p139271327114620"></a>Obtains information about a specified static memory pool, including the total number of memory blocks in the memory pool, number of allocated memory blocks, and size of each memory block.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p49274279460"><aname="p49274279460"></a><aname="p49274279460"></a>Prints information about all nodes in a specified static memory pool (the print level is <strongid="b1937798153019"><aname="b1937798153019"></a><aname="b1937798153019"></a>LOS_INFO_LEVEL</strong>). The information includes the start address of the memory pool, memory block size, total number of memory blocks, start address of each idle memory block, and start addresses of all memory blocks.</p>
>The number of memory blocks in the memory pool after initialization is not equal to the total memory size divided by the memory block size. The reason is the control block of the memory pool and the control header of each memory block have memory overheads. When setting the total memory size, you need to consider these factors.
### How to Develop
The typical development process of static memory is as follows:
1. Plan a memory space as the static memory pool.
2. Call the **LOS\_MemboxInit** API to initialize the static memory pool.
During initialization, the memory space specified by the input parameter is divided into multiple blocks \(the number of blocks depends on the total static memory size and the block size\). Insert all memory blocks to the free list, and place the control header at the beginning of the memory.
3. Call the **LOS\_MemboxAlloc** API to allocate static memory.
The system allocates the first free memory block from the free list and returns the start address of this memory block.
4. Call the **LOS\_MemboxClr** API.
Clear the memory block corresponding to the address contained in the input parameter.
5. Call the **LOS\_MemboxFree** API.
Add the memory block to the free list.
### Development Example
This example implements the following:
1. Initialize a static memory pool.
2. Allocate a memory block from the static memory pool.
3. Store a piece of data in a memory block.
4. Print the data in the memory block.
5. Clear the data in the memory block.
6. Release the memory block.
The sample code is as follows:
```
#include "los_membox.h"
VOID Example_StaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize = 10;
UINT32 boxSize = 100;
UINT32 boxMem[1000];
UINT32 ret;
/* Initialize the memory pool.*/
ret = LOS_MemboxInit(&boxMem[0], boxSize, blkSize);
Memory management, one of the core modules of the OS, manages the memory resources of the system. Memory management primarily involves initializing, allocating, and releasing memory.
While the OS is running, the memory management module manages the memory usage of users and the OS by allocating and releasing memory. This helps achieve the optimal memory usage and usage efficiency and minimize memory fragments.
The OpenHarmony LiteOS-M kernel memory management involves static and dynamic memory management, and provides functions such as memory initialization, allocation, and release.
- Dynamic memory: memory blocks of user-specified size allocated in the dynamic memory pool.
- Advantage: Resources are allocated on demand.
- Disadvantage: Fragments may occur in the memory pool.
- Static memory: memory blocks of the fixed size (preset during initialization) allocated in the static memory pool.
- Advantage: Memory is allocated and released efficiently, and there is no memory fragment in the memory pool.
- Disadvantage: Only the memory blocks of the fixed size can be allocated. Memory cannot be allocated on demand.
## Static Memory
### Working Principles
The static memory is a static array. The block size in the static memory pool is set during initialization and cannot be changed after initialization.
The static memory pool consists of a control block **LOS_MEMBOX_INFO** and several memory blocks **LOS_MEMBOX_NODE** of the same size. The control block is located at the head of the memory pool and used for memory block management. It contains the memory block size (**uwBlkSize**), number of memory blocks (**uwBlkNum**), number of allocated memory blocks (**uwBlkCnt**), and free list (**stFreeList**). Memory is allocated and released by block. Each memory block contains the pointer **pstNext** that points to the next memory block.
**Figure 1** Static memory
![](figures/static-memory.png"static-memory")
### Development Guidelines
#### When to Use
Use static memory allocation to obtain memory blocks of the fixed size. When the memory is no longer required, release the static memory.
#### Available APIs
The following table describes APIs available for OpenHarmony LiteOS-M static memory management. For more details about the APIs, see the API reference.
**Table 1** APIs of the static memory module
| Category| API|
| -------- | -------- |
| Initializing the static memory pool| **LOS_MemboxInit**: initializes a static memory pool, that is, sets the start address, total size, and size of each memory block based on input parameters.|
| Clearing static memory blocks| **LOS_MemboxClr**: clears the memory blocks allocated from the static memory pool.|
| Allocating or releasing static memory| - **LOS_MemboxAlloc**: allocates a memory block from a specified static memory pool.<br>- **LOS_MemboxFree**: releases a memory block allocated from the static memory pool.|
| Obtaining or printing static memory pool information| - **LOS_MemboxStatisticsGet**: obtains information about a specified static memory pool, including the total number of memory blocks in the memory pool, number of allocated memory blocks, and size of each memory block.<br>- **LOS_ShowBox**: prints information about all nodes in a specified static memory pool (the print level is **LOS_INFO_LEVEL**). The information includes the start address of the memory pool, memory block size, total number of memory blocks, start address of each idle memory block, and start addresses of all memory blocks.|
> The number of memory blocks in the memory pool after initialization is not equal to the total memory size divided by the memory block size. The reason is the control block of the memory pool and the control header of each memory block have memory overheads. When setting the total memory size, you need to consider these factors.
#### How to Develop
The typical development process of static memory is as follows:
1. Plan a memory area as a static memory pool.
2. Call **LOS_MemboxInit** to initialize the static memory pool.
During initialization, the memory space specified by the input parameter is divided into multiple blocks (the number of blocks depends on the total static memory size and the block size). Insert all memory blocks to the free list, and place the control header at the beginning of the memory.
3. Call **LOS_MemboxAlloc** to allocate the static memory.
The system obtains the first free block from the free list and returns the start address of the memory block.
4. Call **LOS_MemboxClr**.
Clear the memory block corresponding to the address contained in the input parameter.
5. Call **LOS_MemboxFree**.
Add the memory block to the free list.
#### Development Example
This example implements the following:
1. Initialize a static memory pool.
2. Allocate a memory block from the static memory pool.
3. Store a piece of data in a memory block.
4. Print the data in the memory block.
5. Clear the data in the memory block.
6. Release the memory block.
The sample code is as follows:
```
#include "los_membox.h"
VOID Example_StaticMem(VOID)
{
UINT32 *mem = NULL;
UINT32 blkSize = 10;
UINT32 boxSize = 100;
UINT32 boxMem[1000];
UINT32 ret;
/* Initialize the memory pool. */
ret = LOS_MemboxInit(&boxMem[0], boxSize, blkSize);
if(ret != LOS_OK) {
printf("Membox init failed!\n");
return;
} else {
printf("Membox init success!\n");
}
/* Request a memory block. */
mem = (UINT32 *)LOS_MemboxAlloc(boxMem);
if (NULL == mem) {
printf("Mem alloc failed!\n");
return;
}
printf("Mem alloc success!\n");
/* Assign a value. */
*mem = 828;
printf("*mem = %d\n", *mem);
/* Clear the memory. */
LOS_MemboxClr(boxMem, mem);
printf("Mem clear success \n *mem = %d\n", *mem);
/ Release the memory. */
ret = LOS_MemboxFree(boxMem, mem);
if (LOS_OK == ret) {
printf("Mem free success!\n");
} else {
printf("Mem free failed!\n");
}
return;
}
```
#### Verification
The output is as follows:
```
Membox init success!
Mem alloc success!
*mem = 828
Mem clear success
*mem = 0
Mem free success!
```
## Dynamic Memory
### Working Principles
Dynamic memory management allows memory blocks of any size to be allocated from a large contiguous memory (memory pool or heap memory) configured in the system based on user demands when memory resources are sufficient. The memory block can be released for further use when not required. Compared with static memory management, dynamic memory management allows memory allocation on demand but causes fragmentation of memory.
The dynamic memory of the OpenHarmony LiteOS-M has optimized the memory space partitioning based on the Two-Level Segregate Fit (TLSF) algorithm to achieve higher performance and minimize fragmentation. The figure below shows the core algorithm of the dynamic memory.
**Figure 1** Dynamic memory algorithm for mini systems
Multiple free lists are used for management based on the size of the free memory block. The free memory blocks are divided into two parts: [4, 127] and [2<sup>7</sup>, 2<sup>31</sup>], as indicated by the size class in the above figure.
1. The memory in the range of [4, 127] (lower part in the figure) is equally divided into 31 parts. The size of the memory block corresponding to each part is a multiple of 4 bytes. Each part corresponds to a free list and a bit that indicates whether the free list is empty. The value **1** indicates that the free list is not empty. There are 31 bits corresponding to the 31 memory parts in the range of [4, 127].
2. The memory greater than 127 bytes is managed in power of two increments. The size of each range is [2^n, 2^(n+1) -1], where n is an integer in [7, 30]. This range is divided into 24 parts, each of which is further divided into 8 second-level (L2) ranges, as shown in Size Class and Size SubClass in the upper part of the figure. Each L2 range corresponds to a free list and a bit that indicates whether the free list is empty. There are a total of 192 (24 x 8) L2 ranges, corresponding to 192 free lists and 192 bits.
For example, insert 40-byte free memory to a free list. The 40-byte free memory corresponds to the 10th free list in the range of [40, 43], and the 10th bit indicates the use of the free list. The system inserts the 40-byte free memory to the 10th free list and determines whether to update the bitmap flag. When 40-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list.
If 580-byte free memory needs to be inserted to a free list, the 580-byte free memory corresponds to the 47th (31 + 2 x 8) free list in L2 range [2^9, 2^9+2^6], and the 47th bit indicates the use of the free list. The system inserts the 580-byte free memory to the 47th free list and determines whether to update the bitmap flag. When 580-byte memory is requested, the system obtains the free list corresponding to the memory block of the requested size based on the bitmap flag, and then obtains a free memory node from the free list. If the size of the allocated node is greater than the memory requested, the system splits the node and inserts the remaining node to the free list. If the corresponding free list is empty, the system checks for a free list meeting the requirements in a larger memory range. In actual application, the system can locate the free list that meets the requirements at a time.
The figure below shows the memory management structure.
**Figure 2** Dynamic memory management structure for mini systems
The memory pool header contains the memory pool information, bitmap flag array, and free list array. The memory pool information includes the start address of the memory pool, total size of the heap memory, and attributes of the memory pool. The bitmap flag array consists of seven 32-bit unsigned integers. Each bit indicates whether the free list is inserted with free memory block nodes. The free list contains information about 223 free memory head nodes. The free memory head node information contains a memory node header and information about the previous and next nodes in the free list.
- Memory pool nodes
There are three types of nodes: free node, used node, and end node. Each memory node maintains the size and use flag of the memory node and a pointer to the previous memory node in the memory pool. The free nodes and used nodes have a data area, but the end node has no data area.
The off-chip physical memory needs to be used because the on-chip RAMs of some chips cannot meet requirements. The OpenHarmony LiteOS-M kernel can logically combine multiple discontiguous memory regions so that users are unaware of the discontiguous memory regions in the underlying layer. The OpenHarmony LiteOS-M kernel memory module inserts discontiguous memory regions into a free list as free memory nodes and marks the discontiguous parts as virtual memory nodes that have been used. In this way, the discontinuous memory regions are logically combined as a unified memory pool.
The figure below shows how the discontiguous memory regions are logically integrated.
**Figure 3** Integrating discontiguous memory regions
The discontiguous memory regions are integrated into a unified memory pool as follows:
1. Call **LOS_MemInit** to initialize the first memory region of multiple discontiguous memory regions.
2. Obtain the start address and length of the next memory region, and calculate the **gapSize** between the current memory region and its previous memory region. The **gapSize** is considered as a used virtual node.
3. Set the size of the end node of the previous memory region to the sum of **gapSize** and **OS_MEM_NODE_HEAD_SIZE**.
4. Divide the current memory region into a free memory node and an end node, insert the free memory node to the free list, and set the link relationship between the nodes.
5. Repeat 2 to 4 to integrate more discontiguous memory regions.
### Development Guidelines
#### When to Use
Dynamic memory management allocates and manages memory resources requested by users dynamically. It is a good choice when users need memory blocks of different sizes. You can call the dynamic memory allocation function of the OS to request a memory block of the specified size. You can call the dynamic memory release function to release the memory at any time.
#### Available APIs
The following table describes APIs available for OpenHarmony LiteOS-M dynamic memory management. For more details about the APIs, see the API reference.
**Table 1** APIs of the dynamic memory module
| Category| Description|
| -------- | -------- |
| Initializing or deleting a memory pool| - **LOS_MemInit**: initializes a dynamic memory pool of the specified size.<br>- **LOS_MemDeInit**: deletes a memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled.|
| Allocating or releasing dynamic memory| - **LOS_MemAlloc**: allocates memory of the specified size from the dynamic memory pool.<br>- **LOS_MemFree**: releases the memory allocated from the specified dynamic memory.<br>- **LOS_MemRealloc**: re-allocates a memory block of the required size and copies data from the original block to the newly allocated bock. If the new memory block is successfully allocated, the original memory block will be released.|
| Obtaining memory pool information| - **LOS_MemPoolSizeGet**: obtains the total size of the specified dynamic memory pool.<br>- **LOS_MemTotalUsedGet**: obtains the total memory usage of the specified dynamic memory pool.<br>- **LOS_MemInfoGet**: obtains the memory structure information of the specified memory pool, including the free memory, used memory, number of free memory blocks, number of used memory blocks, and maximum size of the free memory block.<br>- **LOS_MemPoolList**: prints information about all initialized memory pools in the system, including the start address, size, total free memory, used memory, maximum size of the free memory block, number of free memory blocks, and number of used memory blocks of each memory pool. It is valid only when **LOSCFG_MEM_MUL_POOL** is enabled. |
| Obtaining memory block information| - **LOS_MemFreeNodeShow**: prints the size and number of free memory blocks in the specified memory pool.<br>- **LOS_MemUsedNodeShow**: prints the size and number of used memory blocks in the specified memory pool.|
| Checking memory pool integrity| **LOS_MemIntegrityCheck**: checks the integrity of the specified memory pool. It is valid only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled.|
| Adding discontiguous memory regions| **LOS_MemRegionsAdd**: logically integrates multiple discontiguous memory regions into a unified memory pool. This parameter is valid only when **LOSCFG_MEM_MUL_REGIONS** is enabled. If the memory pool pointer **pool** is empty, initialize the first of the multiple memory regions in the memory pool and insert other memory regions as free nodes. If **pool** is not empty, insert the multiple memory regions into the specified memory pool as free nodes.|
> - The dynamic memory module manages memory through control block structures, which consume extra memory. Therefore, the actual memory space available to users is less than the value of **OS_SYS_MEM_SIZE**.
>
> - The **LOS_MemAllocAlign** and **LOS_MemMallocAlign** APIs consume extra memory for memory alignment, which may cause memory loss. When the memory used for alignment is freed up, the lost memory will be reclaimed.
>
> - The discontiguous memory regions passed by the **LosMemRegion** array to the **LOS_MemRegionsAdd** API must be sorted in ascending order by memory start address in memory regions, and the memory regions cannot overlap.
#### How to Develop
The typical development process of dynamic memory is as follows:
1. Call **LOS_MemInit** to initialize a memory pool.
After a memory pool is initialized, a memory pool control header and end node will be generated, and the remaining memory is marked as free nodes. The end node is the last node in the memory pool, and its size is **0**.
2. Call **LOS_MemAlloc** to allocate dynamic memory of any size.
The system checks whether the dynamic memory pool has free memory blocks greater than the requested size. If yes, the system allocates a memory block and returns the pointer to the memory block. If no, the system returns NULL. If the memory block allocated is greater than the requested size, the system splits the memory block and inserts the remaining memory block to the free list.
3. Call **LOS_MemFree** to release dynamic memory.
The released memory block can be reused. When **LOS_MemFree** is called, the memory block will be reclaimed and marked as free nodes. When memory blocks are reclaimed, adjacent free nodes are automatically merged.
#### Development Example
This example implements the following:
1. Initialize a dynamic memory pool.
2. Allocate a memory block from the dynamic memory pool.
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
### 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.
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.
> 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 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**
The MMC device partition is mounted to the **user** directory.
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "sys/stat.h"
#include "fcntl.h"
#include "unistd.h"
#define LOS_OK 0
#define LOS_NOK -1
int FatfsTest(void)
{
int ret;
int fd = -1;
ssize_t len;
off_t off;
char dirName[20] = "user/test";
char fileName[20] = "user/test/file.txt";
char writeBuf[20] = "Hello OpenHarmony!";
char readBuf[20] = {0};
/* Create the user/test directory. */
ret = mkdir(dirName, 0777);
if (ret != LOS_OK) {
printf("mkdir failed.\n");
return LOS_NOK;
}
/* 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");
return LOS_NOK;
}
/* Write the content from writeBuf to the file. */
len = write(fd, writeBuf, strlen(writeBuf));
if (len != strlen(writeBuf)) {
printf("write file failed.\n");
return LOS_NOK;
}
/* 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 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 length of readBuf to readBuf. */
len = read(fd, readBuf, sizeof(readBuf));
if (len != strlen(writeBuf)) {
printf("read file failed.\n");
return LOS_NOK;
}
printf("%s\n", readBuf);
/* Close the file. */
ret = close(fd);
if (ret != LOS_OK) {
printf("close failed.\n");
return LOS_NOK;
}
/* 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. */
ret = rmdir(dirName);
if (ret != LOS_OK) {
printf("rmdir failed.\n");
return LOS_NOK;
}
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 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**:
```
const struct lfs_config cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
.erase = user_provided_block_device_erase,
.sync = user_provided_block_device_sync,
// block device configuration
.read_size = 16,
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};
```
**.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.
**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_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**).
The OpenHarmony LiteOS-M kernel supports File Allocation Table file system \(FATFS\) and LittleFS file systems. Like the OpenHarmony LiteOS-A kernel, the OpenHarmony LiteOS-M kernel provides POSIX over the virtual file system \(VFS\) to ensure interface consistency. However, the VFS of the LiteOS-M kernel is light due to insufficient resources and does not provide advanced functions \(such as pagecache\). Therefore, the VFS of the LiteOS-M kernel implements only API standardization and adaptation. The file systems handle specific transactions. The following table lists the functions supported by the file systems.
<tdclass="cellrowborder"valign="top"width="44.605539446055396%"headers="mcps1.2.6.1.3 "><pid="p1807145320308"><aname="p1807145320308"></a><aname="p1807145320308"></a>Opens a file.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p61801940174715"><aname="p61801940174715"></a><aname="p61801940174715"></a>Closes a file.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p176091241485"><aname="p176091241485"></a><aname="p176091241485"></a>Writes data to a file.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p138512964919"><aname="p138512964919"></a><aname="p138512964919"></a>Sets the file offset.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p625522973712"><aname="p625522973712"></a><aname="p625522973712"></a>Deletes a file.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p14485193113376"><aname="p14485193113376"></a><aname="p14485193113376"></a>Renames a file. </p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p47118331374"><aname="p47118331374"></a><aname="p47118331374"></a>Obtains file information based on the file handle.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p0760185744315"><aname="p0760185744315"></a><aname="p0760185744315"></a>Obtains file information based on the file path name.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p11857231165014"><aname="p11857231165014"></a><aname="p11857231165014"></a>Saves file updates to a storage device.</p>
<tdclass="cellrowborder"valign="top"width="44.605539446055396%"headers="mcps1.2.6.1.3 "><pid="p1977064334716"><aname="p1977064334716"></a><aname="p1977064334716"></a>Creates a directory.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p179816348457"><aname="p179816348457"></a><aname="p179816348457"></a>Opens a directory.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p198171131194514"><aname="p198171131194514"></a><aname="p198171131194514"></a>Reads the content of a directory.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p128021415175112"><aname="p128021415175112"></a><aname="p128021415175112"></a>Closes a directory.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p5786174617471"><aname="p5786174617471"></a><aname="p5786174617471"></a>Deletes a directory.</p>
<tdclass="cellrowborder"valign="top"width="44.605539446055396%"headers="mcps1.2.6.1.3 "><pid="p1312794814473"><aname="p1312794814473"></a><aname="p1312794814473"></a>Mounts a partition.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p1275610616466"><aname="p1275610616466"></a><aname="p1275610616466"></a>Unmounts a partition.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.6.1.2 "><pid="p171607155464"><aname="p171607155464"></a><aname="p171607155464"></a>Forcibly unmounts a partition using the <strongid="b172144710378"><aname="b172144710378"></a><aname="b172144710378"></a>MNT_FORCE</strong> parameter.</p>
The OpenHarmony LiteOS-M kernel supports File Allocation Table file system (FATFS) and LittleFS file systems. Like the OpenHarmony LiteOS-A kernel, the OpenHarmony LiteOS-M kernel provides POSIX over the virtual file system (VFS) to ensure interface consistency. However, the VFS of the LiteOS-M kernel is light due to insufficient resources and does not provide advanced functions (such as pagecache). Therefore, the VFS of the LiteOS-M kernel implements only API standardization and adaptation. The file systems handle specific transactions.
The following tables list the APIs supported by the file systems of the LiteOS-M kernel.
**Table 1** File management operations
| API| Description| FATFS | LITTLEFS |
| -------- | -------- | -------- | -------- |
| open | Opens a file.| Supported| Supported|
| close | Closes a file.| Supported| Supported|
| read | Reads the file content.| Supported| Supported|
| write | Writes data to a file.| Supported| Supported|
| lseek | Sets the file offset.| Supported| Supported|
| unlink | Deletes a file.| Supported| Supported|
| rename | Renames the file.| Supported| Supported|
| fstat | Obtains file information based on the file handle.| Supported| Supported|
| stat | Obtains file information based on the file path name.| Supported| Supported|
| fsync | Saves file updates to a storage device.| Supported| Supported|
**Table 2** Directory management operations
| API| Description| FATFS | LITTLEFS |
| -------- | -------- | -------- | -------- |
| mkdir | Creates a directory.| Supported| Supported|
| opendir | Opens a directory.| Supported| Supported|
| readdir | Reads the content of a directory.| Supported| Supported|
| closedir | Closes a directory.| Supported| Supported|
| rmdir | Deletes a directory.| Supported| Supported|
**Table 3** Partition operations
| API| Description| FATFS | LITTLEFS |
| -------- | -------- | -------- | -------- |
| mount | Mounts a partition.| Supported| Supported|
| umount | Unmounts a partition.| Supported| Supported|
| umount2 | Forcibly unmounts a partition using the **MNT_FORCE** parameter.| Supported| Not supported|
| statfs | Obtains partition information.| Supported| Not supported|
## FAT
### Basic Concepts
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
#### Driver Adaptation
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:
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:
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 file to a device.
5. Set the offset to the start position of the file.
6. Reads the file content.
7. Close the file.
8. Delete the file.
9. Delete the directory.
#### Sample Code
**Prerequisites**
The MMC device partition is mounted to the **user** directory.
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "sys/stat.h"
#include "fcntl.h"
#include "unistd.h"
#define LOS_OK 0
#define LOS_NOK -1
int FatfsTest(void)
{
int ret;
int fd = -1;
ssize_t len;
off_t off;
char dirName[20] = "user/test";
char fileName[20] = "user/test/file.txt";
char writeBuf[20] = "Hello OpenHarmony!";
char readBuf[20] = {0};
/* Create the user/test directory. */
ret = mkdir(dirName, 0777);
if (ret != LOS_OK) {
printf("mkdir failed.\n");
return LOS_NOK;
}
/* 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");
return LOS_NOK;
}
/* Write the content from writeBuf to the file. */
len = write(fd, writeBuf, strlen(writeBuf));
if (len != strlen(writeBuf)) {
printf("write file failed.\n");
return LOS_NOK;
}
/* 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 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 length of readBuf to readBuf. */
len = read(fd, readBuf, sizeof(readBuf));
if (len != strlen(writeBuf)) {
printf("read file failed.\n");
return LOS_NOK;
}
printf("%s\n", readBuf);
/* Close the file. */
ret = close(fd);
if (ret != LOS_OK) {
printf("close failed.\n");
return LOS_NOK;
}
/* Delete 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. */
ret = rmdir(dirName);
if (ret != LOS_OK) {
printf("rmdir failed.\n");
return LOS_NOK;
}
return LOS_OK;
}
```
#### Verification
The development is successful if the return result is as follows:
```
Hello OpenHarmony!
```
## LittleFS
### Basic Concepts
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
Before porting LittleFS to a new hardware device, you need to declare **lfs_config**:
```
const struct lfs_config cfg = {
// block device operations
.read = user_provided_block_device_read,
.prog = user_provided_block_device_prog,
.erase = user_provided_block_device_erase,
.sync = user_provided_block_device_sync,
// block device configuration
.read_size = 16,
.prog_size = 16,
.block_size = 4096,
.block_count = 128,
.cache_size = 16,
.lookahead_size = 16,
.block_cycles = 500,
};
```
**.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.
**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_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**).
As an optional function of the kernel, memory corruption check is used to check the integrity of a dynamic memory pool. This mechanism can detect memory corruption errors in the memory pool in a timely manner and provide alerts. It helps reduce problem locating costs and increase troubleshooting efficiency.
## Function Configuration
**LOSCFG\_BASE\_MEM\_NODE\_INTEGRITY\_CHECK**: specifies the setting of the memory corruption check. This function is disabled by default. To enable the function, set this macro to **1** in **target\_config.h**.
1. If this macro is enabled, the memory pool integrity will be checked in real time upon each memory allocation.
2. If this macro is not enabled, you can call **LOS\_MemIntegrityCheck** to check the memory pool integrity when required. Using **LOS\_MemIntegrityCheck** does not affect the system performance. In addition, the check accuracy decreases because the node header does not contain the magic number \(which is available only when **LOSCFG\_BASE\_MEM\_NODE\_INTEGRITY\_CHECK** is enabled\).
This check only detects the corrupted memory node and provides information about the previous node \(because memory is contiguous, a node is most likely corrupted by the previous node\). To further determine the location where the previous node is requested, you need to enable the memory leak check and use LRs to locate the fault.
>If memory corruption check is enabled, a magic number is added to the node header, which increases the size of the node header. The real-time integrity check has a great impact on the performance. In performance-sensitive scenarios, you are advised to disable this function and use **LOS\_MemIntegrityCheck** to check the memory pool integrity.
## Development Guidelines
### How to Develop
Check for memory corruption by calling **LOS\_MemIntegrityCheck**. If no memory corruption occurs, **0** is returned and no log is output. If memory corruption occurs, related log is output. For details, see the output of the following example.
### Development Example
This example implements the following:
1. Request two physically adjacent memory blocks.
2. Call **memset** to construct an out-of-bounds access and overwrites the first four bytes of the next node.
3. Call **LOS\_MemIntegrityCheck** to check whether memory corruption occurs.
### Sample Code
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "los_memory.h"
#include "los_config.h"
void MemIntegrityTest(void)
{
/* Request two physically adjacent memory blocks.*/
/* Construct an out-of-bounds access to cause memory corruption. The memory block of the first node is 8 bytes. Clearing 12 bytes overwrites the header of the second memory node. */
memory used but magic num wrong, magic num = 0x00000000 /* Error information, indicating that the first four bytes, that is, the magic number, of the next node are corrupted.*/
/* Key information about the corrupted node and its previous node, including the address of the previous node, magic number of the node, and sizeAndFlag of the node. In this example, the magic number of the corrupted node is cleared. */
broken node head LR info: /* The node LR information can be output only after the memory leak check is enabled.*/
LR[0]:0x0800414e
LR[1]:0x08000cc2
LR[2]:0x00000000
pre node head LR info: /* Based on the LR information, you can find where the previous node is requested in the assembly file and then perform further analysis.*/
LR[0]:0x08004144
LR[1]:0x08000cc2
LR[2]:0x00000000
[ERR]Memory interity check error, cur node: 0x20003b10, pre node: 0x20003af0 /* Addresses of the corrupted node and its previous node*/
As an optional function of the kernel, memory leak check is used to locate dynamic memory leak problems. After this function is enabled, the dynamic memory automatically records the link registers \(LRs\) used when memory is allocated. If a memory leak occurs, the recorded information helps locate the memory allocated for further analysis.
## Function Configuration
1.**LOSCFG\_MEM\_LEAKCHECK**: specifies the setting of the memory leak check. This function is disabled by default. To enable the function, set this macro to **1** in **target\_config.h**.
2.**LOSCFG\_MEM\_RECORD\_LR\_CNT**: number of LRs recorded. The default value is **3**. Each LR consumes the memory of **sizeof\(void \*\)** bytes.
3.**LOSCFG\_MEM\_OMIT\_LR\_CNT**: number of ignored LRs. The default value is **4**, which indicates that LRs are recorded from the time when **LOS\_MemAlloc** is called. You can change the value based on actual requirements. This macro is configured because:
-**LOS\_MemAlloc** is also called internally.
-**LOS\_MemAlloc** may be encapsulated externally.
- The number of LRs configured by **LOSCFG\_MEM\_RECORD\_LR\_CNT** is limited.
Correctly setting this macro can ignore invalid LRs and reduce memory consumption.
## Development Guidelines
### How to Develop
Memory leak check provides a method to check for memory leak in key code logic. If this function is enabled, LR information is recorded each time when memory is allocated. When **LOS\_MemUsedNodeShow** is called before and after the code snippet is checked, information about all nodes that have been used in the specified memory pool is printed. You can compare the node information. The newly added node information indicates the node where the memory leak may occur. You can locate the code based on the LR and further check whether a memory leak occurs.
The node information output by calling **LOS\_MemUsedNodeShow** is in the following format: Each line contains information about a node. The first column indicates the node address, based on which you can obtain complete node information using a tool such as a GNU Debugger \(GDB\). The second column indicates the node size, which is equal to the node header size plus the data field size. Columns 3 to 5 list the LR addresses. You can determine the specific memory location of the node based on the LR addresses and the assembly file.
Memory information includes the memory pool size, memory usage, remaining memory size, maximum free memory, memory waterline, number of memory nodes, and fragmentation rate.
- Memory waterline: indicates the maximum memory used in a memory pool. The waterline value is updated upon each memory allocation and release. The memory pool size can be optimized based on this value.
- Fragmentation rate: indicates the fragmentation degree of the memory pool. If the fragmentation rate is high, there are a large number of free memory blocks in the memory pool but each block is small. You can use the following formula to calculate the fragmentation rate:
Fragmentation rate = 100 – 100 x Maximum free memory block size/Remaining memory size
- Other parameters: You can call APIs \(described in [Memory Management](kernel-mini-basic-memory-basic.md)\) to scan node information in the memory pool and collect statistics.
## Function Configuration
**LOSCFG\_MEM\_WATERLINE**: specifies the setting of the memory information statistics function. This function is enabled by default. To disable the function, set this macro to **0** in **target\_config.h**. If you want to obtain the memory waterline, you must enable this macro.
## Development Guidelines
### How to Develop
Key structure:
```
typedef struct {
UINT32 totalUsedSize; // Memory usage of the memory pool
UINT32 totalFreeSize; // Remaining memory in the memory pool
UINT32 maxFreeNodeSize; // Maximum size of the free memory block in the memory pool
UINT32 usedNodeNum; // Number of non-free memory blocks in the memory pool
UINT32 freeNodeNum; // Number of free memory blocks in the memory pool
#if (LOSCFG_MEM_WATERLINE == 1) // This macro is enabled by default. To disable it, set it to 0 in target_config.h.
UINT32 usageWaterLine; // Waterline of the memory pool
#endif
} LOS_MEM_POOL_STATUS;
```
- To obtain the memory waterline, call **LOS\_MemInfoGet**. The first parameter in the API is the start address of the memory pool, and the second parameter is the handle of the **LOS\_MEM\_POOL\_STATUS** type. The **usageWaterLine** field indicates the waterline.
- To calculate the memory fragmentation rate, call **LOS\_MemInfoGet** to obtain the remaining memory size and the maximum free memory block size in the memory pool, and then calculate the fragmentation rate of the dynamic memory pool as follows:
Fragmentation rate = 100 – 100 x Maximum free memory block size/Remaining memory size
### Development Example
This example implements the following:
1. Create a monitoring task to obtain information about the memory pool.
2. Call **LOS\_MemInfoGet** to obtain the basic information about the memory pool.
3. Calculate the memory usage and fragmentation rate.
### Sample Code
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "los_task.h"
#include "los_memory.h"
#include "los_config.h"
void MemInfoTaskFunc(void)
{
LOS_MEM_POOL_STATUS poolStatus = {0};
/* pool is the memory address of the information to be collected. OS_SYS_MEM_ADDR is used as an example.*/
void *pool = OS_SYS_MEM_ADDR;
LOS_MemInfoGet(pool, &poolStatus);
/* Calculate the fragmentation rate of the memory pool. */
The purpose of memory debugging is to locate problems related to dynamic memory. The kernel provides a variety of memory debugging methods. Dynamic memory pool statistics helps you learn the memory pool waterline and fragmentation rate. Memory leak check helps you accurately locate the code where memory leak occurs and analyze the memory usage of each module. Memory corruption check helps you locate memory corruptions.
-**[Memory Information Statistics](kernel-mini-memory-debug-mes.md)**
Memory information includes the memory pool size, memory usage, remaining memory size, maximum free memory, memory waterline, number of memory nodes, and fragmentation rate.
- Memory waterline: indicates the maximum memory used in a memory pool. The waterline value is updated upon each memory allocation and release. The memory pool size can be optimized based on this value.
- Fragmentation rate: indicates the fragmentation degree of the memory pool. If the fragmentation rate is high, there are a large number of free memory blocks in the memory pool but each block is small. You can use the following formula to calculate the fragmentation rate:<br>Fragmentation rate = 100 – 100 x Maximum free memory block size/Remaining memory size
- Other parameters: You can call APIs described in [Memory Management](../kernel/kernel-mini-basic-memory.md) to scan node information in the memory pool and collect statistics.
### Function Configuration
**LOSCFG_MEM_WATERLINE**: specifies the setting of the memory information statistics function. This function is enabled by default. To disable the function, set this macro to **0** in **target_config.h**. If you want to obtain the memory waterline, you must enable this macro.
### Development Guidelines
#### How to Develop
Key structure:
```
typedef struct {
UINT32 totalUsedSize; // Memory usage of the memory pool.
UINT32 totalFreeSize; // Remaining size of the memory pool.
UINT32 maxFreeNodeSize; // Maximum size of the free memory block in the memory pool.
UINT32 usedNodeNum; // Number of non-free memory blocks in the memory pool.
UINT32 freeNodeNum; // Number of free memory blocks in the memory pool.
#if (LOSCFG_MEM_WATERLINE == 1) //The function is enabled by default. To disable it, set this macro to 0 in target_config.h.
UINT32 usageWaterLine; // Waterline of the memory pool.
#endif
} LOS_MEM_POOL_STATUS;
```
- To obtain the memory waterline, call **LOS_MemInfoGet**. The first parameter in the API is the start address of the memory pool, and the second parameter is the handle of the **LOS_MEM_POOL_STATUS** type. The **usageWaterLine** field indicates the waterline.
- To calculate the memory fragmentation rate, call **LOS_MemInfoGet** to obtain the remaining memory size and the maximum free memory block size in the memory pool, and then calculate the fragmentation rate of the dynamic memory pool as follows:<br>Fragmentation rate = 100 – 100 x Maximum free memory block size/Remaining memory size
#### Development Example
This example implements the following:
1. Create a monitoring task to obtain information about the memory pool.
2. Calls **LOS_MemInfoGet** to obtain the basic information about the memory pool.
3. Calculate the memory usage and fragmentation rate.
#### Sample Code
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "los_task.h"
#include "los_memory.h"
#include "los_config.h"
void MemInfoTaskFunc(void)
{
LOS_MEM_POOL_STATUS poolStatus = {0};
/* pool is the memory address of the information to be collected. OS_SYS_MEM_ADDR is used as an example. */
void *pool = OS_SYS_MEM_ADDR;
LOS_MemInfoGet(pool, &poolStatus);
/* Calculate the fragmentation rate of the memory pool. */
As an optional function of the kernel, memory leak check is used to locate dynamic memory leak problems. After this function is enabled, the dynamic memory automatically records the link registers (LRs) used when memory is allocated. If a memory leak occurs, the recorded information helps locate the memory allocated for further analysis.
### Function Configuration
1.**LOSCFG_MEM_LEAKCHECK**: specifies the setting of the memory leak check. This function is disabled by default. To enable the function, set this macro to **1** in **target_config.h**.
2.**LOSCFG_MEM_RECORD_LR_CNT**: specifies the number of LRs recorded. The default value is **3**. Each LR consumes the memory of **sizeof(void\*)** bytes.
3.**LOSCFG_MEM_OMIT_LR_CNT**: specifies the number of ignored LRs. The default value is **4**, which indicates that LRs are recorded from the time when **LOS_MemAlloc** is called. You can change the value based on actual requirements. This macro is configured because:
-**LOS_MemAlloc** is also called internally.
-**LOS_MemAlloc** may be encapsulated externally.
- The number of LRs configured by **LOSCFG_MEM_RECORD_LR_CNT** is limited.
Correctly setting this macro can ignore invalid LRs and reduce memory consumption.
### Development Guidelines
#### How to Develop
Memory leak check provides a method to check for memory leak in key code logic. If this function is enabled, LR information is recorded each time when memory is allocated. When **LOS_MemUsedNodeShow** is called before and after the code snippet is checked, information about all nodes that have been used in the specified memory pool is printed. You can compare the node information. The newly added node information indicates the node where the memory leak may occur. You can locate the code based on the LR and further check whether a memory leak occurs.
The node information output by calling **LOS_MemUsedNodeShow** is in the following format: <br>Each line contains information about a node. The first column indicates the node address, based on which you can obtain complete node information using a tool such as a GNU Debugger (GDB). The second column indicates the node size, which is equal to the node header size plus the data field size. Columns 3 to 5 list the LR addresses. You can determine the specific memory location of the node based on the LR addresses and the assembly file.
The memory node addressed by **0x080041ee** is not released after being requested in **MemLeakTest**.
## Memory Corruption Check
### Basic Concepts
As an optional function of the kernel, memory corruption check is used to check the integrity of a dynamic memory pool. This mechanism can detect memory corruption errors in the memory pool in a timely manner and provide alerts. It helps reduce problem locating costs and increase troubleshooting efficiency.
### Function Configuration
**LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK**: specifies the setting of the memory corruption check. This function is disabled by default. To enable the function, set this macro to **1** in **target_config.h**.
1. If this macro is enabled, the memory pool integrity will be checked in real time upon each memory allocation.
2. If this macro is not enabled, you can call **LOS_MemIntegrityCheck** to check the memory pool integrity when required. Using **LOS_MemIntegrityCheck** does not affect the system performance. In addition, the check accuracy decreases because the node header does not contain the magic number (which is available only when **LOSCFG_BASE_MEM_NODE_INTEGRITY_CHECK** is enabled).
This check only detects the corrupted memory node and provides information about the previous node (because memory is contiguous, a node is most likely corrupted by the previous node). To further determine the location where the previous node is requested, you need to enable the memory leak check and use LRs to locate the fault.
> If memory corruption check is enabled, a magic number is added to the node header, which increases the size of the node header. The real-time integrity check has a great impact on the performance. In performance-sensitive scenarios, you are advised to disable this function and use **LOS_MemIntegrityCheck** to check the memory pool integrity.
### Development Guidelines
#### How to Develop
Check for memory corruption by calling **LOS_MemIntegrityCheck**. If no memory corruption occurs, **0** is returned and no log is output. If memory corruption occurs, the related log is output. For details, see the output of the following example.
#### Development Example
This example implements the following:
1. Request two physically adjacent memory blocks.
2. Use **memset** to construct an out-of-bounds access and overwrites the first four bytes of the next node.
3. Call **LOS_MemIntegrityCheck** to check whether memory corruption occurs.
#### Sample Code
The sample code is as follows:
```
#include <stdio.h>
#include <string.h>
#include "los_memory.h"
#include "los_config.h"
void MemIntegrityTest(void)
{
/* Request two physically adjacent memory blocks. */
/* Construct an out-of-bounds access to cause memory corruption. The memory block of the first node is 8 bytes. Clearing 12 bytes overwrites the header of the second memory node. */
memory used but magic num wrong, magic num = 0x00000000 /* Error information, indicating that the first four bytes, that is, the magic number, of the next node are corrupted. */
/* Key information about the corrupted node and its previous node, including the address of the previous node, magic number of the node, and sizeAndFlag of the node. In this example, the magic number of the corrupted node is cleared. */
broken node head LR info: /* The node LR information can be output only after the memory leak check is enabled. */
LR[0]:0x0800414e
LR[1]:0x08000cc2
LR[2]:0x00000000
pre node head LR info: /* Based on the LR information, you can find where the previous node is requested in the assembly file and then perform further analysis. */
LR[0]:0x08004144
LR[1]:0x08000cc2
LR[2]:0x00000000
[ERR]Memory integrity check error, cur node: 0x20003b10, pre node: 0x20003af0 /* Addresses of the corrupted node and its previous node */
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
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.
## Development Guidelines
**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 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.
Journalling Flash File System Version 2 \(JFFS2\) is a log-structured file system designed for Memory Technology Devices \(MTDs\).
JFFS2 is used on the NOR flash memory of the OpenHarmony. JFFS2 is readable and writable, supports data compression, provides crash/power failure protection, and supports wear leveling. There are many differences between flash memory and disk media. Running a disk file system on a flash memory device will cause performance and security problems. JFFS2 is a file system optimized for flash memory.
## Working Principles<a name="section23911025195913"></a>
This document does not include the physical layout of JFFS2 on storage devices and JFFS2 specifications. For details, see the [official JFFS2 specification document](https://sourceware.org/jffs2/).
The following describes several important mechanisms and features of JFFS2 that you may concern.
1. Mount mechanism and speed: According to the JFFS2 design, all files are divided into nodes of different sizes based on certain rules and stored on the flash memory device in sequence. In the mount process, all node information needs to be obtained and cached in the memory. Therefore, the mount speed is in linear proportion to the flash device capacity and the number of files. This is a native design issue of JFFS2. To increase the mount speed, you can select **Enable JFFS2 SUMMARY** during kernel compilation. If this option is selected, information required by the mount operation will be stored to the flash memory in advance. When the mount operation is performed, this information can be read and parsed quickly, ensuring relatively constant mount speed. However, this space-for-time practice consumes about 8% extra space.
2. Wear leveling: Due to the physical attributes of flash memory devices, read and write operations can be performed only on blocks of a specific size. To prevent certain blocks from being severely worn, wear leveling is used on written blocks in JFFS2 to ensure relatively balanced writes on all blocks. This prolongs the overall service life of the flash memory devices.
3. Garbage collection \(GC\) mechanism: When a deletion operation is performed in JFFS2, the physical memory is not released immediately. An independent GC thread performs GC operations such as space defragmentation and migration. However, GC in JFFS2 affects instantaneous read/write performance, like all GC mechanisms. In addition, JFFS2 reserves about three blocks in each partition for space defragmentation. The reserved space is invisible to users.
4. Compression mechanism: The underlying layer automatically decompresses or compresses the data read or written each time in JFFS2. The actual I/O size is different from the read or write size requested by the user. You cannot estimate whether the write operation will succeed or not based on the size of the written data and the remaining space of the flash memory.
5. Hard link mechanism: JFFS2 supports hard links. Multiple hard links of the same file occupy physical memory space of only one hard link. The physical space is released only when all hard links are deleted.
## Development Guidelines<a name="section179711119014"></a>
The development based on JFFS2 and NOR flash memory is similar to the development based on other file systems because the VFS shields the differences of specific file systems and the standard POSIX APIs are used as external APIs.
The raw NOR flash device has no place to centrally manage and record partition information. Therefore, you need to transfer the partition information by using other configuration methods \(using the **bootargs** parameter during image burning\), call the corresponding API in the code to add partitions, and then mount the partitions.
**Creating a JFFS2 Image**
Use the **mkfs.jffs2** tool to create an image. The default page size is 4 KiB, and the default **eraseblock** size is 64 KiB. Modify the parameter values to match your development.
```
./mkfs.jffs2 -d rootfs/ -o rootfs.jffs2
```
**Table 1** Command description \(run **mkfs.jffs2 --help** to view more details\)
<tdclass="cellrowborder"valign="top"width="50%"headers="mcps1.2.3.1.2 "><pid="p1338510362717"><aname="p1338510362717"></a><aname="p1338510362717"></a>Specifies the page size. If this parameter is not specified, the default value <strongid="b3814183173513"><aname="b3814183173513"></a><aname="b3814183173513"></a>4KiB</strong> is used.</p>
<tdclass="cellrowborder"valign="top"width="50%"headers="mcps1.2.3.1.2 "><pid="p6386123612719"><aname="p6386123612719"></a><aname="p6386123612719"></a>Specifies the <strongid="b56716452412"><aname="b56716452412"></a><aname="b56716452412"></a>eraseblock</strong> size. If this parameter is not specified, the default value <strongid="b17480133411354"><aname="b17480133411354"></a><aname="b17480133411354"></a>64KiB</strong> is used.</p>
<tdclass="cellrowborder"valign="top"width="50%"headers="mcps1.2.3.1.2 "><pid="p1491185544517"><aname="p1491185544517"></a><aname="p1491185544517"></a>Specifies the image size. 0xFF is filled at the end of the image file to make the file to the specified size. If the size is not specified, 0xFF is filled to a value aligned with <strongid="b6777155933718"><aname="b6777155933718"></a><aname="b6777155933718"></a>eraseblock</strong>.</p>
<tdclass="cellrowborder"valign="top"width="50%"headers="mcps1.2.3.1.2 "><pid="p238618361573"><aname="p238618361573"></a><aname="p238618361573"></a>Specifies the source directory of the file system image.</p>
<tdclass="cellrowborder"valign="top"width="50%"headers="mcps1.2.3.1.2 "><pid="p1938603617710"><aname="p1938603617710"></a><aname="p1938603617710"></a>Specifies the image name.</p>
</td>
</tr>
</tbody>
</table>
**Mounting a JFFS2 Partition**
Call **int mount\(const char \*source, const char \*target, const char \*filesystemtype, unsigned long mountflags, const void \*data\)** to mount the device node and mount point.
This function has the following parameters:
-**const char \*source** specifies the device node.
-**const char \*target** specifies the mount point.
-**const char \*filesystemtype** specifies the file system type.
-**unsigned long mountflags** specifies the mount flag, which is **0** by default.
-**const void \*data** specifies the data, which is **NULL** by default.
You can also run the **mount** command in **shell** to mount a JFFS2 partition. In this case, you do not need to specify the last two parameters.
Run the following command:
```
OHOS # mount /dev/spinorblk1 /jffs1 jffs2
```
If the following information is displayed, the JFFS2 partition is mounted:
```
OHOS # mount /dev/spinorblk1 /jffs1 jffs2
mount OK
```
Now, you can perform read and write operations on the NOR flash memory.
**Unmounting a JFFS2 Partition**
Call **int umount\(const char \*target\)** to unmount a partition. You only need to specify the correct mount point.
Run the following command:
```
OHOS # umount /jffs1
```
If the following information is displayed, the JFFS2 partition is unmounted:
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
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
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
```
- 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.
```
sudo /etc/init.d/nfs-kernel-server start
```
Restart the NFS server.
```
sudo /etc/init.d/nfs-kernel-server restart
```
2. Configure the board as an NFS client.
In this section, the NFS client is a device running the OpenHarmony kernel.
- Set the hardware connection.
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.
You can run the **ifconfig** command to check the OpenHarmony kernel device's IP address.
- Start the network and ensure that the network between the board and NFS server is normal.
Start the Ethernet or another type of network, and then run **ping** to check whether the network connection to the server is normal.
```
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.
```
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.
```
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.
## Working Principles
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.
## Development Guidelines
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
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.
```
#include "proc_fs.h"
static int TestRead(struct SeqBuf *buf, void *arg)
{
LosBufPrintf(buf, "Hello World! \n"); /* Print "Hello World!" to the buffer. The data in the buffer will be returned to the read result. */
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.
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.
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.
### Development Guidelines
**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 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.
## JFFS2
### Basic Concepts
Journalling Flash File System Version 2 (JFFS2) is a log-structured file system designed for Memory Technology Devices (MTDs).
JFFS2 is used on the NOR flash memory of the OpenHarmony. JFFS2 is readable and writable, supports data compression, provides crash or power failure protection, and supports wear leveling. There are many differences between flash memory and disk media. Running a disk file system on a flash memory device will cause performance and security problems. JFFS2 is a file system optimized for flash memory.
### Working Principles
For details about the physical layout of the JFFS2 file system on the storage device and the specifications of the file system, visit https://sourceware.org/jffs2/.
The following describes several important mechanisms and features of JFFS2 that you may concern.
1. Mount mechanism and speed: According to the JFFS2 design, all files are divided into nodes of different sizes based on certain rules and stored on the flash memory device in sequence. In the mount process, all node information needs to be obtained and cached in the memory. Therefore, the mount speed is in linear proportion to the flash device capacity and the number of files. This is a native design issue of JFFS2. To increase the mount speed, you can select **Enable JFFS2 SUMMARY** during kernel compilation. If this option is selected, information required by the mount operation will be stored to the flash memory in advance. When the mount operation is performed, this information can be read and parsed quickly, ensuring relatively constant mount speed. However, this space-for-time practice consumes about 8% extra space.
2. Wear leveling: Due to the physical attributes of flash memory devices, read and write operations can be performed only on blocks of a specific size. To prevent certain blocks from being severely worn, wear leveling is used on written blocks in JFFS2 to ensure relatively balanced writes on all blocks. This prolongs the overall service life of the flash memory devices.
3. Garbage collection (GC) mechanism: When a deletion operation is performed in JFFS2, the physical memory is not released immediately. An independent GC thread performs GC operations such as space defragmentation and migration. However, GC in JFFS2 affects instantaneous read/write performance, like all GC mechanisms. In addition, JFFS2 reserves about three blocks in each partition for space defragmentation. The reserved space is invisible to users.
4. Compression mechanism: The underlying layer automatically decompresses or compresses the data read or written each time in JFFS2. The actual I/O size is different from the read or write size requested by the user. You cannot estimate whether the write operation will succeed or not based on the size of the written data and the remaining space of the flash memory.
5. Hard link mechanism: JFFS2 supports hard links. Multiple hard links of the same file occupy physical memory space of only one hard link. The physical space is released only when all hard links are deleted.
### Development Guidelines
The development based on JFFS2 and NOR flash memory is similar to the development based on other file systems because the VFS shields the differences of specific file systems and the standard POSIX APIs are used as external APIs.
The raw NOR flash device has no place to centrally manage and record partition information. Therefore, you need to transfer the partition information by using other configuration methods (using the **bootargs** parameter during image burning), call the corresponding API in the code to add partitions, and then mount the partitions.
**Creating a JFFS2 Image**
Use the **mkfs.jffs2** tool to create an image. The default page size is 4 KiB, and the default **eraseblock** size is 64 KiB. Modify the parameter values to match your development.
```
./mkfs.jffs2 -d rootfs/ -o rootfs.jffs2
```
**Table 1** Command description (run **mkfs.jffs2 --help** to view more details)
| Command| Description|
| -------- | -------- |
| -s | Specifies the page size. If this parameter is not specified, the default value **4KiB** is used.|
| -e | Specifies the **eraseblock** size. If this parameter is not specified, the default value **64KiB** is used.|
| -p | Specifies the image size. 0xFF is filled at the end of the image file to make the file to the specified size. If the size is not specified, 0xFF is filled to a value aligned with **eraseblock**.|
| -d | Specifies the source directory of the file system image.|
| -o | Specifies the image name.|
**Mounting a JFFS2 Partition**
Call **int mount(const char \*source, const char \*target, const char \*filesystemtype, unsigned long mountflags, const void \*data)** to mount the device node and mount point.
This function has the following parameters:
-**const char \*source** specifies the device node.
-**const char \*target** specifies the mount point.
-**const char \*filesystemtype** specifies the file system type.
-**unsigned long mountflags** specifies the mount flag, which is **0** by default.
-**const void \*data** specifies the data, which is **NULL** by default.
You can also run the **mount** command in **shell** to mount a JFFS2 partition. In this case, you do not need to specify the last two parameters.
Run the following command:
```
OHOS # mount /dev/spinorblk1 /jffs1 jffs2
```
If the following information is displayed, the JFFS2 partition is mounted:
```
OHOS # mount /dev/spinorblk1 /jffs1 jffs2
mount OK
```
Now, you can perform read and write operations on the NOR flash memory.
**Unmounting a JFFS2 Partition**
Call **int umount(const char \*target)** to unmount a partition. You only need to specify the correct mount point.
Run the following command:
```
OHOS # umount /jffs1
```
If the following information is displayed, the JFFS2 partition is unmounted:
```
OHOS # umount /jffs1
umount ok
```
## NFS
### Basic Concepts
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
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
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
```
- 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.
```
sudo /etc/init.d/nfs-kernel-server start
```
Restart the NFS server.
```
sudo /etc/init.d/nfs-kernel-server restart
```
2. Configure the board as an NFS client.
In this section, the NFS client is a device running the OpenHarmony kernel.
- Set the hardware connection.
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.
You can run the **ifconfig** command to check the OpenHarmony kernel device's IP address.
- Start the network and ensure that the network between the board and NFS server is normal.
Start the Ethernet or another type of network, and then run **ping** to check whether the network connection to the server is normal.
```
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 ---
packets transmitted, 4 received, 0 loss
Initialize the NFS client.
```
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.
```
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. <br>**SERVER_PATH** indicates the path of the shared directory on the NFS server. <br>**CLIENT_PATH** indicates the NFS path on the local device. <br>**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.
3. Share files using NFS.
Create the **dir** directory on the NFS server and save the directory. 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.
## Ramfs
### 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.
> - A ramfs file system can be mounted only once. Once mounted to a directory, it cannot be mounted to other directories.
>
> - Ramfs is under debugging and disabled by default. Do not use it in formal products.
## procfs
### Basic Concepts
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
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.
### Development Guidelines
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
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.
```
#include "proc_fs.h"
static int TestRead(struct SeqBuf *buf, void *arg)
{
LosBufPrintf(buf, "Hello World! \n"); /* Print "Hello World!" to the buffer. The data in the buffer will be returned to the read result. */
A file system \(often abbreviated to FS\) provides an input and output manner for an OS. It implements the interaction with internal and external storage devices.
The file system provides standard POSIX operation APIs for the upper-layer system through the C library. For details, see the API reference of the C library. The Virtual File System \(VFS\) layer in kernel mode shields the differences between file systems. The basic architecture is as follows:
This chapter describes the functions, syntax, parameter ranges, usage, and examples of key system commands.
For details about the commands that are not described in this document, see the output of the [help](kernel-small-debug-shell-cmd-help.md) command. You can also use the **-h | --help** option of a command to view the help information about the command.
The musl libc library of the debug version provides maintenance and test methods, such as memory leak check, heap memory statistics, memory corruption check, and backtrace, to improve the efficiency of locating memory problems in user space.
Instrumentation is performed on the **malloc** and **free** APIs to log key node information. When memory is requested and released by a program, the memory node integrity is checked. When the program ends, memory statistics are provided for identifying memory leaks.
>After **free** is called, the heap memory will not be released to the heap memory pool immediately. Instead, the heap memory is placed in a queue with a fixed length and filled with the magic number 0xFE. When the queue is full, the memory block first placed in the queue is released to the heap memory pool first.
Write operation: The memory debugging module cannot detect UAF errors from write operations.
- Requested memory block greater than 0x1c000 bytes:
The heap memory greater than 0x1c000 bytes must be requested by calling the **mmap** API via **malloc**. If the heap memory is accessed after being released, the user program will become abnormal \(because the memory region has been unmapped\).
## Double Free<a name="section827194818458"></a>
Double free errors occur when **free\(\)** is called more than once with the same memory address as an argument. When a double free error occurs, the user program exits unexpectedly.
- Requested memory block less than or equal to 0x1c000 bytes:
When a heap memory node is corrupted, the user program exits unexpectedly, and the call stack that requests the heap memory of the node corrupted is output. The memory debugging module, however, cannot debug the memory corrupted by a wild pointer. For example, if the user program mem\_check has heap memory overwriting, you can use the command line to obtain the possible location of the memory corruption.
```
OHOS # ./mem_check --mwatch
OHOS #
==PID:6== Memory integrity information:
[TID:28 allocated addr: 0x272e1ea0, size: 0x120] The possible attacker was allocated from:
#00: <malloc+0x808>[0x640e8] -> /lib/libc.so
#01: <threadFunc1+0x7c>[0x21d0] -> mem_check
```
You can use the call stack parsing script to parse the call stack information.
- Requested memory block greater than 0x1c000 bytes:
When a large memory block \(greater than 0x1c000 bytes\) is requested by calling the **mmap** API via **malloc**, one more page of **PAGE\_SIZE** is allocated at the start and end of the memory region. The two pages are neither readable nor writeable. Any read or write operation to the pages may cause an exception of the user program.
The memory debugging module maintains 128 \(that is the maximum number of threads supported in the system\) linked lists for each process. The index of each linked list is the thread ID.
When memory is requested, key information is saved to the memory node control block, which is inserted to the corresponding linked list based on the thread ID.
When memory is released, the system matches the memory node control block based on the memory address to be released and deletes the control block.
When memory is allocated, the returned address is saved in a link register \(LR\). During the process running, the system adds information, such as the LR corresponding to the suspected leak, to the memory node control block. [Figure 2](#fig716011269106) shows the heap memory node information.
**TID** indicates the thread ID; **PID** indicates the process ID; **ptr** indicates the address of the memory requested; **size** indicates the size of the requested memory; **lr\[_n_\]** indicates the address of the call stack, and _n_ is configurable.
When memory is released, the input parameter pointer in the **free** API is used to match the **ptr** field of the memory node. If the pointer is the same as the **ptr** field of the memory node, the memory node control block will be deleted.
You can export the memory debugging information of each process through the serial port or file, and use the addr2line tool to convert the exported information into the code lines that cause memory leaks. In this way, the memory leakage problem can be solved.
**Figure 3** Process of locating the code lines for a memory leak<aname="fig1562884220111"></a>
You can collect statistics on the percentage of heap memory requested by each thread to provide data support for optimizing memory usage of user programs. The **malloc** and **free** APIs are involved in user-mode heap memory statistics. As shown in [Figure 1](#fig4294145810543), each process maintains 128 linked lists, and the index of each linked list is a thread ID. When heap memory is requested, the **ptr** and **size** information is recorded in the memory node control block, which is inserted to a linked list with the thread ID as the header. When the heap memory is released, the corresponding heap memory block is removed from the linked list based on the **ptr**. In addition, the system calculates the total heap memory used by the current thread and updates its heap memory usage and peak heap memory usage.
- If the memory requested by using **malloc** is less than or equal to 0x1c000 bytes, the heap allocation algorithm is used to allocate memory.
When a user program requests heap memory, information such as the check value is added to the heap memory node. If the check value is abnormal, it is probably that the previous heap memory block is overwritten. Currently, the scenario where the check value is damaged by a wild pointer cannot be identified. When memory is allocated or released, the memory node check value is verified. If the memory node is corrupted and the verification fails, the following information is output: TID, PID, and call stack information saved when the previous heap memory block of the corrupted node is allocated. You can use the addr2line tool to obtain the specific code line and rectify the fault.
**Figure 4** Adding a check value to the node header information<a name="fig2912164881817"></a>
When heap memory is released by using **free**, the memory block is not released immediately. Instead, the magic number 0xFE is written into the memory block, which is then placed in the free queue to prevent the memory block from being allocated by **malloc** within a certain period of time. When a wild pointer or **use-after-free** operation is performed to read the memory, an exception can be detected. However, this mechanism does not apply to write operations.
**Figure 5** Process of releasing memory<a name="fig3593750101916"></a>
- If the memory requested by using **malloc** is greater than 0x1c000 bytes, **mmap** is used to allocate memory.
When **mmap** is used to request a large memory block, one more page is allocated at the start and end of the memory region. The current **PAGE\_SIZE** of each page is **0x1000**. The permissions of the two pages are set to **PROT\_NONE** \(no read or write permission\) by using the **mprotect** API to prevent out-of-bounds read and write of memory. If out-of-bounds read and write of memory occurs, the user program becomes abnormal because the user does not have the read or write permission. The code logic can be identified based on the abnormal call stack information.
**Figure 6** Layout of the memory allocated by using the **mmap** mechanism of **malloc**<a name="fig4150122342016"></a>
<tdclass="cellrowborder"valign="top"width="57.34573457345735%"headers="mcps1.2.4.1.3 "><pid="p1972971913115"><aname="p1972971913115"></a><aname="p1972971913115"></a>Obtains the address information of the call stack.</p>
<tdclass="cellrowborder"valign="top"headers="mcps1.2.4.1.2 "><pid="p174842362505"><aname="p174842362505"></a><aname="p174842362505"></a>Obtains symbol information based on address information.</p>
The sample code explicitly calls the related APIs of the memory debugging module to check the memory.
```
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <debug.h> // Header file that includes the declaration of the memory debugging APIs
#define MALLOC_LEAK_SIZE 0x300
void func(void) {
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE);
}
int main()
{
mem_check_init(NULL); // Output the memory debugging information through the serial port. This function must be called before the user program requests the heap memory for the first time (generally called at the entry of the main function). Otherwise, the debugging information is inaccurate.
// mem_check_init("/storage/mem_debug.txt"); // Output the memory debugging information to the /storage/mem_debug.txt file. If the file fails to be created, output the information through the serial port.
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '1', MALLOC_LEAK_SIZE);
watch_mem(); // Obtain the thread-level memory statistics in the current code.
func();
check_heap_integrity(); // Check the integrity of the heap memory nodes.
check_leak(); // Check whether a heap memory leak occurs in the current code. (Generally, the check result is accurate before the application exits. If the check is performed after the calling of malloc and before the calling of free, the result is inaccurate.)
>- In this example, the compiler path is written into an environment variable in the **.bashrc** file.
>- When compiling user programs and required libraries, add the option **-funwind-tables -rdynamic -g** for stack backtracking.
>- The **-mfloat-abi=softfp**, **-mcpu=cortex-a7**, and **-mfpu=neon-vfpv4** options specify the floating-point calculation optimization, chip architecture, and FPU, which must be the same as the compilation options used by the libc library. Otherwise, the libc library file cannot be found during the link time.
>- **-target arm-liteos** specifies the path of the library files related to the compiler.
>- **--sysroot=/home/<user-name\>/directory/out/hispark\_taurus/ipcamera\_hispark\_taurus/sysroot** specifies the root directory of the compiler library files. In this example, the OpenHarmony project code is stored in **/home/<user-name\>/directory**. The **out/hispark\_taurus/ipcamera\_hispark\_taurus** directory indicates the product specified by the **hb set** command during compilation. In this example, **ipcamera\_hispark\_taurus** is the product specified.
>- **$\(clang -mfloat-abi=softfp -mcpu=cortex-a7 -mfpu=neon-vfpv4 -target arm-liteos -print-file-name=libunwind.a\)** specifies the path of the unwind library.
==PID:4== Detected memory leak(s): // Memory leak information and call stack
[Check point]:
#00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so
#01: <main+0x44>[0x878] -> mem_check
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x1c>[0x850] -> mem_check
#01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x810] -> mem_check
#01: <main+0x3c>[0x870] -> mem_check
#02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so
#01: <exit+0x28>[0x111ec] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x1c>[0x850] -> mem_check
#01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x810] -> mem_check
#01: <main+0x3c>[0x870] -> mem_check
#02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
Check heap integrity ok!
```
## Call Stack Parsing
The **parse\_mem\_info.sh** script in **kernel/liteos\_a/tools/scripts/parse\_memory/** can be used to parse the call stack. You can use the script to convert the debug information into specific source code line number. In the following command, **mem\_debug.txt** stores the memory debugging information, and **elf1** and **elf2** are the executable and linkable format \(ELF\) files to parse.
In addition to calling APIs to check the memory used by user-mode processes, you can run CLI commands to collect memory statistics, check for memory leaks, and check memory integrity.
```
--mwatch: initializes memory debugging, registers signals, and outputs memory debugging information through the serial port.
--mrecord <f_path>: initializes memory debugging, registers signals, and saves the memory debugging information to the f_path file. If the f_path file fails to be created, output the memory debugging information through the serial port.
```
If the process to debug does not exit, you can use the signal mechanism to obtain the corresponding information:
```
kill -35 <pid> # Check the thread-level heap memory usage.
kill -36 <pid> # Check for heap memory leaks.
kill -37 <pid> # Check whether the head node of the heap memory is complete.
Save the debugging information to the **test.txt** file and use the script to parse the information to obtain the number of line where the memory leak occurs.
#00: <main+0x14>[0x724] at /usr1/xxx/TEST_ELF/mem_check.c:14
#01: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x6ec] at /usr1/xxx/TEST_ELF/mem_check.c:8
#01: <main+0x30>[0x740] at /usr1/xxx/TEST_ELF/mem_check.c:19
#02: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
## Running the mrecord Command
1. Run the user program and specify the path of the file that stores the memory debugging information.
```
OHOS # ./mem_check --mrecord /storage/check.txt
```
2. Run the **kill -35 <_pid_\>** command to collect statistics on the memory information. The information is exported to a file. Run the **cat** command to view the information.
```
OHOS # kill -35 4
OHOS # Memory statistics information saved in /storage/pid(4)_check.txt
==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s)
```
3. Run the **kill -36 <_pid_\>** command to check memory integrity. The information is exported to a file. Run the **cat** command to view the information.
```
OHOS # kill -36 4
OHOS # Leak check information saved in /storage/pid(4)_check.txt
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
4. Run the **kill -9 <_pid_\>** command to kill the current process. After the process exits, a memory integrity check is performed by default. The check result is output to a file. You can run the **cat** command to view it.
```
OHOS # kill -9 4
OHOS # Leak check information saved in /storage/pid(4)_check.txt
>The preceding information recorded gradually is added to the file specified during initialization. Therefore, running the **cat** command can also display the historical information in the file.
By default, the OpenHarmony debug version is compiled when a project is built. The libc library of the debug version has integrated the APIs for memory debugging. You can enable memory debugging as required.
You can perform heap memory debugging by using either of the following:
- API: By calling APIs, you can accurately check the heap memory information of a specific code logic segment. However, you have to modify user code.
- CLI: By using the CLI, you do not need to modify user code. However, you cannot accurately check the heap memory information of a specific logic segment.
>After memory debugging is enabled, a heap memory leak check and a heap memory integrity check will be performed by default when a process exits. If memory debugging is disabled, the heap memory statistics, heap memory leak check, and heap memory integrity check cannot be enabled, and there is no response to the calling of any debug API.
The musl libc library of the debug version provides maintenance and test methods, such as memory leak check, heap memory statistics, memory corruption check, and backtrace, to improve the efficiency of locating memory problems in user space.
Instrumentation is performed on the **malloc** and **free** APIs to log key node information. When memory is requested and released by a program, the memory node integrity is checked. When the program ends, memory statistics are provided for identifying memory leaks.
## Working Principles
### Memory Leak Check
The memory debugging module maintains 128 (that is the maximum number of threads supported in the system) linked lists for each process. The index of each linked list is the thread ID.
When memory is requested, key information is saved to the memory node control block, which is inserted to the corresponding linked list based on the thread ID.
When memory is released, the system matches the memory node control block based on the memory address to be released and deletes the control block.
When memory is allocated, the returned address is saved in a link register (LR). During the process running, the system adds information, such as the LR corresponding to the suspected leak, to the memory node control block. <xrefhref="#fig716011269106"idp:producemode_text="auto"class="- topic/xref "id="xref106398301961"></xref> shows the heap memory node information.
**TID** indicates the thread ID; **PID** indicates the process ID; **ptr** indicates the address of the memory requested; **size** indicates the size of the requested memory; **lr[*n*]** indicates the address of the call stack, and *n* is configurable.
When memory is released, the input parameter pointer in the **free** API is used to match the **ptr** field of the memory node. If the pointer is the same as the **ptr** field of the memory node, the memory node control block will be deleted.
You can export the memory debugging information of each process through the serial port or file, and use the addr2line tool to convert the exported information into the code lines that cause memory leaks. In this way, the memory leakage problem can be solved.
**Figure 3** Process of locating the code line for a memory leak
You can collect statistics on the percentage of heap memory requested by each thread to provide data support for optimizing memory usage of user programs. The **malloc** and **free** APIs are involved in user-mode heap memory statistics. As shown in the figure above, each process maintains 128 linked lists, and the index of each linked list is a thread ID. When heap memory is requested, the **ptr** and **size** information is recorded in the memory node control block, which is inserted to a linked list with the thread ID as the header. When the heap memory is released, the corresponding heap memory block is removed from the linked list based on the **ptr**. In addition, the system calculates the total heap memory used by the current thread and updates its heap memory usage and peak heap memory usage.
### Memory Integrity Check
- If the memory requested by using **malloc** is less than or equal to 0x1c000 bytes, the heap allocation algorithm is used to allocate memory.
When a user program requests heap memory, information such as the check value is added to the heap memory node. If the check value is abnormal, it is probably that the previous heap memory block is overwritten. Currently, the scenario where the check value is damaged by a wild pointer cannot be identified. When memory is allocated or released, the memory node check value is verified. If the memory node is corrupted and the verification fails, the following information is output: TID, PID, and call stack information saved when the previous heap memory block of the corrupted node is allocated. You can use the addr2line tool to obtain the specific code line and rectify the fault.
**Figure 4** Adding a check value to the node header information
When heap memory is released by using **free**, the memory block is not released immediately. Instead, the magic number 0xFE is written into the memory block, which is then placed in the free queue to prevent the memory block from being allocated by **malloc** within a certain period of time. When a wild pointer or **use-after-free** operation is performed to read the memory, an exception can be detected. However, this mechanism does not apply to write operations.
- If the memory requested by using **malloc** is greater than 0x1c000 bytes, **mmap** is used to allocate memory.
When **mmap** is used to request a large memory block, one more page is allocated at the start and end of the memory region. The current **PAGE_SIZE** of each page is **0x1000**. The permissions of the two pages are set to **PROT_NONE** (no read or write permission) by using the **mprotect** API to prevent out-of-bounds read and write of memory. If out-of-bounds read and write of memory occurs, the user program becomes abnormal because the user does not have the read or write permission. The code logic can be identified based on the abnormal call stack information.
**Figure 6** Layout of the memory allocated by using the **mmap** mechanism of **malloc**
| mem_check_init | Initializes the memory check module.|
| watch_mem | Obtains the thread-level heap memory usage.|
| check_leak | Checks for heap memory leaks.|
| check_heap_integrity | Checks the heap memory integrity.|
| backtrace | Obtains the address information of the call stack.|
| backtrace_symbols | Obtains symbol information based on address information.|
| print_trace | Prints call stack information.|
**Table 2** Call stack backtracking APIs
| API| Description|
| -------- | -------- |
| backtrace | Obtains the address information of the call stack.|
| backtrace_symbols | Obtains symbol information based on address information.|
| print_trace | Prints call stack information.|
### How to Use
By default, the OpenHarmony debug version is compiled when a project is built. The libc library of the debug version has integrated the APIs for memory debugging. You can enable memory debugging as required.
You can perform heap memory debugging by using either of the following:
- API: By calling APIs, you can accurately check the heap memory information of a specific code logic segment. However, you have to modify user code.
- CLI: By using the CLI, you do not need to modify user code. However, you cannot accurately check the heap memory information of a specific logic segment.
> After memory debugging is enabled, a heap memory leak check and a heap memory integrity check will be performed by default when a process exits. If memory debugging is disabled, the heap memory statistics, heap memory leak check, and heap memory integrity check cannot be enabled, and there is no response to the calling of any debug API.
#### Calling APIs
##### Sample Code
The sample code explicitly calls the related APIs of the memory debugging module to check the memory.
```
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <debug.h> // Header file that includes the declaration of the memory debugging APIs
#define MALLOC_LEAK_SIZE 0x300
void func(void) {
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE);
}
int main()
{
mem_check_init(NULL); // Output the memory debugging information through the serial port. This function must be called before the user program requests the heap memory for the first time (generally called at the entry of the main function). Otherwise, the debugging information is inaccurate.
// mem_check_init("/storage/mem_debug.txt"); // Output the memory debugging information to the /storage/mem_debug.txt file. If the file fails to be created, output the information through the serial port.
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '1', MALLOC_LEAK_SIZE);
watch_mem(); // Obtain the thread-level memory statistics in the current code.
func();
check_heap_integrity(); // Check the integrity of the heap memory nodes.
check_leak(); // Check whether a heap memory leak occurs in the current code. (Generally, the check result is accurate before the application exits. If the check is performed after the calling of malloc and before the calling of free, the result is inaccurate.)
> - In this example, the compiler path is written into an environment variable in the **.bashrc** file.
>
> - When compiling user programs and required libraries, add the option **-funwind-tables -rdynamic -g** for stack backtracking.
>
> - The **-mfloat-abi=softfp**, **-mcpu=cortex-a7**, and **-mfpu=neon-vfpv4** options specify the floating-point calculation optimization, chip architecture, and FPU, which must be the same as the compilation options used by the libc library. Otherwise, the libc library file cannot be found during the link time.
>
> - **-target arm-liteos** specifies the path of the library files related to the compiler.
>
> - **--sysroot=/home/<user-name>/harmony/out/hispark_taurus/ipcamera_hispark_taurus/sysroot** specifies the root directory of the compiler library files. In this example, the OpenHarmony project code is stored in **/home/<user-name>/harmony**. The **out/hispark_taurus/ipcamera_hispark_taurus** directory indicates the product specified by the **hb set** command during compilation. In this example, **ipcamera_hispark_taurus** is the product specified.
>
> - **$(clang -mfloat-abi=softfp -mcpu=cortex-a7 -mfpu=neon-vfpv4 -target arm-liteos -print-file-name=libunwind.a)** specifies the path of the unwind library.
==PID:4== Detected memory leak(s): // Memory leak information and call stack
[Check point]:
#00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so
#01: <main+0x44>[0x878] -> mem_check
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x1c>[0x850] -> mem_check
#01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x810] -> mem_check
#01: <main+0x3c>[0x870] -> mem_check
#02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so
#01: <exit+0x28>[0x111ec] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x1c>[0x850] -> mem_check
#01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x810] -> mem_check
#01: <main+0x3c>[0x870] -> mem_check
#02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
Check heap integrity ok!
```
##### Call Stack Parsing
The **parse_mem_info.sh** script in **kernel/liteos_a/tools/scripts/parse_memory/** can be used to parse the call stack. You can use the script to convert the debug information into specific source code line number. In the following command, **mem_debug.txt** stores the memory debugging information, and **elf1** and **elf2** are the executable and linkable format (ELF) files to parse.
#00: <main+0x38>[0x86c] at /usr1/xxx/TEST_ELF/mem_check.c:22
#01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
[TID: 18, Used: 0x320]
==PID:4== Total heap: 0x320 byte(s), Peak: 0x320 byte(s)
Check heap integrity ok!
==PID:4== Detected memory leak(s):
[Check point]:
#00: <check_leak+0x1c4>[0x2da4c] -> /lib/libc.so
#01: <main+0x44>[0x878] at /usr1/xxx/TEST_ELF/mem_check.c:28
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <main+0x1c>[0x850] at /usr1/xxx/TEST_ELF/mem_check.c:17
#01: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x810] at /usr1/xxx/TEST_ELF/mem_check.c:9
#01: <main+0x3c>[0x870] at /usr1/xxx/TEST_ELF/mem_check.c:24
#02: <(null)+0x24baf9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
#### Using the CLI
In addition to calling APIs to check the memory used by user-mode processes, you can run CLI commands to collect memory statistics, check for memory leaks, and check memory integrity.
```
--mwatch: initializes memory debugging, registers signals, and outputs memory debugging information through the serial port.
--mrecord <f_path>: initializes memory debugging, registers signals, and saves the memory debugging information to the f_path file. If the f_path file fails to be created, output the memory debugging information through the serial port.
```
If the process to debug does not exit, you can use the signal mechanism to obtain the corresponding information:
```
kill -35 <pid> # Check the thread-level heap memory usage.
kill -36 <pid> # Check for heap memory leaks.
kill -37 <pid> # Check whether the head node of the heap memory is complete.
```
##### Sample Code
The sample code constructs a memory problem and uses the command line to perform memory debugging.
```
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#define MALLOC_LEAK_SIZE 0x300
void func(void) {
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '3', MALLOC_LEAK_SIZE);
}
int main()
{
char *ptr = malloc(MALLOC_LEAK_SIZE);
memset(ptr, '1', MALLOC_LEAK_SIZE);
func();
while (1);
}
```
##### Compilation
For details, see [Compilation](kernel-small-debug-user.md#compilation).
##### Running the mwatch Command
```
OHOS # ./mem_check --mwatch // Run the task command to obtain the mem_check process PID, which is 4.
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
OHOS # kill -37 4 // Check the integrity of the head node of the heap memory.
OHOS #
Check heap integrity ok!
```
##### Call Stack Parsing
Save the debugging information to the **test.txt** file and use the script to parse the information to obtain the number of the line where the memory leak occurs.
#00: <main+0x14>[0x724] at /usr1/xxx/TEST_ELF/mem_check.c:14
#01: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so
[TID:18 Leak:0x320 byte(s)] Allocated from:
#00: <func+0x14>[0x6ec] at /usr1/xxx/TEST_ELF/mem_check.c:8
#01: <main+0x30>[0x740] at /usr1/xxx/TEST_ELF/mem_check.c:19
#02: <(null)+0x2555a9dc>[0x219dc] -> /lib/libc.so
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
##### Running the mrecord Command
1. Run the user program and specify the path of the file that stores the memory debugging information.
```
OHOS # ./mem_check --mrecord /storage/check.txt
```
2. Run the **kill -35 <*pid*>** command to collect statistics on the memory information. The information is exported to a file. Run the **cat** command to view the information.
```
OHOS # kill -35 4
OHOS # Memory statistics information saved in /storage/pid(4)_check.txt
==PID:4== Total heap: 0x640 byte(s), Peak: 0x640 byte(s)
```
3. Run the **kill -36 <*pid*>** command to check memory integrity. The information is exported to a file. Run the **cat** command to view the information.
```
OHOS # kill -36 4
OHOS # Leak check information saved in /storage/pid(4)_check.txt
==PID:4== SUMMARY: 0x640 byte(s) leaked in 2 allocation(s).
```
4. Run the **kill -9 <*pid*>** command to kill the current process. After the process exits, a memory integrity check is performed by default. The check result is output to a file. You can run the **cat** command to view it.
```
OHOS # kill -9 4
OHOS # Leak check information saved in /storage/pid(4)_check.txt
> The preceding information recorded gradually is added to the file specified during initialization. Therefore, running the **cat** command can also display the historical information in the file.
## Common Problems
### Use After Free (UAF)
- Requested memory block less than or equal to 0x1c000 bytes:
After the memory is released:
Read operation: If the magic number (0xFEFEFEFE) is read from the memory block released, UAF occurs.
> After **free** is called, the heap memory will not be released to the heap memory pool immediately. Instead, the heap memory is placed in a queue with a fixed length and filled with the magic number 0xFE. When the queue is full, the memory block first placed in the queue is released to the heap memory pool first.
Write operation: The memory debugging module cannot detect UAF errors from write operations.
- Requested memory block greater than 0x1c000 bytes:
The heap memory greater than 0x1c000 bytes must be requested by calling the **mmap** API via **malloc**. If the heap memory is accessed after being released, the user program will become abnormal (because the memory region has been unmapped).
### Double Free
Double free errors occur when **free()** is called more than once with the same memory address as an argument. When a double free error occurs, the user program exits unexpectedly.
### Heap Memory Node Corrupted
- Requested memory block less than or equal to 0x1c000 bytes:
When a heap memory node is corrupted, the user program exits unexpectedly, and the call stack that requests the heap memory of the node corrupted is output. The memory debugging module, however, cannot debug the memory corrupted by a wild pointer. For example, if the user program mem_check has heap memory overwriting, you can use the command line to obtain the possible location of the memory corruption.
```
OHOS # ./mem_check --mwatch
OHOS #
==PID:6== Memory integrity information:
[TID:28 allocated addr: 0x272e1ea0, size: 0x120] The possible attacker was allocated from:
#00: <malloc+0x808>[0x640e8] -> /lib/libc.so
#01: <threadFunc1+0x7c>[0x21d0] -> mem_check
```
You can use the call stack parsing script to parse the call stack information.
- Requested memory block greater than 0x1c000 bytes:
When a large memory block (greater than 0x1c000 bytes) is requested by calling the **mmap** API via **malloc**, one more page of **PAGE_SIZE** is allocated at the start and end of the memory region. The two pages are neither readable nor writeable. Any read or write operation to the pages may cause an exception of the user program.