Queue chain list structure
Queue bidirectional loop Linked list implementation file: File: src/core/ngx_queue.h/.c. In the Nginx queue implementation, the essence is a two-way circular linked list with head node, where the nodes in the doubly linked list are no data areas, only two pointers to nodes. Note that the memory allocation of the queue list is not allocated directly from the memory pool, that is, the memory pool management is not done, but we need to manage the memory ourselves, all we can specify it in the memory pool management or directly in the heap management, it is best to use a memory pool for management. The node structure is defined as follows:
/* Queue structure, which is essentially a two-way loop linked list with a head node */typedef struct ngx_queue_s ngx_queue_t;/* Queue Each node structure, only two pointers, and no data area */struct Ngx_queue_ s { ngx_queue_t *prev; ngx_queue_t *next;};
Queue list Operations
Its basic operations are as follows:
/* H is a pointer to the ngx_queue_t of the linked list structure; Initializes a doubly-linked list */ngx_queue_int (h)//h as a pointer to the ngx_queue_t of the linked list container structure; Determines whether the linked list is empty */ngx_queue_empty (h)//h is a linked list The pointer to the ngx_queue_t structure body, X is the pointer to the ngx_queue_t member in the element structure, and the x is inserted into the list head */ngx_queue_insert_head (h, X)//h is the pointer to the ngx_queue_t of the linked list container structure. X is a pointer to the ngx_queue_t member in the body of the inserted element. Insert x into the tail of the linked list */ngx_queue_insert_tail (h, X)//h is a pointer to the ngx_queue_t of the linked list container structure. Returns a pointer to the ngx_queue_t struct body pointer */ngx_queue_head (h)/* H for the first element in the list container h for the ngx_queue_t of the linked list container structure. Returns a pointer to the ngx_queue_t struct body pointer */ngx_queue_last (h)/* H for the last element in the list container h for the ngx_queue_t of the linked list container structure. Returns a pointer to the list structure body */ngx_queue_sentinel (h)/* x is a pointer to the ngx_queue_t of the linked list container structure. Remove the X element */ngx_queue_remove (x)//h from the container as a pointer to the ngx_queue_t of the linked list container structure. This function is used to split the list, * H is a list container, and q is an element in the chain table H. * The link list h is split into two linked list H and N */ngx_queue_split (h, q, N)//h for the ngx_queue_t of the linked list container structure, n is the pointer of the other linked list container struct ngx_queue_t * Merge linked list , add the N-linked list to the end of the H-linked list */ngx_queue_add (h, N)//h as a pointer to the ngx_queue_t of the linked list container structure. Returns the central element of the list, that is, N/2 + 1 */ngx_queue_middle (h)//h is a pointer to the ngx_queue_t of the linked list container structure, and Cmpfunc is the comparison callback function. Sort the linked list using insert sort */ngx_queue_sort (H, CMPFunc)//q is a pointer to the ngx_queue_t member of an element structure in a linked list. Returns the next element of the Q element. */ngx_queue_next (q)/* Q is a pointer to the ngx_queue_t member of one of the element structures in the linked list. Returns the previous element of the Q element. */ngx_queue_prev (q)//q is a pointer to the ngx_queue_t member of one of the element structures in the list, type is the struct type name of the linked list element, and link is the member name of the ngx_queue_t type in the struct above. Returns the address of the struct to which the Q element belongs */ngx_queue_data (q, type, link)//q is a pointer to the ngx_queue_t member of an element structure in the linked list, and x is the pointer to the ngx_queue_t member in the element structure */ngx_qu Eue_insert_after (q, x)
Below is the implementation of the source code for the queue list operation:
Initialize the list of links
/* Initialize the queue, that is, the node pointer points to itself, expressed as an empty queue */#define NGX_QUEUE_INIT (q) (q) ->prev = q; (q)->next = q/* Determines whether the queue is empty/#define NGX_QUEUE_EMPTY (H) (H = = (h)->prev)
Gets the node in the specified queue chain list
/* Get the Queue head node */#define Ngx_queue_head (H) (h) ->next/* get the tail node of the queue */#define NGX_QUEUE_LAST (H) (h) ->prev# Define Ngx_queue_sentinel (h) (h)/* Gets the next node of the queue specified node */#define NGX_QUEUE_NEXT (q) (q)->next/* gets the previous node of the queue specified node */#define NGX_QUEUE_PREV (q) (q)->prev
Inserting nodes
Insert a new node after the head node:
/* Insert a new node in the next node of the queue head node where h is the head node and x is the new node */#define Ngx_queue_insert_head (H, x) (x)->next = (h)->next; (x)->next->prev = x; (x)->prev = h; (h)->next = X
Inserting a node is simple, just modifying the pointer point. is the process of inserting a node:Note: Dashed lines indicate disconnection, solid lines represent the original connection, broken polylines indicate reconnection, and the figures in the figure correspond to the source step.
Inserting nodes after the tail node
/* Insert a new node after the end of the queue, where h is the tail node and x is the new node */#define NGX_QUEUE_INSERT_TAIL (H, x) (x)->prev = (h)->prev; (x)->prev->next = x; (x)->next = h; (h)->prev = X
is the process of inserting a node:
Delete a node
Deleting the specified node, deleting the node just modifies the pointer to the neighboring node, and does not actually release the memory of that node, and the memory release must be handled by us.
#if (ngx_debug) #define NGX_QUEUE_REMOVE (x) (x)->next->prev = (x)->prev; (x)->prev->next = (x)->next; (x)->prev = NULL; (x)->next = null#else/* Deletes the node specified by the queue */#define NGX_QUEUE_REMOVE (x) (x)->next->prev = (x)->prev; (x)->prev->next = (x)->next#endif
The procedure for deleting a node is as follows:
Split linked list
/* Split the queue list so that it is referred to as two separate list of queues; * where h is the head node of the original queue, q is an element node in the original queue, n is a new node, * After splitting, the original queue is delimited by Q, and the node before the head node H to Q is a queue (excluding Q node), * The other queue is based on N as the head node, with node Q and its subsequent nodes as the new queue list; */#define Ngx_queue_split (H, Q, N) (n)->prev = (h)->prev; (n)->prev->next = n; (n)->next = q; (h)->prev = (q)->prev; (h)->prev->next = h; (q)->prev = n;
The macro has 3 parameters, H is the queue header (that is, the linked table head pointer), the queue from the Q node (the linked list) is split into two queues (linked list), the node after Q of the new queue of the head node is n. The list splitting process is as follows: Merge linked list
/* Merge the list of two queues to connect the N queue list to the tail of the H queue list */#define NGX_QUEUE_ADD (H, N) (h)->prev->next = (n)->next; (n)->next->prev = (h)->prev; (h)->prev = (n)->prev; (h)->prev->next = h; (n)->prev = (n)->next = n;/* This is my personal added statement, and if you add the statement, the head node n will point to the node of the queue list */
where h, N is a two-queue pointer, the head node pointer, which links the n queue after the H queue. The exact operation is as follows:
Get Intermediate node
/* Returns the queue List center element */ngx_queue_t *ngx_queue_middle (ngx_queue_t *queue) { ngx_queue_t *middle, *next; /* Get the Queue link Header node * /middle = ngx_queue_head (queue); /* If the head node of the queue list is the tail node, it indicates that the queue list has only one element * /if (middle = = Ngx_queue_last (queue)) { return middle; } /* Next as a temporary pointer, first point to the head node of the queue list * /next = ngx_queue_head (queue); for (;;) {/ * If the queue list has more than one element, it is equivalent to middle = middle->next * /middle = ngx_queue_next (middle); Next = Ngx_queue_next (next); /* The queue list has an even number of elements * /if (next = ngx_queue_last (queue)) { return middle; } Next = Ngx_queue_next (next); /* The queue list has an odd number of elements * /if (next = ngx_queue_last (queue)) { return middle; }} }
List sort queue list sorting using a stable simple insert sorting method, that is, starting from the first node, the current node (q) is inserted in the queue (linked list) in the previous order, in the following program, the queue in front of the queued tail node is prev. The operation is as follows:
/* the Stable Insertion Sort *//* Queue list */voidngx_queue_sort (ngx_queue_t *queue, ngx_int_t (*cmp) (const ngx_queue_t *, const Ngx_que ue_t *) {ngx_queue_t *q, *prev, *next; Q = ngx_queue_head (queue); /* If the queue list has only one element, return directly */if (q = = Ngx_queue_last (queue)) {return; }/* Traverse the entire queue list */for (q = ngx_queue_next (q); Q! = Ngx_queue_sentinel (queue); q = next) {prev = Ngx_queue_pre V (q); Next = Ngx_queue_next (q); /* First separate the element node Q */Ngx_queue_remove (q); /* Locate the position suitable for q insertion */do {if (CMP (prev, q) <= 0) {break; } prev = Ngx_queue_prev (prev); } while (prev! = Ngx_queue_sentinel (queue)); /* INSERT element Node Q */Ngx_queue_insert_after (prev, q); }}
Get the node data address in the queue by the basic structure of the queue and the above operation, the Nginx queue operation only makes simple modification to the linked list pointer, and is not responsible for the allocation of the node data space. Therefore, when you use the Nginx queue, you define the data structure and allocate space, and in it contains a pointer or an object of ngx_queue_t, when you need to get the queue node data, use the Ngx_queue_data macro, which is defined as follows:
/* Returns the address of the struct type to which the q belongs, type is the structure type of the linked list element */#define NGX_QUEUE_DATA (q, type, link) ( type *) ((U_char *) q-offsetof (type, link)) /*
Test procedure:
#include <stdio.h> #include "ngx_queue.h" #include "ngx_conf_file.h" #include "ngx_config.h" #include "ngx_ Palloc.h "#include" nginx.h "#include" ngx_core.h "#define MAX 10typedef struct score{unsigned int score; ngx_queue_t Que;} Ngx_queue_score;volatile ngx_cycle_t *ngx_cycle;void Ngx_log_error_core (ngx_uint_t level, ngx_log_t *log, ngx_err_t Err, const char *FMT, ...) {}ngx_int_t CMP (const ngx_queue_t *x, const ngx_queue_t *y) {Ngx_queue_score *xinfo = ngx_queue_data (x, Ngx_queue_score , Que); Ngx_queue_score *yinfo = Ngx_queue_data (y, Ngx_queue_score, Que); Return (Xinfo->score > Yinfo->score);} void Print_ngx_queue (ngx_queue_t *queue) {ngx_queue_t *q = ngx_queue_head (queue); printf ("Score:"); for (; Q! = Ngx_queue_sentinel (queue); q = Ngx_queue_next (q)) {Ngx_queue_score *ptr = ngx_queue_data (q, Ngx_que Ue_score, Que); if (ptr! = NULL) printf ("%d\t", Ptr->score); } printf ("\ n");} int main () {ngx_pool_t *pool; ngx_queue_t *queue; Ngx_queue_score *qscore; Pool = Ngx_create_pool (1024x768, NULL); Queue = Ngx_palloc (pool, sizeof (ngx_queue_t)); Ngx_queue_init (queue); int i; for (i = 1; i < MAX; i++) {Qscore = (ngx_queue_score*) ngx_palloc (pool, sizeof (Ngx_queue_score)); Qscore->score = i; Ngx_queue_init (&qscore->que); if (i%2) {ngx_queue_insert_tail (queue, &qscore->que); } else {Ngx_queue_insert_head (queue, &qscore->que); }} printf ("Before sort:"); Print_ngx_queue (queue); Ngx_queue_sort (queue, CMP); printf ("After sort:"); Print_ngx_queue (queue); Ngx_destroy_pool (pool); return 0;}
Output Result:
$./queue_test before Sort:score: 8 6 4 2 1 3 5 7 9After Sort:score: 1 2 3 4 5 6 7 8 9
Summary in the Nginx queue list, which maintains a pointer to the list node, and there is no actual data area, all the operation of the actual data needs our own operation, the chain list is a two-way circular link list, its operation is the basic operation of the doubly linked list.
Nginx queue doubly linked list structure ngx_quene_t