From the code structure, nginx can be divided into three parts: infrastructure, concurrency model and application module. We have introduced the modular design of nginx and analyzed the nginx concurrency model. The concurrency model is mainly implemented by the core class module and event class module. The application module mainly refers to the HTTP and mail modules, which implement the functions of nginx as a Web server, reverse proxy server, and mail proxy server. The application module is more related to relevant domain knowledge, at the end of the analysis.
Infrastructure mainly includes memory, log, String, time, configuration, file, and some basic data structures (containers): array, list, hash table, queue, red-black tree, radix tree.
This article analyzes memory-related data structures and operation APIs. All memory allocations in nginx use the memory pool technology. First, we analyze the memory pool.
Nginx encapsulates the memory allocation function of C. It mainly adds some log points to facilitate the monitoring of all memory allocation operations:
Ngx_alloc uses malloc to allocate memory space;
Ngx_calloc uses malloc to allocate memory space and initializes the allocated space to 0;
Ngx_memalign uses posix_memalign to allocate memory space, which is aligned according to the specified alignment value;
Ngx_free releases memory space.
To facilitate the use of memory by modules and facilitate memory management, nginx implements the memory pool mechanism for memory allocation and management. nginx creates a memory pool in a specific lifecycle, when memory allocation is required, all resources in the memory pool are used. nginx releases resources in the memory pool when appropriate. Developers only need to call the appropriate API to apply for memory, you don't have to worry too much about memory release, which improves development efficiency.
The data structure of the memory pool is defined as follows:
// Memory Pool
Struct ngx_pool_s {
Ngx_pool_data_t D;
Size_t Max; // the maximum size of common memory (small blocks) that can be allocated to the memory pool
Ngx_pool_t * Current; // point to the memory pool currently available for allocation
Ngx_chain_t * chain;
Ngx_pool_large_t * large; // point to a large memory block (large blocks)
Ngx_pool_cleanup_t * cleanup; // point to the memory recycler
Ngx_log_t * log;
};
Typedef struct {
U_char * last; // point to the end of the used memory
U_char * end; // point to the end of the allocated memory
Ngx_pool_t * Next; // points to the next memory pool and organizes the memory pool into a linked list.
Ngx_uint_t failed; // The number of failed times allocated in the current Memory Pool, based on which the current
} Ngx_pool_data_t;
// Large memory block
Struct ngx_pool_large_s {
Ngx_pool_large_t * Next; // points to the next large memory block and organizes the large memory block into a linked list.
Void * alloc; // point to the actually allocated memory block
};
// Memory recycler
Struct ngx_pool_cleanup_s {
Ngx_pool_cleanup_pt handler; // callback function
Void * data; // callback function parameters
Ngx_pool_cleanup_t * Next; // points to the next memory recycler and organizes the memory recycler into a linked list.
};
// Function pointer
Typedef void (* ngx_pool_cleanup_pt) (void * data );
The following are several important APIs:
1. Create a memory pool
Ngx_pool_t *
Ngx_create_pool (size_t size, ngx_log_t * log)
{
Ngx_pool_t * P;
P = ngx_alloc (size, log); // allocate memory of the specified size and format the header as ngx_pool_t
If (P = NULL ){
Return NULL;
}
// Initialize the header Structure
P-> D. Last = (u_char *) P + sizeof (ngx_pool_t); // point to the end of the ngx_pool_t struct
P-> D. End = (u_char *) P + size; // point to the end of the allocated memory
P-> D. Next = NULL;
P-> D. Failed = 0;
Size = size-sizeof (ngx_pool_t );
// The maximum normal memory size that can be allocated from the memory pool cannot exceed ngx_max_alloc_from_pool and the size of the allocated memory minus the size of the header
// If you need to allocate more memory than this size, allocate another large memory block.
P-> max = (size <ngx_max_alloc_from_pool )? Size: ngx_max_alloc_from_pool;
P-> current = P;
P-> chain = NULL;
P-> large = NULL;
P-> cleanup = NULL;
P-> log = log;
Return P;
}
2. Destroy the 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;
// Call the callback function of all memory recyclers to clear the memory before recycling
For (C = pool-> cleanup; 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 all the large memory blocks
For (L = pool-> large; L = L-> next ){
Ngx_log_debug1 (ngx_log_debug_alloc, pool-> log, 0, "Free: % P", L-> alloc );
If (L-> alloc ){
Ngx_free (L-> alloc );
}
}
// Release all normal memory
For (P = pool, n = pool-> D. Next;/* void */; P = n, n = N-> D. Next ){
Ngx_free (P );
If (n = NULL ){
Break;
}
}
}
3. Reset the memory pool
Void
Ngx_reset_pool (ngx_pool_t * Pool)
{
Ngx_pool_t * P;
Ngx_pool_large_t * l;
// Release all large memory blocks
For (L = pool-> large; L = L-> next ){
If (L-> alloc ){
Ngx_free (L-> alloc );
}
}
Pool-> large = NULL;
// Initialize all normal memory (the last of all memory pools points to the back of the ngx_pool_t struct)
For (P = pool; P = p-> D. Next ){
P-> D. Last = (u_char *) P + sizeof (ngx_pool_t );
}
}
4. allocate memory from the memory pool and align
Void *
Ngx_palloc (ngx_pool_t * Pool, size_t size)
{
U_char * m;
Ngx_pool_t * P;
// Common memory size
If (size <= pool-> MAX ){
// From the current available memory pool, search for a memory pool with enough space to be allocated along the memory pool linked list.
P = pool-> current;
Do {
// Alignment memory
M = ngx_align_ptr (p-> D. Last, ngx_alignment );
// Sufficient space
If (size_t) (p-> D. End-m)> = size ){
P-> D. Last = m + size;
Return m;
}
// The current memory pool is insufficient. Find the next memory pool.
P = p-> D. Next;
} While (P );
// The memory pool on the linked list of all memory pools does not have enough space to allocate. Create a new memory pool, add it to the linked list of the memory pool, and allocate memory.
// The size of the created memory pool is the same as that of other memory pools on the memory pool linked list.
Return ngx_palloc_block (pool, size );
}
// Large memory size is allocated and added to the large memory linked list
Return ngx_palloc_large (pool, size );
}
There are also three APIs for memory allocation:
Ngx_pnalloc, allocation, non-alignment
Ngx_pcalloc, allocation, alignment, initialization 0
Ngx_pmemalign
5. release large memory blocks
Ngx_int_t
Ngx_pfree (ngx_pool_t * Pool, void * P)
{
Ngx_pool_large_t * l;
For (L = pool-> large; L = L-> next ){
If (P = L-> alloc ){
Ngx_log_debug1 (ngx_log_debug_alloc, pool-> log, 0,
"Free: % P", L-> alloc );
Ngx_free (L-> alloc );
L-> alloc = NULL;
Return ngx_ OK;
}
}
Return ngx_declined;
}
6. Add a memory recycler, create a memory recycler, and add it to the linked list Of recyclers in the memory pool.
Ngx_pool_cleanup_t *
Ngx_pool_cleanup_add (ngx_pool_t * P, size_t size)
{
Ngx_pool_cleanup_t * C;
C = ngx_palloc (p, sizeof (ngx_pool_cleanup_t ));
If (C = NULL ){
Return NULL;
}
If (size ){
C-> DATA = ngx_palloc (p, size );
If (c-> DATA = NULL ){
Return NULL;
}
} Else {
C-> DATA = NULL;
}
// The callback function must be set separately.
C-> handler = NULL;
C-> next = p-> cleanup;
P-> cleanup = C;
Ngx_log_debug1 (ngx_log_debug_alloc, p-> log, 0, "add cleanup: % P", C );
Return C;
}
From the implementation of the memory pool, we can see that the nginx memory is a small part of the pre-allocated memory. Later, we can increase the allocation as needed and reuse it. In addition, we can also see a feature of nginx implementation: data members are first added to the data container, and then data members are set as needed, such as the process of adding a recycler above, first, add the created recycler to the recycler linked list, and then set the callback function of the recycler as needed. This is often the case when you analyze the basic data structure.