Memory Management Methods in Linux Embedded Systems

Source: Internet
Author: User

1 Requirements for memory allocation in Embedded Systems

① Fast.The real-time guarantee in embedded systems requires that the memory allocation process be as fast as possible. Therefore, in embedded systems, it is impossible to adopt complex and perfect memory allocation policies in general operating systems. Generally, simple and fast memory allocation solutions are adopted. Of course, there are different allocation schemes for different programs with different requirements on implementation. For example, VxWorks adopts a simple first matching method such as instant aggregation; VRTX adopts multiple fixed-size binning schemes.

② Reliability.That is, the request for memory allocation must be satisfied. If the allocation fails, it may have disastrous consequences. Embedded system application environments are ever-changing, some of which have extremely high reliability requirements. For example, in the auto-Driving System of a car, the system detects that the car is about to crash. If the memory allocation fails and cannot be operated, the car will crash. This is intolerable.

③ Efficiency.Memory allocation should be minimized. It is impossible to increase the memory size to meet all memory allocation requests. On the one hand, the cost requirements of embedded systems make the internal existence only a very limited resource; on the other hand, even if you do not consider the cost, the limited space and area of the system determine that the configurable memory capacity is limited.

2. Static and Dynamic Allocation

Whether applications use static or dynamic allocation remains a constant debate in the design of embedded systems.

Of course, the most appropriate answer is to adopt different solutions for different systems. If the system requires extremely high real-time performance and reliability, it cannot tolerate a delay or a allocation failure. Of course, the static allocation scheme is required, that is, the memory required for program compilation has been allocated. For example, the embedded system on the Mars detector must adopt a static allocation scheme.

In addition, a real-time operating system OSEKWorks dedicated for automotive electronics and industrial automation does not support dynamic memory allocation. In such applications, the cost does not support dynamic memory allocation. In such applications, cost is not a priority, and real-time and reliability must be ensured. Of course, it is inevitable that the system loses flexibility to adopt static allocation. In the design phase, the required memory must be known and allocated in advance; all possible situations must be taken into account in advance during the design phase, as the system will not be able to handle any situations that are not taken into account.

Such a allocation scheme must cause a great waste, because the memory allocation must be configured according to the worst case. In fact, only a small part of the allocation scheme may be used during the operation; in addition, without changing the hardware platform, it is impossible to flexibly add functions to the system, making it difficult to upgrade the system. Most systems are integrated with hard real-time systems and soft real-time systems. That is to say, some tasks in the system have strict time limit requirements, while the other part only requires the faster the completion, the better.

According to the RMSRate Monotoin Scheduling) theory, such a system must adopt preemptive task Scheduling. In such a system, dynamic memory allocation can be used to meet tasks with low reliability and real-time requirements. The advantage of dynamic memory allocation is that it gives designers great flexibility to easily transplant Programs originally running on non-embedded operating systems to embedded systems. For example, network protocol stacks used in many embedded systems. If static memory allocation is required, porting such a protocol stack is much more difficult. In addition, the use of dynamic memory allocation allows the designer to adjust the system functions flexibly without changing the basic hardware platform, and to make a balance between the functions of the system. For example, you can adjust the number of supported VLANs and the number of supported route entries, or different versions support different protocols.

After all, the dynamic memory is allocated to embedded system programmers with less restrictions and greater freedom. Therefore, most real-time operating systems provide dynamic memory allocation interfaces, such as malloc and free functions.

3. Memory Allocation interface provided by RTOS

Different RTOS adopt different memory allocation policies for different locations.

For example, in VRTX, a memory allocation scheme similar to the Binning algorithm developed by Doug Lea In gnu c is used. The system memory is divided into some fixed-size memory block algorithms, the system memory is divided into a set of fixed-size memory blocks. The advantage of this method is that the search speed is fast and no memory fragments are generated. However, its disadvantage is also obvious, that is, it is easy to cause waste, because the size of the memory block is only limited, the allocation can only take a large memory block to meet a small demand, in addition, the memory allocation table managed by the operating system is also a huge burden. The following describes in detail the memory allocation policy adopted in VxWorks of the popular RTOS -- WindRiver of US Wind River Corporation.

The predecessor of VxWorks is VRTX. It is said that the name of VxWorks is from make vrtx work. The memory management function of VxWorks exists in two libraries; memPartLib compact memory partition manager) and memLib complete memory partition manager ). MemPartLib) provides tools for allocating memory blocks from memory partitions. This library contains two types of programs. One is a common tool for creating and managing memory partitions and allocating and managing memory blocks from these partitions; the other is that the standard malloc/free program provides interfaces with memory partitions. The system memory partition ID is memSysPartId, which is a global variable) is created by calling memInit by usrRoot during kernel initialization. The starting address is after the BSS segment of VxWorks in RAM, And the size is all idle memory, as shown in 1.

