2.3.3 allocating sub-memory allocation using allocating sub-memory is the ultimate goal. Apache provides the apr_allocator_alloc function for allocating sub-allocated memory. However, in practice, this function calls allocator_alloc. The prototype declaration of the allocator_alloc function is as follows: the parameters of the apr_memnode_t * allocator_alloc (apr_allocator_t * Allocator, apr_size_t size) function are very simple, the size is the size to be allocated. If the allocation is successful, the allocated apr_memnode_t structure is returned. {Apr_memnode_t * node, ** ref; Specify max_index; apr_size_t I, index; size = apr_align (size + apr_memnode_t_size, boundary_size); If (size <min_alloc) size = min_alloc; index = (size> boundary_index)-1; if (index> apr_uint32_max) {return NULL ;} the first thing the function does is adjust the size of the actually allocated space according to the allocation principle we mentioned earlier: If the size is less than 8 K, it is calculated as 8 K; otherwise, it is adjusted to an integer multiple of 4 K. The function also calculates the index size corresponding to the node. Once the index size is obtained, the node linked list is known. Now Apache can find a suitable node for memory allocation. The following three conditions must be taken into account when allocating memory from the Allocation Sub-Account: (1) if the node size to be allocated is
Rule Node", That is, index <= Allocator-> max_index. At this time, the minimum node that can satisfy the allocation is the linked list node corresponding to the index, but the linked list corresponding to the index may be empty at this time, therefore, the function will search along the array until the first available non-empty node is found or until the end of the array. At the same time, the program code also provides another policy and the reason for not using it:
Note: An optimization wocould be to check Allocator-> free [Index] First and if no node is present, directly use Allocator-> free [max_index]. this seems like overkill though and cocould cause memory waste.Another solution is to directly check Allocator-> free [Index]. Once unavailable, use the largest index Allocator-> free [max_index]. however, this policy may cause a waste of memory. Apache adopts the "most appropriate" principle. According to this principle, the first memory is certainly the most suitable. The following italic Code does nothing more: If (index <= Allocator-> max_index) {max_index = Allocator-> max_index;
Ref = & Allocator-> free [Index];
I = index;
While (* ref = NULL & I <max_index ){
Ref ++;
I ++;
}When the loop exits, it means that the traversal is over. At this time, two results may be generated: first, find a non-empty linked list, and then we can go to the linked list for actual memory allocation; second, all linked lists starting from index are empty. So far, when the loop exits, I = max_index. The two cases can be described in the following simplified figure: For the first case, the processing is as follows: if (node = * ref )! = NULL) {If (* ref = node-> next) = NULL & I> = max_index) {do {ref --; max_index --;} while (* ref = NULL & max_index> 0); Allocator-> max_index = max_index;} Allocator-> current_free_index + = node-> index; if (Allocator-> current_free_index> Allocator-> max_free_index) Allocator-> current_free_index = Allocator-> max_free_index; node-> next = NULL; node-> first_avail = (char *) node + apr_memnode_t _ Size; return node;} (2). If the size of the allocated node exceeds the maximum node in the "rule node", the function will consider indexing the 0 linked list. The actual size of the node in the index 0 linked list is marked by the member variable index. When the index 0 linked list is traversed through next, the function compares the required size index with the actual node size node-> index. If index> node-> index, it is obvious that the node cannot meet the allocation requirements. In this case, you must continue to traverse. Once an appropriate node size is found, the function will adjust the node-> first_avail pointer to the actual available free space. In addition, you also need to adjust the current_free_index in the allocation subitem to the new allocated value. (3) If no suitable space can be found in the free [0] linked list for allocation, you can only "create another stove" at this time. What a function can do is to call malloc to allocate space of the actual size, initialize various node variables, and return the code: If (node = malloc (size )) = NULL) return NULL; node-> next = NULL; node-> Index = index; node-> first_avail = (char *) node + apr_memnode_t_size; node-> endp = (char *) node + size. Let's take a look at a typical call in Apache to allocate sub-allocated space. The following code can be found in worker. found in C: apr_allocator_t * Allocator; apr_allocator_create (& Allocator); apr_allocator_max_free_set (Allocator, ap_max_mem_free); apr_pool_re Ate_ex (& ptrans, null, null, Allocator); apr_allocator_owner_set (Allocator, ptrans); I was confused when I followed this code. When an allocation subaccount is initially created, the index linked list in the internal free array is empty. Therefore, when we call node = allocator_alloc (Allocator, min_alloc-apr_memnode_t_size) in apr_pool_create_ex )) = NULL, the memory required cannot come from the nodes in the index linked list, but can only be allocated locally. Once these nodes are allocated, they are used as nodes in the memory pool, but the allocated nodes are not immediately associated with the Free array, that is, they do not assign values to elements in the free array. In this way, if the node and free array are not "mounted", the linked list structure shown in Figure 1 will never be formed. When will they be attached to the free array? In the past, all the mounting processes were performed only when the node was released. 2.3.4 allocate sub-memory for release as described above. When allocating memory, Apache first tries to find the suitable space in the existing linked list. If no suitable memory area exists, apache must allocate and use the actual memory according to the above allocation principles. However, the actual memory block will not be immediately mounted to the linked list. These areas will only be mounted to the memory when they are released. Therefore, from this perspective, the release of the allocated sub-memory does not actually free the memory call, but instead recycle it to the allocated linked list pool. The memory release function provided by Apache is apr_allocator_free. However, this function is only an external interface. In the Function Memory, allocator_free is actually called. The following is a prototype of the allocator_free function: In the static apr_inline void allocator_free (apr_allocator_t * Allocator, apr_memnode_t * node) function, node is the memory node to be released, and is finally returned to the Allocation Sub. {Apr_memnode_t * Next, * freelist = NULL; Comment index, max_index; Comment max_free_index, current_free_index; max_index = Allocator-> max_index; max_free_index = Allocator-> max_free_index; current_free_index = Allocator-> current_free_index; Because node is not only a node, but also a node linked list, if you need to completely release the node in the linked list, therefore, the following loop is the framework structure of the entire release process. Do {next = node-> next; Index = node-> index ;......} While (node = NEXT )! = NULL); For each node, we will adopt different processing policies based on its index size (that is, the memory size): (1) if the node size exceeds the released que value max_free_index, we cannot simply return it to the index linked list, but must completely return it to the operating system. The function saves all such nodes that need to be completely released in the freelist linked list. After all the nodes have been traversed, you only need to release freelist to release all the nodes that must be released, as follows: If (max_free_index! = Apr_allocator_max_free_unlimited & index> current_free_index) {node-> next = freelist; freelist = node;} If max_free_index is apr_allocator_max_free_unlimited, there is no recycling threshold. No memory, no matter how large it is, APR will not return it to the operating system. (2) If index <max_index, it means that the node belongs to the "rule node" range. Therefore, the node can be returned to the corresponding "rule linked list. If the index size of the node to be released is index, the node is attached to the free [Index] linked list. If the current free [Index] is empty, it indicates that the node of this size is the first node, and the index and max_index must be compared at this time. If index> max_index, you must re-update the size of max_index and insert the node into the head of the linked list as the first node. The code can be described as follows: else if (index <max_index) {If (node-> next = Allocator-> free [Index]) = NULL & index> max_index) {max_index = index ;} allocator-> free [Index] = node; current_free_index-= index;} (3). If the node exceeds the range of the "rule node", but does not exceed the range of the recycle node, in this case, we can place it in the header of the index 0 linked list. The Code is as follows: else {node-> next = Allocator-> free [0]; Allocator-> free [0] = node; current_free_index-= index ;} after all nodes are processed, we must adjust each member variable in the Allocation Sub-table, including max_index and current_free_index. Do not forget to release the freelist linked list. Allocator-> max_index = max_index; Allocator-> current_free_index = current_free_index; while (freelist! = NULL) {node = freelist; freelist = node-> next; free (node);} after all the work is done, the release of the entire node is complete. In fact, the memory in the entire memory pool is built through the above constant release. Once a memory pool is built, it can be obtained directly from the memory pool next time. 2.3.5 sub-memory allocation management process based on the above description, we will now look at some of the entire sub-work allocation process. Assume that the following code exists: 1. apr_allocator_t * Allocator; 2. apr_allocator_create (& Allocator); 3. apr_allocator_max_free_set (Allocator, 0); // For simplicity, no recycling is performed. 4. apr_memnode_t * memnode1 = apr_allocator_alloc (Allocator, 3000); 5. apr_allocator_free (memnode1); 6. apr_memnode_t * memnode2 = apr_allocator_alloc (Allocator, 3000); 7. apr_allocator_free (memnode2); 8. apr_memnode_t * memnode3 = apr_allocator_alloc (Allocator, 3000); 9. A Pr_allocator_free (memnode3); when the execution of the first row is complete, the created Allocation Sub-item is as follows. In this figure, no memory block is available for allocation: in the fourth row, the system requires a memory allocation of 2000 bytes, but no space is available for allocation (index> Allocator-> max_index, and allocator-> free [0] = NULL ), therefore, the allocated sub-account directly requests 8 K space from the operating system and removes the size of the structure header. The actual available memory size is 8 k-apr_memnode_t_size. When the fifth row is executed, the memory will be returned to the Allocation Sub-Account and saved in the Index 1 linked list. After the dotted lines in are removed, they are in the status before release, and vice versa. The result is as follows: we will consider the execution results of rows 6 and 7. When you apply for a memory of kb for the allocated sub-account again, the calculated result shows that the memory must be obtained in a linked list with an index of 1. If the index 1 linked list is null, repeat the previous steps;
About the author
Zhang zhongqingAt present, the main research direction is embedded browsers, mobile middleware and large-scale server design. The source code analysis of Apache is currently underway, and the Apache source code Panoramic Analysis is planned to be published in the next book. Apache articles are part of this book. If you are interested in Apache, contact us through flydish at sina.com.cn!
If you think this article is good, click the "recommended this article" link after this article !!