This article guides reading
During the running of the program, some data may be generated, for example, data received by the serial port and data acquired by the ADC. If data needs to be stored in the memory for further calculation and processing, a suitable memory space should be allocated to it, and after the data is processed, the corresponding memory space is released. In order to facilitate the allocation and release of memory, AWorks provides two kinds of memory management tools: heap and memory pool.
This article is "for the AWorks framework and interface programming (on)" Part III software - Chapter 9 memory management - Sections 1 to 2: heap manager and memory pool.
This chapter guides reading
In a computer system, data is generally stored in memory. Only when the data needs to participate in the operation is it removed from the memory and passed to the CPU for calculation. After the operation, the data is stored back in the memory. This requires the system to allocate appropriate memory space for various types of data.
The amount of memory required for some data can be determined before compiling. There are two main types: one is a global variable or a static variable. This part of the data is valid for the whole life cycle of the program. It allocates a fixed memory space for these data at compile time, and can be used directly afterwards without additional management. A class is a local variable, this part of the data is valid only in the current scope (such as in the function), they need memory allocated automatically from the stack, and no additional management, but it should be noted that due to this part of the data Memory is allocated from the stack. Therefore, you need to ensure that your application has enough stack space. Try to avoid defining local variables that occupy a large amount of memory (for example, an array that occupies several K of memory) to avoid stack overflow. Stack overflow may damage the system. The key data is very likely to cause the system to crash.
The memory size required by some data needs to be determined according to the actual situation during the running of the program, and cannot be determined before compiling. For example, a 1K memory space may be temporarily needed to store data sent from the remote end through the serial port. This requires that the system has the ability to dynamically manage the memory space. When the user needs a memory space, he or she will apply to the system. The system selects a suitable memory space for the user. After the user completes the use, it is released back to the system so that the system will This section of memory space is recycled and reused. In AWorks, there are two common memory management methods: heap and memory pools.
9.1 Heap Manager
The heap manager is used to manage a section of continuous memory space, and can allocate any size of memory block according to the user's needs in the case of satisfying system resources. It is completely "assigned on demand". When the user no longer uses the allocated memory block, Memory blocks can be released back to the heap for use by other applications. Similar to the function implemented by the malloc()/free() function in the C standard library.
9.1.1 Overview of the Heap Manager
Before using the heap manager, a brief introduction to its principle is provided through an example, so that users can use the heap manager more effectively. For example, use the heap manager to manage 1024 bytes of memory space. Initially, all memory is idle and the entire memory space can be considered as a large free memory block. The schematic diagram is shown in Figure 9.1.
Figure 9.1 Initial State - Individual Free Memory Blocks
The memory allocation strategy is: When a user requests a certain amount of memory space, the heap manager starts searching from the first free memory block until it finds a free memory block that meets the user's needs (that is, the capacity is not less than the memory space requested by the user. Size), then allocates a user-specified size of memory space from this free memory block.
For example, the user applies for 100 bytes of memory space to the heap manager. Since the capacity of the first free memory block is 1024, the demand can be satisfied. Therefore, 100 bytes of memory space can be allocated to the user from the allocated memory. The schematic diagram is shown in Figure 9.2.
Figure 9.2 allocate 100 bytes - split into two memory blocks
Note: The hatch is shaded to indicate that the block has been allocated. Otherwise, the block is not allocated and is in an idle state. The number indicates the capacity of the block.
Similarly, if the user requests three consecutive memory spaces from the heap manager, the memory space for each request is: 150, 250, and 200 bytes, respectively. For the details of the allocated memory, see Figure 9.3.
Figure 9.3 Reassign 100, 300, and 200-Byte Memory Spaces - Split into Five Memory Blocks
As the allocation continues, there may be more and more memory blocks. If the user's request cannot be satisfied, the allocation fails. If the user requests 400 bytes of memory on the basis of Figure 9.3, there is only one free block and the capacity is 324 bytes, which cannot satisfy the requirement. The allocation fails and the user cannot get a valid memory space.
When a certain segment of memory requested by the user is no longer used, the memory block should be released back to the heap for recycling.
The recovery strategy is: When a user releases a memory block back to the heap, first, the memory block becomes a free block, if the memory block adjacent to the memory block is also a free block, they are merged into a large Free memory block.
For example, on the basis of Figure 9.3, the previously allocated 250-byte memory space is freed. The memory block is first made free, as shown in Figure 9.4.
Figure 9.4 Freeing a Memory Block - Neighboring Blocks Are Not Free Blocks
Since the memory block is not a free block before and after, there is no need for a merge operation. At this point, 250 bytes of free space are freed, and there is a total of 574 bytes of memory in the heap. However, if the user requests 400 bytes of memory space from the heap manager at this time, since the first free block and the second free block do not meet the requirements, the memory allocation will still fail.
This exposes the drawbacks of a heap manager. Frequent allocation and release of memory blocks of different sizes will generate many memory fragments, namely free memory blocks interrupted by allocated memory blocks in Figure 9.4. The figure shows free memory with a capacity of 250 bytes and a capacity of 324 bytes. The block is interrupted by a memory block with an allocated capacity of 200 bytes, so that each free block is physically discontinuous and cannot form a large free block. In this case, even if the size of the requested memory space is less than the total size of the free space of the current heap, the allocation may fail because there is not a large free block.
On the basis of Fig. 9.4, even if the first allocated 100-byte memory space is released, the total free space reaches 674 bytes, as shown in Fig. 9.5, and the allocation request for a 400-byte memory space cannot be satisfied.
Figure 9.5 Releases a 100-Byte Memory Block
With the allocation and recovery of a large number of memory blocks in the system, the memory fragmentation will be more and more, and some memory blocks that are not released for a long time will always be divided into some free memory blocks, resulting in memory fragmentation. This also informs the user that when a memory block is no longer used, the memory block should be released as soon as possible to avoid causing excessive memory fragmentation.
In Figure 9.5, there are 3 free blocks. If the user requests a 280-byte memory space on this basis, the heap manager first looks at the first free block according to the allocation strategy. Its capacity is 100 bytes. Satisfy the demand, and then look at the second free block, its capacity is 250 bytes, the same can not meet the demand, then look at the third free block, its capacity is 324 bytes, and finally allocate a space from the block to the user, allocate See Figure 9.6 for the memory diagram after.
Figure 9.6 Redistribute 280 bytes of memory space
On the basis of Fig. 9.6, if the user releases the 200-byte memory block first, it is first made free, as shown in Figure 9.7.
Figure 9.7 Release 200 bytes of memory space (1) - Marked as free
Since the memory block whose size is 250 on the left side is also an idle block, it is necessary to merge them into one large memory block, that is, to merge into a memory block with a size of 450, as shown in the figure in Figure 9.8.
Figure 9.8 Release of 200 bytes of memory space (2) - Merge with adjacent free blocks
At this point, since there is a free block of size 450, if the user requests 400 bytes of memory at this time, the application can be successfully applied. Compared with Figure 9.5, although Figure 9.5 has a total of 674 bytes of free space and Figure 9.8 has only 594 bytes of free space, Figure 9.8 can satisfy a 400-byte memory space request. It can be seen that, due to memory fragmentation, the total free space size does not determine the success or failure of a memory request.
The schematic diagram after successful application of 400 bytes is shown in Figure 9.9.
Figure 9.9 400-byte memory space
On the basis of Fig. 9.9, if the user releases another 280-byte memory block, it will be the same as the free block first. The figure is shown in Figure 9.10.
Figure 9.10 Release 280 bytes of memory space (1) - Marked as free
Since both left and right sides are free blocks, they need to be merged into one large memory block, that is, they are merged into a memory block of size 374, as shown in Figure 9.11.
Figure 9.11 Release 280 bytes of memory space (2) - Merge with adjacent free blocks
The reason for merging adjacent free memory blocks is to avoid memory blocks becoming more and more fragmented. If this continues, it will be difficult to have a large free block. Users will then apply for large memory free. Will not meet the demand.
Through a series of examples of memory allocation and release above, the heap manager's strategy of allocating and releasing memory is shown, enabling the user to have a certain understanding of the relevant principles.
In fact, in the software implementation of the heap manager, in order to facilitate management, each memory block is organized in the form of a linked list. The head of each memory block is fixed to store some related information for memory block management. Figure 9.12.
Figure 9.12 Memory block (with free memory blocks and allocated memory blocks) linked list
The figure shows four very important pieces of information: magic, used, p_next, and p_prev.
Magic is called Magic Number and is assigned a special fixed value. It indicates that the memory block is a memory block managed by the heap manager and can check the memory operation to a certain extent. For example, if this area is rewritten, magic's value is modified to other values, indicating that there is an illegal memory operation, may be the user's memory operation across the border, etc., should be handled in a timely manner; when a memory block is released, the heap manager will check the magic's The value, if its value is not a special fixed value, indicates that this is not a memory block allocated by the heap manager. This release operation is illegal.
Used is used to indicate whether the memory block has already been used. If the value is 0, it indicates that the memory block is a free block; if the value is 1, it means that the memory block has been allocated, not a free block.
P_next and p_prev are used to organize each memory block in the form of a doubly-linked list so that the next or previous memory block of the current memory block can be found conveniently. For example, when releasing a memory block, it is necessary to view adjacent memory blocks. (The previous memory block and the next memory block) are free blocks to decide whether to merge them into one free block.
In addition, in order to speed up the search for free blocks when allocating memory blocks, there are two additional pointers in the message for organizing all free blocks into a single linked list. In this way, when allocating a memory block, it is only necessary to find the free block satisfying the demand in the free linked list, and it is not necessary to traverse all the memory blocks in turn. The schematic diagram is shown in Figure 9.13.
Figure 9.13 Space block linked list
For the user, the memory space obtained by the user is the user data space (a pointer that directly points to the user data space). The information stored in the memory block header is invisible to the user, and the user should not attempt to access or modify the information. .
The need for users' attention is that since the relevant information of the memory block needs to occupy a certain amount of memory space (in a 32-bit system, usually 24 bytes), the actual size occupied by each memory block is larger than the memory space requested by the user. For example, the user requests a 100-byte memory space. In practice, a 124-byte memory block is allocated to store the memory block information. Based on this, the actual memory space available to the user is slightly less than the total space of the heap.
9.1.2 Heap Manager Interface
AWorks provides a heap manager, which can be used by users through related interfaces. The prototype of the correlation function is shown in Table 9.1.
Table 9.1 Heap Manager Interface (aw_memheap.h)
1. Defining a heap manager instance
Before using the heap manager, you must first use the aw_memheap_t type to define the heap manager instance. This type is defined in aw_memheap.h. Users of the specific type definition do not need to be concerned. You only need to use this type to define the heap manager instance. That is:
The address can be passed as an argument to the memheap parameter in the initialization interface.
In AWorks, a heap manager is used to manage a contiguous section of memory space. In a system, multiple heap managers can be used to manage multiple sections of contiguous memory space. This requires the definition of multiple heap manager instances, such as:
In this way, each application can define its own heap manager to manage memory allocation and release in the application, so that the memory usage between applications does not affect each other.
If all applications use a heap, the memory allocation among applications will affect each other. A memory leak occurs in an application. Over time, it is highly likely that a heap space will be completely leaked. This will affect all applications. . At the same time, all applications share a heap, which also results in more frequent allocation and release in one heap, more inconsistent allocated memory blocks, and more memory fragmentation.
2. Initialize the heap manager
After you define a heap manager instance, you must use this interface to initialize it before you can use it to specify the memory space it manages. Its function prototype is:
Among them, memheap is a pointer to the heap manager instance; name is the name of the heap manager, the name is a string, only used as an identifier; start_addr specifies the first address of the memory space it manages; size specifies the memory space it manages Size (bytes).
The return value of the function is the standard error number. When AW_OK is returned, the initialization is successful; otherwise, the initialization fails.
The core of the initialization function is to specify the memory space it manages. Usually, this continuous memory space can be obtained by defining a global array variable whose size is related to the actual application and should be determined according to the actual situation. For example, if you use the heap manager to manage 1 KB of memory, the sample program for initializing the heap manager is shown in Listing 9.1.
Listing 9.1 Initializing the Heap Manager Sample Program
In the program, an array of 1024 bytes is defined for allocating a 1 KB memory space for management using the heap manager.
3. Allocate memory
After the heap manager is initialized, the interface can be used to allocate a memory block of a specified size to the user. The size of the memory block can be any size that meets the resource requirements. The function prototype for allocating memory blocks is:
Among them, heap is a pointer to the heap manager; size is the memory block size. The return value is the first address of the allocated memory block. In particular, if the return value is NULL, the allocation fails.
When allocating memory blocks, because the heap manager does not know what type of data the allocated memory block uses to store, the return value is a generic typeless pointer, that is, the void * type, which indicates that it points to a certain type. The determined address. Obviously, what type of data is allocated by the allocated memory block is determined by the user. Therefore, when the user uses this memory space, it must convert it to a pointer of the actual data type.
For example, to allocate memory blocks for storing 100 unsigned 8-bit data, the sample program is detailed in Listing 9.2.
Listing 9.2 Sample Program for Allocating Memory Blocks
In the program, the return value of aw_memheap_alloc() is cast to a pointer to the uint8_t data type. Note that in memory allocated using aw_memheap_alloc(), the initial value of the data is random, not necessarily zero. Therefore, if the memory pointed to by ptr is not assigned, its value may be arbitrary.
4. Resizing memory
Sometimes, you need to dynamically adjust the previously allocated memory block size. If you initially allocate a smaller memory block, but the memory is insufficient with the increase of data, you can use this function to readjust the previously allocated memory block. size. Its function prototype is:
Among them, heap is a pointer to the heap manager; ptr is the first address of the memory block allocated using the aw_memheap_alloc() function, that is, the return value of the aw_memheap_alloc() function is called; new_size is the adjusted memory block size. The return value is the first address of the resized memory space. In particular, if the return value is NULL, the resize fails.
The new size specified by newsize can be larger (enlarged) than the original memory block, or it can be smaller (smaller) than the original memory block. If it is an enlarged memory block, the memory of the new extended part will not be initialized, the value is random, but the data in the original memory block remains unchanged. If it is to shrink the memory block, the memory that exceeds the new_size portion will be released, the data in it will be discarded, and the remaining data will remain unchanged. In particular, if the value of newsize is 0, it is equivalent to no longer using the memory block pointed to by ptr. In this case, the entire memory block will be directly released. In this case, the return value is also NULL.
The return value of the function may be different from the value of ptr, that is, the system may reselect a suitable memory block to meet the need for resizing. At this time, the first address of the memory will change. For example, when the new size is larger than the original space, the system first determines whether there is enough continuous space after the original memory block to meet the requirements for expanding the memory. If there is, the memory space is directly expanded and the memory address is unchanged. Return the first address of the original memory block, that is, the value of ptr; if not, allocate a new memory block according to newsize, copy the original data to the newly allocated memory block, and then automatically release the original memory. Block, the original memory block will no longer be available, at this time, the return value will be the first address of the memory block to be re-allocated, and there will be no direct relationship with ptr.
For example, first use aw_memheap_alloc() to allocate a 100-byte memory block and then resize it to a 200-byte memory block. The sample program is detailed in Listing 9.3.
Listing 9.3 Resize memory
It is worth noting that the original memory space is still valid after failing to resize the memory space. Therefore, if you use the call form of line 9 of Listing 9.3, if the memory expansion fails, the return value is NULL, which will cause the value of ptr to be set to NULL. At this point, although the original memory space is still valid, because the pointer information to the memory space is lost, the user can no longer access the corresponding memory space, nor can he release the corresponding memory space, causing a memory leak.
In actual use, in order to avoid the adjustment of the memory size, if the value of the original ptr is overwritten with NULL, a new pointer should be used to store the return value of the function. See Listing 9.4 for details.
Listing 9.4 Resize memory (use new pointer to save function return value)
At this point, even if the expansion of the memory space fails, the pointer ptr to the original memory space is still valid, and the originally allocated memory space can still be used to avoid the memory leak. When the expansion of the memory space is successful, the value of ptr is directly re-assigned to new_ptr to point to the memory space after the expansion is completed.
5. Release memory
When the user no longer uses the requested memory block, it must be released, otherwise it will cause a memory leak. The function prototype for releasing memory is:
Among them, ptr is the first address of the memory block allocated using the aw_memheap_alloc() or aw_memheap_realloc() function, which is the return value of calling these functions. Note that ptr can only be the return value of the above several functions, it can not be other address values, for example, can not use the first address of the array as a function parameter to release the memory space occupied by the static array. Passing in the wrong address value is very likely to cause the system to crash.
When aw_memheap_free() is used to free a block of memory, the corresponding block of memory becomes invalid and the user cannot continue to use it. The sample program for freeing memory blocks is shown in Listing 9.5.
Listing 9.5 Sample Program for Freeing Memory Blocks
In order to avoid releasing the memory block and then continue to use it, you can develop a good habit. When the memory is released, the corresponding ptr pointer is set to NULL.
9.1.3 System Heap Management
In AWorks, the entire memory space is first used to satisfy the storage of some data that is known to occupy memory space, such as: global variables, static variables, stack space, program code, or constants.
Global variables and static variables are better understood, and the amount of memory they occupy can be determined by the type of data. It should be noted that, when introducing the heap manager, the definition of a memory space to be managed also uses a static array. See line 6 of Listing 9.1 for details:
This is also a static variable whose memory size is known.
For stack space, in AWorks, the stack space is statically allocated, similar to a static array, and the amount of memory it occupies is determined by the user and is also known.
Under normal circumstances, program code and constants are stored in read-only memory such as ROM or FLASH and are not stored in memory. However, in some platforms, program code and constants may also be stored in memory for efficiency or chip architecture considerations. For example, in the i.MX28x platform, program code and constants are also stored in DDR memory. The size of memory space occupied by program code and constants can be determined after compilation, and the occupied memory space size is also known.
After the storage of these data is satisfied, all the remaining memory space is used as the system heap space, which is convenient for the user to use dynamically during the running of the program.
In order to facilitate the use of the user, you need to use some appropriate method to manage the system heap space. In AWorks, it is managed by default using the previously described heap manager. For the scalability of the system, AWorks does not limit the heap space that must be managed based on the heap manager provided by AWorks. If users have more suitable management methods for special applications, they can also use their own methods to manage the system in specific environments. Heap space. In order to maintain the unity of the application, AWorks defined a set of dynamic memory management common interface, which facilitates the user to use the system heap space without having to care about the specific management method. The relevant function prototype is shown in Table 9.2.
Table 9.2 Dynamic Memory Management Interface (aw_mem.h)
According to the previous introduction, before using the heap manager, you need to define the heap manager instance, and then initialize the instance to specify the memory space it manages. After the initialization is complete, the user can request memory from it. If you use the heap manager to manage the system heap space (the default), then the interfaces in Table 9.2 can all be implemented based on the heap manager interface. At this point, a default heap manager instance will be defined in the system, and its initialization operation will be completed automatically when the system is started, and the memory space it manages will be designated as the system heap space. In this way, after the system is started, the user can apply for a memory block directly to the default heap manager in the system. For example, aw_mem_alloc() is used to allocate a block of memory that is directly based on the heap manager implementation. For an example program, see Listing 9.6.
Listing 9.6 Aw_mem_alloc() Function Example
Among them, __g_system_heap is a system-defined heap manager instance that has been initialized at system startup. Listing 9.6 is just a simple example of how the aw_mem_alloc() function is implemented. In practice, the user can use the most appropriate method to manage the system heap space according to the specific situation, and implement the universal memory management universal interface defined by AWorks.
The following describes in detail the meaning of each interface and how to use it.
Allocate memory
Aw_mem_alloc() is used to allocate a block of a specified size from the system heap. The usage is the same as malloc() in the C standard library. Its function prototype is:
The parameter size specifies the size of the memory space in bytes. The return value is a pointer of type void *, which points to the first address of the allocated memory block. In particular, if the return value is NULL, the allocation fails.
For example, to apply for a storage space to store an int type of data, the sample program is detailed in Listing 9.7.
Listing 9.7 Application Memory Sample Program
In the program, the return value of aw_mem_alloc() is cast to a pointer to an int data type. Note that in memory allocated using aw_mem_alloc(), the initial value of the data is random and not necessarily zero. Therefore, if the memory pointed to by ptr is not assigned, its value will be arbitrary.
2. Allocate multiple memory blocks of a specified size
In addition to using aw_mem_alloc() to directly allocate a memory block of a specified size, you can use aw_mem_calloc() to allocate multiple contiguous blocks of memory. The usage is the same as calloc() in the C standard library. Its function prototype is:
This function is used to allocate nelem memory size of the memory block, the total size of the memory space allocated: nelem × size, in fact, is equivalent to allocate a large memory block size nelem × size, the return value is also allocated memory block First address. Unlike aw_mem_alloc(), the memory block allocated by this function is initialized to 0. For example, to allocate memory for storing 10 int data, the sample program is detailed in Listing 9.8.
Listing 9.8 allocates 10 memory blocks for storing int data
Since the allocated memory space is initialized to 0, even if the memory pointed to by ptr is not assigned, its value is determined to be 0.
3. Allocate memory blocks with certain alignment requirements
Occasionally, a user-requested memory block may be used to store data with special alignment requirements. The first address required to allocate a block of memory must be aligned to the specified number of bytes. At this point, you can use aw_mem_align() to allocate a block of memory that satisfies the specified alignment requirement. Its function prototype is:
Among them, size is the allocated memory block size, align represents the alignment requirement, and its value is an integer power of 2, for example: 2, 4, 8, 16, 32, 64, etc. The return value is also the first address of the allocated memory block whose value satisfies the alignment requirement and is an integer multiple of align. If the value of align is 16, then in accordance with the alignment of 16 bytes, the first address of the allocated memory block will be an integer multiple of 16, and the lower 4 bits of the address are all 0. For a sample program, see Listing 9.9.
Listing 9.9 Sample Program for Allocating Blocks with Alignment Requirements
In the program, the assigned address is printed out through aw_kprintf() to see the specific value of the address. The actual operation can be found, and the address value is 16 bytes aligned. Note that this function is the same as the memory block allocated by aw_mem_alloc(), where the initial value of the data is random and not necessarily zero.
In the heap manager, there is no similar allocation of memory block interface to meet certain alignment requirements, only ordinary memory allocation block interface: aw_memheap_alloc (). The allocated memory block may be aligned or misaligned. In order to make the memory block returned to the user to meet the alignment requirements, when using aw_memheap_alloc() to allocate memory blocks, the alignment-1 byte space can be allocated. At this time, even if the first address of the obtained memory block does not meet the alignment requirement, You can return the first aligned address from the beginning of the memory block to the user to meet the user's alignment requirements.
For example, to allocate 200 bytes of memory and require 8-byte alignment, first allocate a 207-byte (200 + 8 - 1) memory block using aw_memheap_alloc(), assuming a memory block address range of: 3 ~ 209, see Figure 9.14(a) for a diagram. Since the first address 3 is not an integer multiple of 8, it is not aligned by 8 bytes. In this case, the first aligned address is returned directly to the user, ie: 8. Since the user needs a 200-byte memory block, the address range of the memory block used by the user is: 8 ~ 207. Obviously, it is within the memory block address range actually obtained by using aw_memheap_alloc(). The memory blocks obtained are completely valid, as shown in Figure 9.14(b).
Figure 9.14 Memory Alignment Processing - Multi-allocation of align-1 bytes of space
Why allocate more space for align - 1 byte? When the actual memory block obtained does not meet the alignment requirement, it indicates that the first address of the memory block is not an integer multiple of alignment, that is, the result of the remainder of the alignment (the C language operator is not 0) is assumed to be a remainder of the alignment. The result is N (N ≥ 1). As long as the first address is aligned-N, the resulting address value is an integer multiple of align. The value is also the first aligned address starting from the first address. Since the first address is not aligned, there must be: N ≥ 1, so: align - N ≤ align - 1, that is, the offset of the first aligned address in the sequence relative to the starting address does not exceed align - 1. Based on this, as long as the allocation of memory block allocates 1 to 1 bytes of space, then you can return an aligned address to the user, while still meeting the user's requested memory block capacity requirements.
If there is no more allocation of align-1 bytes of memory, for example, only aw_memheap_alloc() is used to allocate 200 bytes of memory, the resulting memory block addresses range from 3 to 203, as shown in Figure 9.15(a). At this point, if you also return the first aligned address to the user in the same order, that is: 8. Since the user needs a 200-byte memory block, the address range of the memory block used by the user is: 8 to 208. Obviously, the space between 204 and 208 is not the allocated effective memory, making The user got an illegal memory space. Once accessed, it may cause application errors. The schematic diagram is shown in Figure 9.15 (b).
Figure 9.15 Memory Alignment Processing - No More Allocation of -1 Byte Space
In practice, since the value of align-1 is often a rather special odd value, such as: 3, 7, 15, 31, etc., it is often easy to allocate the first address of the memory block so that there are many unaligned addresses. Therefore, the memory space of align bytes is often directly allocated.
At the same time, for efficiency reasons, in AWorks, each allocated memory is always aligned according to the default number of CPU natural alignment bytes. For example, in a 32-bit system, all memory allocated by default is aligned by 4 bytes. Here, the implementation of the aw_mem_alloc() function can be updated as shown in Listing 9.10.
Listing 9.10 Memory allocated by the aw_mem_alloc() function is aligned to 4 bytes
4. Resizing memory
Sometimes, the size of the allocated memory block needs to be dynamically adjusted. If a small memory block is allocated at the beginning, but the memory is insufficient with the increase of data, this function can be used to readjust the previously allocated memory block size. . Its function prototype is:
Among them, ptr is the first address of the memory block allocated using the aw_mem_alloc(), aw_mem_calloc() or aw_mem_align() function, which is the return value of calling these functions. New_size is the adjusted size. The return value is the first address of the resized memory block. In particular, if the resize fails, the return value is NULL.
For example, first use aw_mem_alloc() to allocate a memory block that stores 1 int data, and then resize the memory block so that it can store 2 int data. The sample program is detailed in Listing 9.11.
Listing 9.11 Sample Memory Allocation Program (Resize Memory)
5. Release memory
Previously explained four methods of allocating memory blocks. No matter which way the dynamically allocated memory blocks are used, they must be released. Otherwise, memory leaks will occur. The function prototype for freeing memory blocks is:
Among them, ptr is the first address of the memory block allocated using the aw_mem_alloc(), aw_mem_calloc(), aw_mem_align(), or aw_mem_realloc() functions, that is, the return value of calling these functions.
When aw_mem_free() is used to free a memory block, the corresponding address space becomes invalid and the user cannot continue to use it. The sample program for freeing memory blocks is shown in Listing 9.12.
Listing 9.12 Sample program to free memory blocks
9.2 memory pool
The heap manager is extremely flexible and can allocate any size memory block, which is very convenient. However, it also has obvious disadvantages: First, the distribution efficiency is not high. In each allocation, all free memory blocks must be sequentially searched until they find a free memory block that meets the demand. Second, memory fragments of different sizes are easily generated. .
In order to improve the efficiency of memory allocation and to avoid memory fragmentation, AWorks provides another memory management method: Memory Pool. It dispenses with the advantage that a chunk of memory of any size can be allocated in the heap manager, setting the size of the allocated memory block to a fixed value.
Since the size of the allocated memory block is a fixed value each time, there is no limitation of the space size. Therefore, each time the user applies for a memory block, the first free memory block can be allocated without any searching process. Similarly, the memory is released. When a memory block, it only needs to be marked as free, without any additional merge operation, which greatly improves the efficiency of memory allocation and release.
At the same time, since each memory block requested and released is the same size, as long as there are free blocks, it can be allocated successfully. It is impossible for some free memory blocks to be unusable due to being divided by an allocated memory block. In this case, any free block can be used without limitation, no memory fragmentation exists, and memory fragmentation is completely avoided.
However, fixing the memory block size limits its flexibility of use and may result in unnecessary memory space waste. For example, the user only needs a small memory space, but if each memory in the memory pool is large, This will cause a large amount of memory to be allocated during memory allocation, resulting in wasted memory.这就è¦æ±‚在定义内å˜æ± 时,应尽å¯èƒ½å°†å†…å˜æ± ä¸å†…å˜å—的大å°å®šä¹‰ä¸ºä¸€ä¸ªåˆç†çš„值,é¿å…过多的内å˜æµªè´¹ã€‚
系统ä¸å¯ä»¥å˜åœ¨å¤šä¸ªå†…å˜æ± ,æ¯ä¸ªå†…å˜æ± 包å«å›ºå®šä¸ªæ•°å’Œå¤§å°çš„内å˜å—。基于æ¤ï¼Œåœ¨å®žé™…应用ä¸ï¼Œä¸ºäº†æ»¡è¶³ä¸åŒå¤§å°çš„内å˜å—需求,å¯ä»¥å®šä¹‰å¤šç§å°ºå¯¸ï¼ˆå†…å˜æ± ä¸å†…å˜å—的大å°ï¼‰çš„内å˜æ± (比如:å°ã€ä¸ã€å¤§ä¸‰ç§ï¼‰ï¼Œç„¶åŽåœ¨å®žé™…应用ä¸æ ¹æ®å®žé™…用é‡é€‰æ‹©ä»Žåˆé€‚的内å˜æ± ä¸åˆ†é…内å˜å—ï¼Œè¿™æ ·å¯ä»¥åœ¨ä¸€å®šç¨‹åº¦ä¸Šå‡å°‘内å˜çš„浪费。
9.2.1 内å˜æ± 原ç†æ¦‚è¿°
内å˜æ± 用于管ç†ä¸€æ®µè¿žç»çš„内å˜ç©ºé—´ï¼Œç”±äºŽå„个内å˜å—的大å°å›ºå®šï¼Œå› æ¤ï¼Œé¦–先将内å˜ç©ºé—´åˆ†ä¸ºè‹¥å¹²ä¸ªå¤§å°ç›¸åŒçš„内å˜å—,例如,管ç†1024å—节的内å˜ç©ºé—´ï¼Œæ¯å—大å°ä¸º128å—节。则共计å¯ä»¥åˆ†ä¸º8个内å˜å—。åˆå§‹æ—¶ï¼Œæ‰€æœ‰å†…å˜å—å‡ä¸ºç©ºé—²å—,示æ„图详è§å›¾9.16。
图9.16 åˆå§‹çŠ¶æ€â€”—8个空闲å—
在AWorksä¸ï¼Œä¸ºä¾¿äºŽç®¡ç†ï¼Œå°†å„个空闲内å˜å—使用å•å‘链表的形å¼ç»„织起æ¥ï¼Œç¤ºæ„图详è§å›¾9.17。
图9.17 以å•å‘链表的形å¼ç»„织å„个空闲å—
这就è¦æ±‚一个空闲å—能够å˜æ”¾ä¸€ä¸ªp_next指针,以便组织链表。在32ä½ç³»ç»Ÿä¸ï¼ŒæŒ‡é’ˆçš„大å°ä¸º4个å—èŠ‚ï¼Œå› æ¤ï¼Œè¦æ±‚å„个空闲å—的大å°ä¸èƒ½ä½ŽäºŽ4个å—节。æ¤å¤–,出于对é½è€ƒè™‘,å„个空闲å—的大å°å¿…须为自然对é½å—节数的æ£æ•´æ•°å€ã€‚例如,在32ä½ç³»ç»Ÿä¸ï¼Œå—大å°åº”该为4å—节的整数å€ï¼Œæ¯”如:4ã€8ã€12ã€6……而ä¸èƒ½ä¸º5ã€7ã€9ã€13ç‰ã€‚
基于æ¤ï¼Œå½“需è¦åˆ†é…一个内å˜å—时,åªéœ€ä»Žé“¾è¡¨ä¸çš„å–出第一个空闲å—å³å¯ã€‚例如,需è¦åœ¨å›¾9.17的基础上分é…一个内å˜å—,å¯ä»¥ç›´æŽ¥ä»Žé“¾è¡¨ä¸å–出第一个空闲å—,示æ„图详è§å›¾9.18。
图9.18 从链表ä¸å–出一个内å˜å—
æ¤æ—¶ï¼Œç©ºé—²å—链表ä¸ï¼Œå°†åªå‰©ä¸‹7个空闲å—,示æ„图详è§å›¾9.19。
图9.19 剩余7个空闲å—
值得注æ„的是,虽然在空闲å—链表ä¸ï¼Œå„个内å˜å—ä¸å˜æ”¾äº†ä¸€ä¸ªp_next指针,å 用了一定的内å˜ç©ºé—´ï¼Œä½†æ˜¯ï¼Œå½“该内å˜å—从空闲链表ä¸å–出,分é…给用户使用时,已分é…的内å˜å—并ä¸éœ€è¦ç»„织为一个链表,p_next的值也就没有任何æ„ä¹‰äº†ï¼Œå› æ¤ï¼Œç”¨æˆ·å¯ä»¥ä½¿ç”¨å†…å˜å—ä¸æ‰€æœ‰çš„内å˜ï¼Œä¸å˜åœ¨ç”¨æˆ·ä¸å¯è®¿é—®çš„区域,ä¸ä¼šé€ æˆé¢å¤–的空间浪费。
è€Œåœ¨å †ç®¡ç†å™¨ä¸ï¼Œæ— 论是空闲å—还是已分é…的内å˜å—,头部å˜å‚¨çš„相关信æ¯éƒ½å¿…é¡»ä¿æŒæœ‰æ•ˆï¼Œå…¶å 用的内å˜ç©ºé—´ç”¨æˆ·æ˜¯ä¸èƒ½ä½¿ç”¨çš„,对于用户æ¥è®²ï¼Œè¿™ç›¸å½“äºŽé€ æˆäº†ä¸€å®šçš„内å˜ç©ºé—´æµªè´¹ã€‚
当用户ä¸å†ä½¿ç”¨ä¸€ä¸ªå†…å˜å—时,需è¦é‡Šæ”¾ç›¸åº”的内å˜å—,释放时,直接将内å˜å—é‡æ–°åŠ 入空闲å—链表å³å¯ã€‚示æ„图详è§å›¾9.20。
图9.20 释放一个内å˜å—
释放åŽï¼Œç©ºé—²é“¾è¡¨ä¸å°†æ–°å¢žä¸€ä¸ªå†…å˜å—,示æ„图详è§å›¾9.21。
图9.21 释放åŽï¼Œæ–°å¢žä¸€ä¸ªå†…å˜å—
ç”±æ¤å¯è§ï¼Œæ•´ä¸ªå†…å˜æ± 的分é…和释放æ“作都éžå¸¸ç®€å•ã€‚分é…时,从空闲链表ä¸å–出一个内å˜å—,释放时,将内å˜å—é‡æ–°åŠ 入空闲链表ä¸ã€‚
9.2.2 内å˜æ± 接å£
AWorksæ供了内å˜æ± 软件库,用户通过相关接å£ä½¿ç”¨å³å¯ã€‚相关函数的原型详è§è¡¨9.3。
表9.3 内å˜æ± 接å£ï¼ˆaw_pool.h)
1. 定义内å˜æ± 实例
在使用内å˜æ± å‰ï¼Œå¿…须先使用aw_pool_t类型定义内å˜æ± 实例,该类型在aw_pool.hä¸å®šä¹‰ï¼Œå…·ä½“ç±»åž‹çš„å®šä¹‰ç”¨æˆ·æ— éœ€å…³å¿ƒï¼Œä»…éœ€ä½¿ç”¨è¯¥ç±»åž‹å®šä¹‰å†…å˜æ± 实例å³å¯ï¼Œå³ï¼š
其地å€å³å¯ä½œä¸ºåˆå§‹åŒ–接å£ä¸p_poolå‚数的实å‚ä¼ é€’ã€‚
一个内å˜æ± å¯ä»¥ç®¡ç†ä¸€æ®µè¿žç»çš„内å˜ç©ºé—´ï¼Œåœ¨AWorksä¸ï¼Œå¯ä»¥ä½¿ç”¨å¤šä¸ªå†…å˜æ± ,以分别管ç†å¤šæ®µè¿žç»çš„内å˜ç©ºé—´ã€‚æ¤æ—¶ï¼Œå°±éœ€è¦å®šä¹‰å¤šä¸ªå†…å˜æ± 实例,例如:
为了满足å„ç§å¤§å°çš„内å˜å—需求,å¯ä»¥å®šä¹‰å¤šä¸ªå…·æœ‰ä¸åŒå†…å˜å—大å°çš„内å˜æ± 。例如:定义å°ã€ä¸ã€å¤§ä¸‰ç§å°ºå¯¸çš„内å˜æ± ,它们对应的内å˜å—大å°åˆ†åˆ«ä¸º8ã€64ã€128ã€‚ç”¨æˆ·æ ¹æ®å®žé™…用é‡é€‰æ‹©ä»Žåˆé€‚的内å˜æ± ä¸åˆ†é…内å˜å—,以在一定程度上å‡å°‘内å˜çš„浪费。
2. åˆå§‹åŒ–内å˜æ±
定义内å˜æ± 实例åŽï¼Œå¿…须使用该接å£åˆå§‹åŒ–åŽæ‰èƒ½ä½¿ç”¨ï¼Œä»¥æŒ‡å®šå†…å˜æ± 管ç†çš„内å˜ç©ºé—´ï¼Œä»¥åŠå†…å˜æ± ä¸å„个内å˜å—的大å°ã€‚其函数原型为:
å…¶ä¸ï¼Œp_pool指å‘å¾…åˆå§‹åŒ–的内å˜æ± ,å³ä½¿ç”¨aw_pool_t类型定义的内å˜æ± 实例;p_pool_mem为该内å˜æ± 管ç†çš„实际内å˜ç©ºé—´é¦–地å€ï¼›pool_size指定整个内å˜ç©ºé—´çš„大å°ï¼›item_size指定内å˜æ± ä¸æ¯ä¸ªå†…å˜å—的大å°ã€‚
函数的返回值为内å˜æ± ID,其类型aw_pool_id_tï¼Œè¯¥ç±»åž‹çš„å…·ä½“å®šä¹‰ç”¨æˆ·æ— éœ€å…³å¿ƒï¼Œè¯¥IDå¯ä½œä¸ºå…¶å®ƒåŠŸèƒ½æŽ¥å£çš„å‚数,用以表示需è¦æ“作的内å˜æ± 。特别地,若返回ID的值为NULL,表明åˆå§‹åŒ–失败。
åˆå§‹åŒ–时,系统会将pool_size大å°çš„内å˜ç©ºé—´ï¼Œåˆ†ä¸ºå¤šä¸ªå¤§å°ä¸ºitem_size的内å˜å—进行管ç†ã€‚例如,使用内å˜æ± 管ç†1KB的内å˜ç©ºé—´ï¼Œæ¯ä¸ªå†…å˜å—的大å°ä¸º16å—节,åˆå§‹åŒ–范例程åºè¯¦è§ç¨‹åºæ¸…å•9.13。
程åºæ¸…å•9.13 åˆå§‹åŒ–内å˜æ±
程åºä¸ï¼Œå°†1024å—节的空间分æˆäº†å¤§å°ä¸º16å—节的内å˜å—进行管ç†ã€‚注æ„,出于效率考虑,å—大å°å¹¶ä¸èƒ½æ˜¯ä»»æ„值,åªèƒ½ä¸ºè‡ªç„¶å¯¹é½å—节数的æ£æ•´æ•°å€ã€‚例如,在32ä½ç³»ç»Ÿä¸ï¼Œå—大å°åº”该为4å—节的整数å€ï¼Œè‹¥ä¸æ»¡è¶³è¯¥æ¡ä»¶ï¼Œåˆå§‹åŒ–时,将会自动å‘上修æ£ä¸º4å—节的整数å€ï¼Œä¾‹å¦‚,å—大å°çš„值设置为5,将被自动修æ£ä¸º8。用户å¯ä»¥é€šè¿‡aw_pool_item_size ()函数获得实际的内å˜å—大å°ã€‚
3. 获å–内å˜æ± ä¸å®žé™…çš„å—大å°
å‰é¢æ到,åˆå§‹åŒ–时,为了ä¿è¯å†…å˜æ± 的管ç†æ•ˆçŽ‡ï¼Œå¯èƒ½ä¼šå¯¹ç”¨æˆ·ä¼ 入的å—大å°è¿›è¡Œé€‚当的修æ£ï¼Œç”¨æˆ·å¯ä»¥é€šè¿‡è¯¥å‡½æ•°èŽ·å–当å‰å†…å˜æ± ä¸å®žé™…çš„å—大å°ã€‚其函数原型为:
å…¶ä¸ï¼Œpool_id为åˆå§‹åŒ–函数返回的内å˜æ± ID,其用于指定è¦èŽ·å–ä¿¡æ¯çš„内å˜æ± 。返回值å³ä¸ºå†…å˜æ± ä¸å®žé™…çš„å—大å°ã€‚
例如,åˆå§‹åŒ–时,将内å˜æ± çš„å—大å°è®¾å®šä¸º5,然åŽé€šè¿‡è¯¥å‡½æ•°èŽ·å–内å˜æ± ä¸å®žé™…çš„å—大å°ã€‚范例程åºè¯¦è§ç¨‹åºæ¸…å•9.14。
程åºæ¸…å•9.14 获å–内å˜æ± ä¸å®žé™…çš„å—大å°
è¿è¡Œç¨‹åºå¯ä»¥å‘现,实际内å˜å—的大å°ä¸º8。
实际应用ä¸ï¼Œä¸ºäº†æ»¡è¶³ä¸åŒå®¹é‡å†…å˜ç”³è¯·çš„需求,å¯ä»¥å®šä¹‰å¤šä¸ªå†…å˜æ± ,æ¯ä¸ªå†…å˜æ± 定义ä¸åŒçš„å—大å°ã€‚如定义3ç§å—大å°å°ºå¯¸çš„内å˜æ± ,分别为8å—节(å°ï¼‰ã€64å—节(ä¸ï¼‰ã€128å—节(大)。范例程åºè¯¦è§ç¨‹åºæ¸…å•9.15。
程åºæ¸…å•9.15 定义多ç§ä¸åŒå—大å°çš„内å˜æ±
程åºä¸ï¼Œå°†ä¸‰ç§ç±»åž‹å†…å˜æ± 的总容é‡åˆ†åˆ«å®šä¹‰ä¸ºäº†512ã€1024ã€2048。实际ä¸ï¼Œåº”æ ¹æ®æƒ…况定义,例如,å°åž‹å†…å˜å—需求é‡å¾ˆå¤§ï¼Œåˆ™åº”该增大对应内å˜æ± 的总容é‡ã€‚
4. 获å–内å˜å—
内å˜æ± åˆå§‹åŒ–完毕åŽï¼Œç”¨æˆ·å¯ä»¥ä»Žå†…å˜æ± ä¸èŽ·å–固定大å°å†…å˜å—,其函数原型为:
å…¶ä¸ï¼Œpool_id为åˆå§‹åŒ–函数返回的内å˜æ± ID,其用于指定内å˜æ± ,表示从该内å˜æ± ä¸èŽ·å–内å˜å—。返回值为void *类型的指针,其指å‘获å–内å˜å—的首地å€ï¼Œç‰¹åˆ«åœ°ï¼Œè‹¥è¿”回值为NULL,则表明获å–失败。从内å˜æ± ä¸èŽ·å–一个内å˜å—的范例程åºè¯¦è§ç¨‹åºæ¸…å•9.16。
程åºæ¸…å•9.16 获å–内å˜å—范例程åº
5. 释放内å˜å—
当获å–的内å˜å—使用完毕åŽï¼Œåº”该释放该内å˜å—,将其返还到内å˜æ± ä¸ã€‚其函数原型为:
å…¶ä¸ï¼Œpool_id为åˆå§‹åŒ–函数返回的内å˜æ± ID,其用于指定内å˜æ± ,表示将内å˜å—释放到该内å˜æ± ä¸ã€‚p_item为使用aw_pool_item_get()函数获å–内å˜å—的首地å€ï¼Œå³è°ƒç”¨aw_pool_item_get()函数的返回值,表示è¦é‡Šæ”¾çš„内å˜å—。
返回值为aw_err_tç±»åž‹çš„æ ‡å‡†é”™è¯¯å·ï¼Œè‹¥å€¼ä¸ºAW_OK,表示释放æˆåŠŸï¼Œå¦åˆ™ï¼Œè¡¨ç¤ºé‡Šæ”¾å¤±è´¥ï¼Œé‡Šæ”¾å¤±è´¥å¾€å¾€æ˜¯ç”±äºŽå‚æ•°é”™è¯¯é€ æˆçš„,例如,释放一个ä¸æ˜¯ç”±aw_pool_item_get()函数获å–的内å˜å—。注æ„,内å˜å—从哪个内å˜æ± ä¸èŽ·å–,释放时,就必须释放到相应的内å˜æ± ä¸ï¼Œä¸å¯å°†å†…å˜å—释放到其它ä¸å¯¹åº”的内å˜æ± ä¸ã€‚
当使用aw_pool_item_return()将内å˜å—释放åŽï¼Œç›¸åº”的内å˜ç©ºé—´å°†å˜ä¸ºæ— 效,用户ä¸èƒ½å†ç»§ç»ä½¿ç”¨ã€‚释放内å˜å—的范例程åºè¯¦è§ç¨‹åºæ¸…å•9.17。
程åºæ¸…å•9.17 释放内å˜å—范例程åº
Cable Assemblies /Wiring Harness
Ribbon Flat Cable,Ffc Cable,Rf Coaxial Cable,Electronics Wire
Shenzhen Hongyian Electronics Co., Ltd. , https://www.hongyiancon.com