When creating other partitions, you generally need to call malloc to allocate a memory segment from the system memory partition before creating the partition. The memory partition structure is defined as mem_part, which contains one object tag, one idle block managed by a two-way linked list, and one semaphore to protect the partition and some statistical information, such as the total size, maximum block size, debugging options, number of allocated blocks, and allocated size. The statement is as follows:

     
      
Typedef struct mem_part {OBJ_CORE objCore;/* object flag */DL-LIST freeList;/* idle linked list */SEMAPHORE sem;/* protect the SEMAPHORE of the partition */Unsigned totalWords; /* Number of words in the partition */Unsigned minBlockWords;/* Minimum block size in the unit of words */Unsigned options;/* options, used for debugging or statistics * // * allocation Statistics */unsigned curBlocksAllocated;/* Number of currently allocated blocks */unsigned curWorkdAllocated;/* Number of currently allocated words */unsigned cumBlockAllocated; /* Number of accumulated blocks */unsigned cumWordsAllocated;/* Number of accumulated allocated words */} PARTITION;
     

Generally, there is only one memory partition in the system, that is, the system partition. The memory required by all tasks is allocated by calling malloc directly. Allocation uses the First-Fit algorithm to note that this algorithm can easily cause a large number of fragments). Memory released through free will be aggregated to form larger idle blocks. This is the memory allocation mechanism of VxWorks. A certain alignment format is required during allocation. Note that different CPU architectures have different alignment requirements.

To optimize performance, the pointers returned by malloc are aligned, and the overhead varies with the structure. For example, 68 KB is 4-byte alignment, with an overhead of 8 bytes, 8 bytes alignment, 12 bytes overhead, 16 bytes alignment for MIPS, 12 bytes overhead, and 16 bytes alignment for I960, the overhead is 16 bytes.

The MemLib Library provides enhanced memory partition management tools, some interfaces are added, and debugging options can be set. Two types of errors can be detected: ① try to allocate too much memory; ② find bad blocks when releasing the memory. There are four options for error handling. When an error occurs, the message is recorded or the task is suspended. However, when using dynamic memory to allocate malloc/free, you must note the following restrictions. ① Because the system memory partition is a critical resource protected by semaphores, using malloc will cause the current call to be suspended, so it cannot be used to interrupt the service program; ② because the search algorithm needs to be executed for memory allocation, the execution time is related to the current memory usage of the system and is uncertain. Therefore, it is not suitable for operations with a specified time limit; ③ The use of a simple first matching algorithm can easily lead to a large amount of memory fragments in the system, reducing memory usage efficiency and system performance.

In this case, static allocation and dynamic allocation are generally used in system design. That is, for important applications, the required memory is configured during system initialization. In the process of system operation, no memory allocation/release will be performed, so as to avoid the constant increase caused by memory allocation and release. In addition, because there is no memory fragmentation during system initialization, the demand for large memory blocks is easily met. For other applications, dynamic memory allocation is performed during runtime. In particular, some applications require a large number of small memory blocks of fixed sizes. In this case, you can use a memory allocation scheme that is allocated multiple times at a time. The following describes the memory allocation scheme and its application scenarios in detail.

4. Memory Allocation Scheme for multiple times at a time

Embedded systems often have applications similar to memory databases.

These applications manage some trees in the memory, such as the MAC address table and VLAN table in the Ethernet switch, or the route table in the router. These trees are composed of many nodes of the same size. In this way, you can allocate a large buffer pool each time, such as an array containing multiple memory units, each of which is used for one node. We use an idle linked list to manage idle memory units in the array. Each time the program needs to allocate memory to create a new node, it will take one unit from the idle linked list to the caller. When the program deletes a node and releases the memory, the released memory units are returned to the idle linked list.

If the idle memory unit in the linked list is empty, call malloc again to allocate a large memory block from the system memory as the new buffer pool. Using such a scheme has the following advantages: ① reduces the number of malloc/free calls, thus reducing risks and fragments; ② It is time-determined to take a memory unit from the buffer pool. Of course, if the buffer pool is exhausted and you need to re-call the malloc allocation), it can be used in scenarios with strict time limits to ensure real-time performance; ③ it allows users to add debugging functions and statistical functions for memory allocation and release to better monitor the memory usage in the system. This solution involves the structure of a buffer pool. Generally, the buffer pool structure consists of the following parts: unit size, block size, or number of units), buffer pool pointer, idle linked list, and parameters used for statistics and debugging. Operations on the buffer pool include creating a buffer pool, releasing the buffer pool, allocating one memory unit from the buffer pool, and releasing the memory unit back to the buffer pool. The following two examples describe the specific usage of the solution.

