First, the use of nginx memory pool
Nginx uses a memory pool to manage memory. That is to open up a memory pool space, and then get the memory from the memory pool, avoid frequent calls to malloc/free operations. malloc is only called if there is not enough memory pool space
Allocates a new block of memory and joins it into the memory pool.
1, for each client-initiated HTTP request, the Nginx server needs to open up space to receive the client's request line, the request packet header, and the requested package body. All of this is to get space from the memory pool and store the data in these spaces.
2, the Nginx server response to the client request, divided into the response packet header, response package body. This data also needs to get space from the memory pool and store it
3, Nginx as a reverse proxy server, but also to get the space from the memory pool, and storage of the upstream server response package body. And then forwarded to the downstream client.
In short, Nginx's operation of memory is to get space from the memory pool. If there is not enough memory pool space, malloc is called to reopen a block of memory and join it into the memory pool. Of course, the disadvantage of such processing is also obvious, the application of a large amount of memory will inevitably lead to a waste of memory space, but rather than frequent malloc and free, the cost of doing so is very small, which is typical with space-changing time.
Second, the memory pool source code Analysis
1. Create a memory pool
For each client-initiated request, Nginx creates a request structure for it. Each request structure creates a pool of memory. An HTTP request message that is used to receive the client, and an HTTP response message that is stored to the client. The Create memory pool is completed by the Ngx_create_pool function, the parameter size is the block of memory to be created, and the return value is ngx_pool_t memory pool handle
ngx_pool_t * Ngx_create_pool (size_t size, ngx_log_t *log) { ngx_pool_t *p; Use malloc to open a size space p = ngx_memalign (ngx_pool_alignment, size, log); if (p = = null) { return null; } Last point to data space P->d.last = (U_char *) p + sizeof (ngx_pool_t); End points to the ending position of the entire memory block p->d.end = (U_char *) p + size; P->d.next = NULL; p->d.failed = 0; Size represents the amount of data space (the space that really holds the data, not including the size of the NGX_POOL_T header structure) size = size-sizeof (ngx_pool_t); P->max = (Size < Ngx_max_alloc_from_pool)? Size:ngx_max_alloc_from_pool; Current block executes the memory block just created p->current = p; P->chain = NULL; P->large = NULL; P->cleanup = NULL; P->log = log; return p;}
For example, to create a 1K-size memory pool, the memory layout after calling Ngx_create_pool (1024x768, log) is as follows
Figure: Creating a memory pool
The last field points to the first address of the data space, which is used to hold the actual data. When the memory is obtained from this block of memory, the last pointer is also moved, pointing to the first address of the latest available space.
End points to the ending address of this memory block.
The Max field needs to be described, and Max represents the largest data space for small memory blocks. In this example, sizeof (ngx_pool_s) occupies 40 bytes, so applying 1k of memory pool can actually store data in only 984 bytes.
How does the Max field work? If you also need to open up a small memory block, then the maximum space for this small memory block is max size (composed of ngx_pool_data_t and the actual data stored), and this field is also a critical value, if you need to get from the memory pool of space less than Max, then open up a small block of memory can be, Otherwise you need to open up a large memory block.
2. Get the space from the memory pool
When Nginx processes a client request and sends a response, it needs to get space from the memory pool for storing the message. The following 4 functions can be used to get space from the memory pool, the difference is the way in which memory bytes are processed, and whether the memory after acquisition is cleared 0. No other difference.
void *ngx_palloc (ngx_pool_t *pool, size_t size);
void *ngx_pnalloc (ngx_pool_t *pool, size_t size);
void *ngx_pcalloc (ngx_pool_t *pool, size_t size);
void *ngx_pmemalign (ngx_pool_t *pool, size_t size, size_t alignment);
The following is an example of the Ngx_pnalloc function, which analyzes the process of getting a piece of memory from a memory pool.
Gets the memory size from the memory pool, the return value is obtained to the memory address void * NGX_PNALLOC (ngx_pool_t *pool, size_t size) { U_char *m; ngx_pool_t *p; If the data space to be allocated is less than or equal to the actual maximum data space of the memory block, get memory if (size <= Pool->max) { p = pool->current; ) from the small memory block Do { m = p->d.last; The remaining space of the memory block satisfies the size of the data to be obtained, then directly returns the memory address if ((size_t) (p->d.end-m) >= size) { p->d.last = m + size; return m; } If the memory block is more than the space is not sufficient to obtain the size, look for the next memory block p = p->d.next; } while (P);//If all the small memory blocks do not find the appropriate memory, then re-open a small memory block Return Ngx_palloc_block (pool, size); } The data space to be allocated is larger than the actual maximum data space of the memory block, and the memory return Ngx_palloc_large (pool, size) is obtained from the large memory block;}
Again, the Max member represents the maximum value of the data space for each small chunk of memory.
1, the function will first determine whether to get the memory size is less than or equal to max, if it is, then the small memory from the current point to traverse the block, until a memory block to find the remaining space to hold a size space, then return this memory block. If all small blocks of memory have no space to accommodate size, a small chunk of memory is re-opened and added to the ngx_pool_data_t Small memory block list
2. If the amount of memory to get is larger than Max, then each small block of memory has no space to hold the size of the content. You need to use malloc to open up a large memory block and add it to the large large memory block list.
Call Ngx_pnalloc (pool, 200) to get a 200-byte memory layout such as:
Figure: Getting Space
As long as the last pointer changes, it points to the first address of the latest available space. The Max field is a fixed value from the beginning of the Ngx_create_pool creation of the memory pool to the destruction of the memory pool, which does not change in size.
Function: Opens up a new small memory block with a small memory block size of Max members in ngx_pool_t. Note: The newly opened small memory block consists of the ngx_pool_data_t field plus the data space in the ngx_pool_t,//does not include the other field space of the ngx_pool_t//parameter: size indicates how much space needs to be obtained from the newly opened small memory block//return value: Gets the first address of the size space to the static void * Ngx_palloc_block (ngx_pool_t *pool, size_t size) {U_char *m; size_t psize; ngx_pool_t *p, *new, *current; Psize is the actual data space, that is, the Max member in ngx_pool_t psize = (size_t) (Pool->d.end-(U_char *) pool); Use malloc to generate a psize-sized block of memory M = ngx_memalign (Ngx_pool_alignment, psize, Pool->log); if (M = = null) {return null; } new = (ngx_pool_t *) m; End points to the end of the memory block New->d.end = m + psize; New->d.next = NULL; new->d.failed = 0; M The data space of the memory block m + = sizeof (ngx_pool_data_t); m = Ngx_align_ptr (M, ngx_alignment); Last point to the space that can be used New->d.last = m + size; Current = pool->current; for (p = current; p->d.next; p = p->d.next) {//If the failure exceeds the number of times, the current pointer is moved. After acquiring the space, it gets the IF (p->d.failed++ > 4) from the memory block that the new current points to Current = p->d.next; }}//Insert the newly opened small memory block into the tail of the list p->d.next = new; Pool->current = current? Current:new; return m;}
If there is no space in all small memory blocks that satisfies the condition, the ngx_palloc_block is called to reassign a small block of memory. Note that the failed field: By default, the current pointer is pointing to the first block of memory, and when it is necessary to get space from the memory pool, it begins the traversal of the block of memory that points to it and determines whether the remaining space in the block satisfies the condition. When too many of the small memory blocks allocated by the Ngx_palloc_block are called, there is bound to be a memory block with a failed field value greater than 4, which changes the current pointer. After the memory is fetched from the memory pool, it is traversed from the memory block pointed to by the current present pointer until a block of memory satisfies the condition is found. That is, failed points to the memory block will not be used, but it is curious that the memory block can not be exploited, why not immediately free? It is also estimated to reduce the frequency of calls to the system, and then when the memory is destroyed when the space is freed.
In the example, only 784 bytes of space are left in the memory block. If you need to get 800 bytes of space at this point, then because the remaining space of this memory block cannot satisfy 800 bytes, we need to re-open a small memory block, the new memory block size of ngx_pool_s in the Max member, that is, 984 bytes. After opening up a new block of memory, the memory layout is as follows.
Figure: Open up small memory blocks
It is important to note that this newly opened memory block consists of the ngx_pool_data_t field and the data space in the ngx_pool_t, excluding the other field spaces of the ngx_pool_t. That is, only call the Ngx_create_pool function to create the memory pool, there will be a full head structure of the memory pool. Only the memory pool header structure is required to maintain the header information associated with these memory pools.
If you want to get more space from a memory block than the Max member in the pool, it means that there is no space in all small chunks of memory to accommodate the amount of space you want to get. Therefore, you should use malloc to open up a large memory block and add it to the large memory block list. The Ngx_palloc_large function is used to open up a large block of memory. and add to the large memory block linked list
Function: Use malloc mode to open a size space and insert into pool large memory block list large head//<span style= "White-space:pre" ></span> Note: This function is called only if the size is larger than the Max member in the pool, and is used to open up a large memory block space//parameter: size needs to open up the amount of memory//return value: Newly opened memory block space first address static void * Ngx_palloc_large ( ngx_pool_t *pool, size_t size) {void *p; ngx_uint_t N; ngx_pool_large_t *large; Allocate storage space P = ngx_alloc (size, pool->log); if (p = = null) {return null; } n = 0; Traverse all large memory linked lists to find the available nodes for (large = pool->large; large; large = Large->next) {///Find available nodes, then assign memory pointers. Why some large memory blocks of ALLOC will be null, the reader can see ngx_pfree this function to know why if (Large->alloc = = NULL) {Large->alloc = P; return p; }//if it's been found 3 times. No available nodes, jump out of the loop and create a new node. The goal is to improve the search message, avoid a long memory block, if you do not find an idle block of memory, you need to go all the way down//This hard coding method of poor readability, in the development process, the last do not use if (n++ > 3) {Brea K }}//Open a node space large = Ngx_palloc (pool, sizeof (ngx_pool_large_t)); if (large = = NULL) {ngx_free (P); return NULL; }//Insert the newly opened node into the large memory block list of the head large->alloc = p; Large->next = pool->large; Pool->large = large; return p;}
This function has a process to determine whether n is greater than 3, which is not worth advocating, readable line difference. Although in order to improve the efficiency of the search, when there are too many memory blocks to look for, if the first 3 memory blocks do not meet the criteria, then allocate a large block of memory directly. A typical space-time-change algorithm.
Also, why are some large chunks of memory alloc empty? You can view the next Ngx_pfree function, and after calling this function, the space that the large memory block alloc points to is freed. Therefore, the space that alloc points to is empty, which means that the large memory block can be exploited, and the alloc can be used to point to a newly opened space.
In figure: Get space, if you need to get a 1k size space, because 1k is larger than 984 (max member size), you need to open up a large memory block, the memory layout is as follows;
Figure: Opening up large chunks of memory
Below is a memory pool possible memory layout map, a bit more lines, looked dizzy Ah!
Figure: Internal Pool Pool Layout
In this picture, there are two more linked lists. The clain linked list itself is used to store HTTP request messages and response messages. This list space does not need to use malloc to open up, but directly from the small memory block or large memory block to get space.
Cleanup Linked list: Nginx processing HTTP requests, a request requires multiple modules to work together, so each module can register a cleanup callback, in the HTTP end of the request, the module will call the cleanup function, release resources, such as closing files. The cleanup list also does not need to use malloc to open up, but to get space directly from small chunks of memory or large chunks of memory. The two lists know that they are small chunks of memory or large chunks of memory to obtain space, the subsequent blog will be detailed analysis.
3. Freeing the memory pool
The Nginx server destroys the pool pool at the same time when a client request is ended.
Destroy memory pool void Ngx_destroy_pool (ngx_pool_t *pool) {ngx_pool_t *p, *n; ngx_pool_large_t *l; ngx_pool_cleanup_t *c; Clean cleanup list for (c = pool->cleanup; C; c = c->next) {if (C->handler) {Ngx_log_ DEBUG1 (Ngx_log_debug_alloc, Pool->log, 0, "Run Cleanup:%p", c); C->handler (C->data); }}//Release large memory (here only the Alloc point is released using malloc open space, and there is no release ngx_pool_large_s//Because ngx_pool_large_s is the space taken in a small chunk of memory, so simply release the small memory block Yes) for (L = pool->large; l; l = l->next) {ngx_log_debug1 (ngx_log_debug_alloc, Pool->log, 0, "free :%p ", L->alloc); if (l->alloc) {ngx_free (l->alloc); }}//Free small memory block for (p = pool, n = pool->d.next;/* void */; p = n, n = n->d.next) {ngx_free (P); if (n = = NULL) {break; } }}
Internal pool, thread pool, process pool, etc. are all pre-allocated to avoid frequent system calls, pre-allocation of these resources, by the application layer for unified management. Nginx This memory pool design idea is still worth learning. At this point nginx memory pool analysis, follow-up on the nginx source of other data structures, HTTP requests, HTTP response, reverse proxy, upstream for detailed analysis. will also establish a QQ group, convenient for everyone to communicate with each other and progress together!
Nginx Memory Pool