4.1 memory allocation in the Intel switch driver

In Intel's switch chip-based switch solution, because the software address learning method is used, a lot of data needs to be maintained in the memory, such as MAC address table soft copy, VLAN table, static Unicast address table, and multicast address table. These tables are composed of several trees, each of which consists of some fixed-size nodes. Generally, each node has dozens of bytes. The number of nodes in each tree can increase, but the number of nodes is dozens. A maximum of 16 K nodes are allowed. Therefore, this solution is suitable for use. The specific implementation is as follows:

1)Buffer Pool Structure BlockMemMgr typedef struct {MemSize data_cell_size;/* data unit size */MemSize block_size; /* block size * // * the variables below are the maximum number of blocks contained by each predefined manager, such as 64 MAX_BLOCKS_OF_MEM_SIZE */Unsigned short blocks_being_used; /* Number of used blocks */Void mem_ptr [PAX_BLOCKS_OF_MEM_SIZE];/* block array */SLList free_data_cells_list;/* idle linked list */} parameters in the BlockMemMgr structure include: unit size, block size, number of used blocks, address of all blocks, one-way linked list of idle linked list ).

2)Buffer pool management functions

◆ Block_mem_create: Create a block memory manager. The parameters include the memory pointer. If it is NULL, it indicates that it is allocated by itself.) block size, unit size, and return manager pointer. The process is as follows: ① check the parameter validity. ② The unit size is 4-byte aligned, and the number of units in each block is calculated. 4-byte alignment for memory pointers or allocate memory pointers. ③ Initialize the block memmgr, including the unit size and block size. Set the pointer of 1st memory blocks. If the memory is external, set the block used flag to 0), it means that the block cannot be added; otherwise, the number of used blocks is set to 1. ④ Create an idle linked list and add all units in the block to the linked list. The last unit is at the beginning of the linked list. ⑤ Return BlockMemMgr.

◆ Block_mem_destroy: deconstruct a block memory manager to release all memory allocated by the manager. The caller is responsible for releasing the external memory. The parameter is BlockMemMgr. The success or failure flag is returned. ① Check the parameter validity. ② Delete a one-way linked list and set the linked list pointer to NULL ). ③ If blocks are dynamically allocated, release them. ④ Release the BlockMemMgr structure.

◆ Block_malloc: allocate one unit from the block memory manager. The parameter is BlockMemMgr, and the data unit pointer is returned. ① Check the parameter validity. ② Determine whether the idle linked list is NULL or not ). If it is null, check whether the block can be dynamically allocated. If not, return failure. If the block can be dynamically allocated, allocate one block and perform the same operations as block_mem_create. ③ Allocate 1st units from the idle linked list and return their pointers. Note that there is a small trick: when the data unit is idle, it stores the node information of the idle linked list, and after allocation, it stores the data content.

◆ Block_free: release one data unit and return to the block memory manager. Be careful not to release one unit twice. The parameters are BlockMemMgr and cell pointer. ① Check the parameter validity. ② Compare the addresses to determine which data unit belongs. ③ Determine whether the content of a data unit is the node information of the idle linked list, that is, the address of a unit in the block) to determine whether it is released twice. ④ Insert the data unit to the front of the idle linked list. ⑤ The pointer that references this unit is set to NULL.

The Memory Management Code complies with the following conventions:① The managed memory is actually writable memory; ② the allocated memory is 4 bytes or 32-bit alignment; ③ block_malloc and block_free are partially secure in interrupt-level calls, unless there is no idle CELL in the BLOCK, you need to re-call malloc to allocate a new BLOCK, and malloc and free are not safe because semaphores and search algorithms are used, may cause service program interruption ). Of course, block_mem_create and block_mem_destroy must be called at the process level.

4.2 memory allocation in TMS

TMS is a development kit launched by WindRiver for managed switches. It uses IDB to manage data of various protocols, such as STPS and GVRP. To support IDB, it has established its own buffer pool management solution. The program is in bufPoolLib. c. This program contains functions used for buffer pool management. These functions allow allocation of a fixed number and size of buffers from one pool.

By pre-allocating a certain number of fixed-size buffers, we can avoid repeated small memory block allocation/release of memory fragments and waste. Since it allocates a buffer pool from a single block, it is more efficient than executing one allocation for each buffer zone. The module adds a flag of MAGIC to each buffer zone. When the buffer is released, the flag is checked. The module provides users with the ability to allocate and release operation-defined callback functions.

In this way, objects can be created and reconstructed automatically, and objects composed of members allocated by multiple buffer pools can be deleted as a single object. This is similar to the Automatic Object Construction and deconstruct in C ++, but it uses C language without the burden of stack allocation. The module allows you to allocate a buffer pool from the stack through calloc) or create them in the space you allocate. The module uses a one-way linked list to maintain unallocated buffers, but does not track allocated buffers. The module is not task-safe. You need to protect the buffer pool when using signals.

1)Buffer Pool Structure

     
      
Typedef struct {ulong_t magic;/* special mark for consistency detection */Boolean localAlloc;/* Whether the memory is allocated when the buffer is created */SL_LIST freeList; /* idle linked list */Void store;/* memory pointer to the buffer */STATUS (* createFn) (void *, ulong_t argl ); /* The callback function pointer */STATUS * destroyFn) (void *, ulong_targl) when the buffer is created;/* The callback function pointer */Ulong_t argVal when the buffer is released; /* callback function parameter */} buf_pool_t;
     

Parameters in the structure include checking for MAGIC, local allocation, idle linked list, memory pointer, callback function pointer for creating the buffer pool, callback function pointer for release, and callback function parameter.

2)Related functions

◆ BufPoolInitializeStorage: allocate and initialize the storage zone. Parameters include the storage area address (if it is NULL, it is allocated locally), the buffer size, and the number of buffers. ① Obtain the required memory size based on the buffer size and number. ② If the pointer is NULL, calloc is called to allocate memory. Set the local allocation flag. ③ The initial memory is 0. ④ Initialize the pointer. The buffer pool structure buf_pool_t is at the top of the allocated memory block. The actual storage area follows. Buf_pool_t includes the parameter check mark, local allocation, storage address, callback function during allocation, callback function during release, and callback function variable. In this case, only the storage area pointer is set.

◆ BufPoolCreate: Creates a buffer pool. The parameter is memory stop. Buffer size and number, callback function at creation, callback function at release, callback function parameters. ① Size alignment. ② Call bufPoolInitializeStorage to initialize the memory zone and buf_pool_t structure. ③ Fill the buf_pool_t structure with input parameters. ④ Add the buffer to the idle linked list, and the last buffer is at the beginning.

◆ BufPoolDestroy: Delete the buffer pool. The parameter is the buf_pool_t pointer. ① Check whether the MAGIC field in the buffer pool structure is personalized. ② If it is locally allocated, the memory area is switched over.

◆ BufPoolAlloc: allocates a buffer pool from the buffer pool. The parameter is the buffer pool structure pointer. If there is an idle buffer, it is removed from the idle linked list and provided to the caller. The callback function is executed during creation. If the callback function returns an error, the buffer is returned to the idle linked list. ① Check whether the MAGIC mark in the buffer pool structure is in good condition. ② Extract the first node from the idle linked list. ③ If the node is not empty, clear the node and use its address as the parameter to call the callback function. ④ If the callback function returns an error, the node is returned to the idle linked list. ⑤ Return the idle buffer address.

◆ BufPoolFree: return the buffer pool to the buffer pool. If a callback function is defined, the callback function is called between the return buffer. The parameters are the Buffer Pool Structure and buffer pointer. ① Check whether the buffer pool MAGIC mark is in good condition. ② If callback functions are defined and called. If an error is returned, the error number is set. ③ Add the buffer to the header of the idle linked list. Note that this function has two points: ① the callback function returns an error and returns the buffer. ② The buffer zone is not checked for Secondary release, which is different from the Intel driver. In addition, the buffer pool of TMS does not have BLOCK essentials, and you do not need to determine which CELL belongs to which BLOCK, which simplifies the operation.

5 Summary

Many embedded applications write their own memory management solutions based on the malloc/free provided by RTOS.

Writing such a memory management solution has two purposes: one is to reduce the dependency on malloc/free, so as to avoid memory fragmentation and time uncertainty; the other is to enhance the program's error checking capability and send back memory usage errors. For memory requirements of a widely used database type in embedded systems, that is, the need to allocate multiple fixed-size memory units, "one-flash allocation, the "multiple-use" solution is undoubtedly a good solution. The two examples in this article demonstrate its superiority.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.