Nginx learning (5) - basic structure (source code)

Nginx source code learning

  • Nginx has implemented many more efficient data structures and public functions based on its own characteristics, so we should try to call the API provided by Nginx when developing

basic data structure

1. String structure: ngx_str_t

  • Location : ngx_string.h and ngx_string.c under src/core

  • Prototype : As you can see, Nginx customizes a string structure with length. This means that the string pointed to by data does not end with "\0".

    typedef struct {
          
          
    	size_t      len;
    	u_char     *data;
    } ngx_str_t;
    
  • Nginx string manipulation macros :

    • 1. Construct a constant string :
      #define ngx_string(str) {
                
                 sizeof(str) - 1, (u_char *) str } 
      
    • 2. Initialize the string to an empty string :
      #define ngx_null_string     {
                
                 0, NULL }
      
    • 3. Set ngx_str_t to text :
      #define ngx_str_set(str, text) \
       (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text
      
    • 4. Set ngx_str_t to null :
      #define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL
      
  • Use of Nginx string manipulation macros :

    ngx_str_t str = ngx_string("hello world");
    ngx_str_t str1 = ngx_null_string;
    
    ngx_str_t str2, str3;
    ngx_str_set(&str2, "hello world"); 	
    ngx_str_null(&str3); 		
    
  • Nginx string manipulation function

    • 1. String conversion : Convert the first n characters of src into lowercase and store them in the dst string. str will not change.

      void ngx_strlow(u_char *dst, u_char *src, size_t n);
      // 若想改变源字符串
      ngx_strlow(str->data, str->data, str->len);
      
    • 2. String comparison :

      ngx_strncmp(s1, s2, n)// 区分大小写的比较前n个字符。
      ngx_strcmp(s1, s2)// 区分大小写比较两个字符串。
      
      ngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);				// 不区分大小写的字符串比较。返回0表示相等。
      ngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);	// 不区分大小写的前n个字符的比较。
      
    • 3. String formatting :

      u_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);
      u_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);		// 推荐使用
      u_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...);	// 推荐使用
      
      // 这里特别要提醒的是,我们最常用于格式化ngx_str_t结构,其对应的转义符是%V,传给函数的一定要是指针类型。
      ngx_str_t str = ngx_string("hello world");
      char buffer[1024];
      ngx_snprintf(buffer, 1024, "%V", &str); // 注意,str取地址
      
      • Formatting parameters in Nginx are explained in ngx_string.c
        Insert image description here
    • 4. String BASE64 encoding and decoding : put the result in dst, you need to ensure that dst has enough space to store the result.

      // 如果不知道具体大小,可先调用ngx_base64_encoded_length与ngx_base64_decoded_length来预估最大占用空间。
      #define ngx_base64_encoded_length(len)  (((len + 2) / 3) * 4)
      #define ngx_base64_decoded_length(len)  (((len + 3) / 4) * 3)
      
      void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);
      ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);
      
    • 5. String ESCAPE encoding : Encode src according to type, and put the result in dst. If dst passes NULL, the space size of the result can be obtained.

      // type可以是:
      #define NGX_ESCAPE_URI 0
      #define NGX_ESCAPE_ARGS 1
      #define NGX_ESCAPE_HTML 2
      #define NGX_ESCAPE_REFRESH 3
      #define NGX_ESCAPE_MEMCACHED 4
      #define NGX_ESCAPE_MAIL_AUTH 5
      
      uintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type);
      
      // 反escape编码:
      // type可以是 0 或下面的宏:
      #define NGX_UNESCAPE_URI       1
      #define NGX_UNESCAPE_REDIRECT  2
      
      void ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);
      
      • If type is 0, it means that all characters in src must be transcoded. If it is NGX_UNESCAPE_URI and NGX_UNESCAPE_REDIRECT, it will end after encountering '?', and the following characters will be ignored. The difference between NGX_UNESCAPE_URI and NGX_UNESCAPE_REDIRECT is that NGX_UNESCAPE_URI will transcode characters that need to be transcoded, while NGX_UNESCAPE_REDIRECT will only transcode non-visible characters.
    • 6. Encode html tags : dst passes NULL and returns the space occupied by the result.

      uintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);
      
    • 7. Encode JSON : pass NULL to dst and return the space occupied by the result.

      uintptr_t ngx_escape_json(u_char *dst, u_char *src, size_t size);
      

2. Structure similar to resource management: ngx_pool_t

  • Location : ngx_palloc.h and ngx_palloc.c under src/core
  • Prototype :
    typedef struct ngx_pool_s            ngx_pool_t;
    
    struct ngx_pool_s {
          
          
        ngx_pool_data_t       d;
        size_t                max;
        ngx_pool_t           *current;
        ngx_chain_t          *chain;
        ngx_pool_large_t     *large;
        ngx_pool_cleanup_t   *cleanup;
        ngx_log_t            *log;
    };
    
  • Operation :
    • 1. Create a pool with an initial node size of size : log is the object to output logs when subsequent operations are performed on the pool.

      // size的有效最大值为NGX_MAX_ALLOC_FROM_POOL,若传入的值超过NGX_MAX_ALLOC_FROM_POOL,
      // 则实际用到的不会超过NGX_MAX_ALLOC_FROM_POOL,造成浪费。
      // size还必须大于sizeof(ngx_pool_t),否则会导致程序崩溃,因为在这片内存中需要存储ngx_pool_t本身。
      ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
      
    • 2. Memory allocation :

      // 从这个pool中分配一块为size大小的内存。此函数分配的内存的起始地址按照NGX_ALIGNMENT进行了对齐。
      // 对齐操作会提高系统处理的速度,但会造成少量内存的浪费。
      void *ngx_palloc(ngx_pool_t *pool, size_t size);
      
      // 从这个pool中分配一块为size大小的内存。但是此函数分配的内存并没有像上面的函数那样进行对齐。
      void *ngx_pnalloc(ngx_pool_t *pool, size_t size);
      
      // 该函数也是分配size大小的内存,并且对分配的内存块进行了清零。内部实际上是调用ngx_palloc实现的。
      void *ngx_pcalloc(ngx_pool_t *pool, size_t size);
      
      // 按照指定对齐大小alignment来申请一块大小为size的内存。
      // 此处获取的内存不管大小都将被置于大内存块链中管理。(也就是pool->large管理的链表)
      void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
      
    • 3. Memory release :

      // 对于被置于大块内存链,也就是被large字段管理的一列内存中的某块进行释放。(也就是释放pool->large所管理链表的某个节点)
      // 该函数的实现是顺序遍历large管理的大块内存链表。所以效率比较低下。
      // 如果在这个链表中找到了这块内存,则释放,并返回NGX_OK。否则返回NGX_DECLINED。
      ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);
      
      
      // ngx_pool_t中的cleanup字段管理着一个特殊的链表,该链表的每一项都记录着一个特殊的需要释放的资源。
      // 对于这个链表中每个节点所包含的资源如何去释放,是自说明的。
      // 这也就提供了非常大的灵活性。意味着,ngx_pool_t不仅仅可以管理内存,通过这个机制,也可以管理任何需要释放的资源。
      // 这个 size 就是要存储这个data字段所指向的资源的大小,该函数会为data分配size大小的空间。
      ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
      // 看一下 ngx_pool_cleanup_t 结构
      typedef void (*ngx_pool_cleanup_pt)(void *data);
      typedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;
      struct ngx_pool_cleanup_s {
              
              
          ngx_pool_cleanup_pt   handler;	// 是一个函数指针,指向一个可以释放data所对应资源的函数。该函数只有一个参数,就是data。
          void                 *data;		// 指明了该节点所对应的资源。
          ngx_pool_cleanup_t   *next;		// 指向该链表中下一个元素。
      };
      
      
      // 该函数就是释放pool中持有的所有内存,以及依次调用cleanup字段所管理的链表中每个元素的handler字段所指向的函数。
      // 释放掉所有该pool管理的资源。并且把pool指向的ngx_pool_t也释放掉。
      void ngx_destroy_pool(ngx_pool_t *pool);
      
      
      // 该函数释放pool中所有大块内存链表上的内存,小块内存链上的内存块都修改为可用。但是不会去处理cleanup链表上的结点。
      void ngx_reset_pool(ngx_pool_t *pool);
      

3. Nginx array structure: ngx_array_t

  • Location : ngx_array.c and ngx_array.h under src/core
  • Prototype :
    typedef struct {
          
          
        void        *elts;		// 指向实际的数据存储区域。
        ngx_uint_t   nelts;		// 数组实际元素个数。
        size_t       size;		// 数组单个元素的大小,单位是字节。
        ngx_uint_t   nalloc;	/* 数组的容量。表示该数组在不引发扩容的前提下,可以最多存储的元素的个数。
        						   当nelts增长到达nalloc 时,如果再往此数组中存储元素,则会引发数组的扩容。
        						   数组的容量将会扩展到原有容量的2倍大小。实际上是分配新的一块内存,
        						   新的一块内存的大小是原有内存大小的2倍。原有的数据会被拷贝到新的一块内存中。*/
        ngx_pool_t  *pool;		// 该数组用来分配内存的内存池。
    } ngx_array_t;
    
  • Operation :
    • 1. Create an array object :
      // p:数组分配内存使用的内存池;
      // n:数组的初始容量大小,即在不扩容的情况下最多可以容纳的元素个数。
      // size:单个元素的大小,单位是字节。
      ngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);
      
    • 2. Add a new element : Append a new element to array a and return a pointer to the new element.
      // 需要把返回的指针使用类型转换,转换为具体的类型,然后再给新元素本身或者是各字段(如果数组的元素是复杂类型)赋值。
      void *ngx_array_push(ngx_array_t *a);
      
    • 3. Add elements in batches : Append n elements to array a, and return a pointer to the position of the first element of these appended elements.
      void *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);
      
    • 4. Destroy the array object :
      // 销毁该数组对象,并将其内存放回内存池。
      void ngx_array_destroy(ngx_array_t *a);
      
    • 5. Initialization :
      // 如果一个数组对象是被分配在栈上的,那么就需要调用此函数,进行初始化的工作以后,才可以使用。
      
      // 由于使用ngx_palloc分配内存,数组在扩容时,旧的内存不会被释放,会造成内存的浪费。(ngx_array_push和ngx_array_push_n函数对扩容机制的实现)
      // 因此,最好能提前规划好数组的容量,在创建或者初始化的时候一次搞定,避免多次扩容,造成内存浪费。
      static ngx_inline ngx_int_t ngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size);
      

4. Hash table structure:

(1) ngx_hash_t: ordinary hash table

  • Location : ngx_hash.h and ngx_hash.c under src/core
  • Prototype :
    typedef struct {
          
          
        ngx_hash_elt_t  **buckets;
        ngx_uint_t        size;
    } ngx_hash_t;
    
  • Features of Nginx hash table :
    • The way ngx_hash_t resolves hash conflicts is similar to the open chain method.
    • Unlike other hash table implementations, ngx_hash_t can insert and delete elements. It can only be initialized once. After the entire hash table is constructed, elements can neither be deleted nor inserted.
    • The chain opening of ngx_hash_t does not really open a linked list, but actually opens a continuous storage space, which can almost be regarded as an array. This is because when ngx_hash_t is initialized, it will go through a pre-calculation process and calculate how many elements will be in each bucket in advance, so that the size of each bucket can be known in advance. Then there is no need to use a linked list, a continuous storage space is enough. This also saves memory usage to a certain extent.
  • Operation :
    • 1. Initialization function :
      // hinit是初始化的一些参数的一个集合。
      // names是初始化一个 ngx_hash_t 所需要的所有 key 的一个数组。
      // nelts就是key的个数。
      ngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts);
      
      // 看一下 ngx_hash_init_t 结构
      typedef struct {
              
              
          ngx_hash_t       *hash;	// 该字段如果为NULL,那么调用完初始化函数后,该字段指向新创建出来的hash表。
          						// 如果该字段不为NULL,那么在初始化的时候,所有的数据被插入到这个字段所指的hash表中。
          ngx_hash_key_pt   key;	// 指向从字符串生成hash值的hash函数。nginx的源代码中提供了默认的实现函数ngx_hash_key_lc。
      
          ngx_uint_t        max_size;		// hash表中的桶的个数。
          								// 该字段越大,元素存储时冲突的可能性越小,每个桶中存储的元素会更少,则查询起来的速度更快。
          								// 当然,这个值越大,越造成内存的浪费也越大。
          ngx_uint_t        bucket_size;	// 每个桶的最大限制大小,单位是字节。
      
          char             *name;			// 该 hash 表的名字。
          ngx_pool_t       *pool;			// 该hash表分配内存使用的pool。
          ngx_pool_t       *temp_pool;	// 该hash表使用的临时pool,在初始化完成以后,该pool可以被释放和销毁掉。
      } ngx_hash_init_t;
      
      // 再看一下 ngx_hash_key_t 结构
      typedef struct {
              
              
          ngx_str_t         key;
          ngx_uint_t        key_hash;	// key的 hash 值
          void             *value;
      } ngx_hash_key_t;
      
    • 2. Find :
      // 在hash里面查找key对应的value。
      // 实际上这里的key是对真正的key(也就是name)计算出的hash值。
      // len是name的长度。
      // 如果查找成功,则返回指向value的指针,否则返回NULL。
      void *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len);
      

(2) ngx_hash_wildcard_t: wildcard domain name hash table

  • Location : ngx_hash.c and ngx_hash.h under src/core
  • Prototype :
    typedef struct {
          
          
        ngx_hash_t        hash;
        void             *value;
    } ngx_hash_wildcard_t;
    
  • Features :
    • In order to handle the matching problem of domain names with wildcard characters, nginx implements a hash table such as ngx_hash_wildcard_t.
    • It supports two types of domain names with wildcard characters.
      • If the wildcard character comes first, for example: "*.abc.com", you can also omit the asterisk and write it directly as ".abc.com".
      • The other one is with the wildcard at the end, for example: "mail.xxx.*". Special attention should be paid to the wildcard at the end and cannot be omitted.
  • Note :
    • A hash table of type ngx_hash_wildcard_t can only contain the key with the wildcard character first or the key with the wildcard character after it. Keys that cannot contain two types of wildcard characters at the same time.
  • Operation :
    • 1. Initialization :

      // 该函数迎来构建一个可以包含通配符key的hash表。该函数执行成功返回NGX_OK,否则NGX_ERROR。
      // hinit: 构造一个通配符hash表所需参数的一个集合。
      // nelts: names数组元素的个数。
      ngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts);
      
      • Description of the names parameter:
        • Construct an array of all wildcard keys in this hash table. It should be noted that the keys here have been preprocessed. For example: "*.abc.com" or ".abc.com" becomes "com.abc." after being preprocessed. And "mail.xxx.*" is preprocessed into "mail.xxx.".
          Why is it being treated like this? Here we have to briefly describe the implementation principle of the wildcard hash table.
          When constructing this type of hash table, a "linked list" of a hash table is actually constructed, which is "linked" through the keys in the hash table. For example: for "*.abc.com", two hash tables will be constructed. The first hash table has an entry with the key com, and the value of this entry contains a pointer to the second hash table. There is an entry abc in the second hash table, and the value of this entry contains a pointer to the value corresponding to *.abc.com. Then when querying, for example, when querying www.abc.com, first check com. By checking com, you can find the second-level hash table. In the second-level hash table, then look for abc, and so on, until you find a certain When the value corresponding to the entry found in the hash table of the next level corresponds to a real value rather than a pointer to the hash table of the next level, the query process ends.
          One thing that needs special attention here is that the lower two bits of the value of the element in the names array must be 0 (for special purposes). If this condition is not met, the hash table query will not produce correct results.
    • 2. Query :

// 该函数查询包含通配符在前的key的hash表的。
void *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);

// 该函数查询包含通配符在末尾的key的hash表的。
void *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);

// hwc: hash表对象的指针。
// name: 需要查询的域名,例如: www.abc.com。
// len: name的长度。

(3) ngx_hash_combined_t: combined type hash table

  • Location : ngx_hash.h and ngx_hash.c under src/core
  • Prototype :
    // 该类型实际上包含了三个hash表,一个普通hash表,一个包含前向通配符的hash表和一个包含后向通配符的hash表。
    typedef struct {
          
          
        ngx_hash_t            hash;
        ngx_hash_wildcard_t  *wc_head;
        ngx_hash_wildcard_t  *wc_tail;
    } ngx_hash_combined_t;
    
  • Operation :
    • Query :
      // 该函数在此组合hash表中,依次查询其三个子hash表,看是否匹配,一旦找到,立即返回查找结果,
      // 也就是说如果有多个可能匹配,则只返回第一个匹配的结果。
      // 成功返回查询的结果,未查到则返回NULL。
      void *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name, size_t len);
      
      // hash: 此组合hash表对象。
      // key: 根据key计算出的hash值。
      // name: key实际的具体内容。
      // len: name的长度。
      

(4) ngx_hash_keys_arrays_t: This structure is used to preprocess keys

  • Location : ngx_hash.h and ngx_hash.c under src/core
  • Prototype :
可以看到在构建一个ngx_hash_wildcard_t的时候,需要对通配符的哪些key进行预处理。
这个处理起来比较麻烦。而当有一组key,这些里面既有无通配符的key,也有包含通配符的key的时候。
我们就需要构建三个hash表,一个包含普通的key的hash表,一个包含前向通配符的hash表,
一个包含后向通配符的hash表(或者也可以把这三个hash表组合成一个ngx_hash_combined_t)。
在这种情况下,为了让大家方便的构造这些hash表,nginx提供给了此辅助类型。

typedef struct {
    
    
    ngx_uint_t        hsize;	// 将要构建的hash表的桶的个数。对于使用这个结构中包含的信息构建的三种类型的hash表都会使用此参数。

    ngx_pool_t       *pool;		// 构建这些hash表使用的pool。
    ngx_pool_t       *temp_pool;// 在构建这个类型以及最终的三个hash表过程中可能用到临时pool。该temp_pool可以在构建完成以后,被销毁掉。

    ngx_array_t       keys;		// 存放所有非通配符key的数组。
    ngx_array_t      *keys_hash;// 这是个二维数组,第一个维度代表的是bucket的编号,
    							// 那么keys_hash[i]中存放的是所有的key算出来的hash值对hsize取模以后的值为i的key。
    							/* 假设有3个key,分别是key1,key2和key3假设hash值算出来以后对hsize取模的值都是i,
    							那么这三个key的值就顺序存放在keys_hash[i][0],keys_hash[i][1], keys_hash[i][2]。
    							该值在调用的过程中用来保存和检测是否有冲突的key值,也就是是否有重复。*/

    ngx_array_t       dns_wc_head;	// 放前向通配符key被处理完成以后的值。
    								// 比如:“*.abc.com” 被处理完成以后,变成 “com.abc.” 被存放在此数组中。
    ngx_array_t      *dns_wc_head_hash; // 同理 keys_hash

    ngx_array_t       dns_wc_tail;	// 存放后向通配符key被处理完成以后的值。
    								// 比如:“mail.xxx.*” 被处理完成以后,变成 “mail.xxx.” 被存放在此数组中。
    ngx_array_t      *dns_wc_tail_hash;	// 同理 keys_hash
} ngx_hash_keys_arrays_t;
  • Operation :
    • Initialization :
      // 初始化ngx_hash_keys_arrays_t结构,主要是对这个结构中的ngx_array_t类型的字段进行初始化,成功返回NGX_OK。
      ngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type);
      
      // type: 该字段有2个值可选择,即 NGX_HASH_SMALL 和 NGX_HASH_LARGE 用来指明将要建立的hash表的类型,
      // 如果是 NGX_HASH_SMALL,则有比较小的桶的个数和数组元素大小。NGX_HASH_LARGE则相反。
      
    • Added : For information on the use of this data structure, please refer to the ngx_http_server_names function in src/http/ngx_http.c.
      // 一般是循环调用这个函数,把一组键值对加入到这个结构体中。返回NGX_OK是加入成功。返回NGX_BUSY意味着key值重复。
      ngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value, ngx_uint_t flags);
      
      /* flags: 有两个标志位可以设置, NGX_HASH_WILDCARD_KEY 和 NGX_HASH_READONLY_KEY 。
      同时要设置的使用按位或操作符就可以了。NGX_HASH_READONLY_KEY被设置的时候,在计算hash值的时候,key的值不会被转成小写字符,否则会。
      NGX_HASH_WILDCARD_KEY被设置的时候,说明key里面可能含有通配符,会进行相应的处理。
      如果两个标志位都不设置,传0。*/
      

5. Response message structure: ngx_chain_t

  • Location : ngx_buf.h and ngx_buf.c under src/core
  • Prototype :
typedef struct ngx_chain_s           ngx_chain_t;
struct ngx_chain_s {
    
    
    ngx_buf_t    *buf;		// 实际数据。
    ngx_chain_t  *next;		// 下一结点。
};
  • Operation :
    • 1. Create :
      // 该函数创建一个ngx_chain_t的对象,并返回指向对象的指针,失败返回NULL。
      ngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);
      
    • 2. Release
      #define ngx_free_chain(pool, cl)            \
      (cl)->next = (pool)->chain;             \
      (pool)->chain = (cl)
      

ngx_buf_t

  • Prototype :
struct ngx_buf_s {
    
    
    u_char          *pos;	// 当buf所指向的数据在内存里的时候,pos指向的是这段数据开始的位置。
    u_char          *last;	// 当buf所指向的数据在内存里的时候,last指向的是这段数据结束的位置。
    off_t            file_pos;	// 当buf所指向的数据是在文件里的时候,file_pos指向的是这段数据的开始位置(在文件中的偏移量)。
    off_t            file_last;	// 当buf所指向的数据是在文件里的时候,file_pos指向的是这段数据的结束位置(在文件中的偏移量)。

    u_char          *start;         /* start of buffer */
    u_char          *end;           /* end of buffer */
    							/* 当buf所指向的数据在内存中的时候,这以整块数据可能被拆分在不同的buf中,
    							start和end指向的是这一整块数据的开始和结尾
    							(也就是说start和end之间可能存在不属于这整块数据的内容),
    							而pos和last指向的是当前buf的开始和结尾。 */
    ngx_buf_tag_t    tag;	// 实际上是一个void*类型的指针,使用者可以关联任意的对象上去,只要对使用者有意义。
    ngx_file_t      *file;	// 当buf所包含的内容在文件中时,file字段指向对应的文件对象。
    ngx_buf_t       *shadow;/* 当这个buf完整copy了另外一个buf的所有字段的时候,那么这两个buf指向的实际上是同一块内存,
							    或者是同一个文件的同一部分,此时这两个buf的shadow字段都是指向对方的。
							    那么对于这样的两个buf,在释放的时候,就需要使用者特别小心,具体是由哪里释放,要提前考虑好,
							    如果造成资源的多次释放,可能会造成程序崩溃! */


    /* the buf's content could be changed */
    unsigned         temporary:1;	/* 为1时表示该buf所包含的内容是在一个用户创建的内存块中,
    								并且可以被在filter处理的过程中进行变更,而不会造成问题。*/

    /*
     * the buf's content is in a memory cache or in a read only memory
     * and must not be changed
     */
    unsigned         memory:1;	// 为1时表示该buf所包含的内容是在内存中,但是这些内容却不能被进行处理的filter进行变更。

    /* the buf's content is mmap()ed and must not be changed */
    unsigned         mmap:1;	// 为1时表示该buf所包含的内容是在内存中, 是通过mmap使用内存映射从文件中映射到内存中的,
    							// 这些内容却不能被进行处理的filter进行变更。

    unsigned         recycled:1;/* 可以回收的。也就是这个buf是可以被释放的。这个字段通常是配合shadow字段一起使用的,对于使用
								ngx_create_temp_buf 函数创建的buf,并且是另外一个buf的shadow,
								那么可以使用这个字段来标示这个buf是可以被释放的。*/
    unsigned         in_file:1;	// 为1时表示该buf所包含的内容是在文件中。
    unsigned         flush:1;	/* 遇到有flush字段被设置为1的buf的chain时,则该chain的数据即便不是最后结束的数据
    								(last_buf被设置,标志所有要输出的内容都完了),也会进行输出,
    								不会受postpone_output配置的限制,但是会受到发送速率等其他条件的限制。*/
    unsigned         sync:1;	
    unsigned         last_buf:1;// 数据被以多个chain传递给了过滤器,此字段为1表明这是最后一个buf。
    unsigned         last_in_chain:1;/* 在当前的chain里面,此buf是最后一个。
    						需要注意的是last_in_chain的buf不一定是last_buf,但是last_buf的buf一定是last_in_chain的。
    						这是因为数据会被以多个chain传递给某个filter模块。 */

    unsigned         last_shadow:1;	// 在创建一个buf的shadow的时候,通常将新创建的一个buf的last_shadow置为1。
    unsigned         temp_file:1;	// 由于受到内存使用的限制,有时候一些buf的内容需要被写到磁盘上的临时文件中去,
    								// 那么这时,就设置此标志 
    
    /* STUB */ int   num;
};
  • Operation :
    • 1. Create :
      #define ngx_alloc_buf(pool)  ngx_palloc(pool, sizeof(ngx_buf_t))
      #define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))
      
      
      //对于创建temporary字段为1的buf(就是其内容可以被后续的filter模块进行修改),
      //可以直接使用函数ngx_create_temp_buf进行创建。
      ngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);
      /* 对于创建的这个对象,它的start和end指向新分配内存开始和结束的地方。pos和last都指向这块新分配内存的开始处,
      这样,后续的操作可以在这块新分配的内存上存入数据。 */
      
      
      // 为了配合对ngx_buf_t的使用,nginx定义了以下的宏方便操作。
      // 1. 返回这个buf里面的内容是否在内存里。
      #define ngx_buf_in_memory(b)       ((b)->temporary || (b)->memory || (b)->mmap)
      
      // 2. 返回这个buf里面的内容是否仅仅在内存里,并且没有在文件里。
      #define ngx_buf_in_memory_only(b)  (ngx_buf_in_memory(b) && !(b)->in_file)
      
      // 3. 返回该buf是否是一个特殊的buf,只含有特殊的标志和没有包含真正的数据。
      #define ngx_buf_special(b)                                                   \
      		 (((b)->flush || (b)->last_buf || (b)->sync)                              \
      			&& !ngx_buf_in_memory(b) && !(b)->in_file)
      
      // 4. 返回该buf是否是一个只包含sync标志而不包含真正数据的特殊buf。
      #define ngx_buf_sync_only(b)                                                 \
      	 ((b)->sync && !ngx_buf_in_memory(b)                                      \
      		&& !(b)->in_file && !(b)->flush && !(b)->last_buf)
      
      // 5. 返回该buf所含数据的大小,不管这个数据是在文件里还是在内存里。
      #define ngx_buf_size(b)                                                      \
      	(ngx_buf_in_memory(b) ? (off_t) ((b)->last - (b)->pos):                  \
                          		((b)->file_last - (b)->file_pos))
      

6. Linked list structure: ngx_list_t

  • Location : ngx_list.h and ngx_list.c under src/core
  • Prototype :
typedef struct {
    
    
    ngx_list_part_t  *last;		// 指向该链表的最后一个节点。
    ngx_list_part_t   part;		// 该链表的首个存放具体元素的节点。
    size_t            size;		// 链表中存放的具体元素所需内存大小。
    ngx_uint_t        nalloc;	// 每个节点所含的固定大小的数组的容量。
    ngx_pool_t       *pool;		// 该list使用的分配内存的pool。
} ngx_list_t;

// 再看一下ngx_list_part_t  
typedef struct ngx_list_part_s  ngx_list_part_t;
struct ngx_list_part_s {
    
    
    void             *elts;		// 节点中存放具体元素的内存的开始地址。
    ngx_uint_t        nelts;	// 节点中已有元素个数。这个值是不能大于链表头节点ngx_list_t类型中的nalloc字段的。
    ngx_list_part_t  *next;		// 指向下一个节点。
};
  • Operation :
    • 1. Create :
      // 1. 该函数创建一个ngx_list_t类型的对象,并对该list的第一个节点分配存放元素的内存空间。
      // pool: 分配内存使用的pool。
      // n: 每个节点固定长度的数组的长度。
      // size: 存放的具体元素的个数。
      // 成功返回指向创建的ngx_list_t对象的指针,失败返回NULL。
      ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);
      
      /* 2. 该函数是用于ngx_list_t类型的对象已经存在,但是其第一个节点存放元素的内存空间还未分配的情况下,
      可以调用此函数来给这个list的首节点来分配存放元素的内存空间。
      那么什么时候会出现已经有了ngx_list_t类型的对象,而其首节点存放元素的内存尚未分配的情况呢?
      那就是这个ngx_list_t类型的变量并不是通过调用ngx_list_create函数创建的。
      例如:如果某个结构体的一个成员变量是ngx_list_t类型的,那么当这个结构体类型的对象被创建出来的时候,
      这个成员变量也被创建出来了,但是它的首节点的存放元素的内存并未被分配。
      总之,如果这个ngx_list_t类型的变量,如果不是你通过调用函数ngx_list_create创建的,那么就必须调用此函数去初始化,
      否则,你往这个list里追加元素就可能引发不可预知的行为,亦或程序会崩溃! */
      static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)
      
    • 2. Add :
      // 该函数在给定的 list 的尾部追加一个元素,并返回指向新元素存放空间的指针。如果追加失败,则返回NULL。
      void *ngx_list_push(ngx_list_t *list);
      

7. Doubly linked list: ngx_queue_t

  • Location : ngx_queue.h and ngx_queue.c under src/core
  • Prototype :
typedef struct ngx_queue_s  ngx_queue_t;
struct ngx_queue_s {
    
    
    ngx_queue_t  *prev;
    ngx_queue_t  *next;
};
  • Features :
    • ngx_queue_t just declares forward and backward pointers. When using it, we first need to define a sentinel node (for subsequent nodes that specifically store data, we call it data nodes)
#define ngx_queue_init(q)                                                     \
    (q)->prev = q;                                                            \
    (q)->next = q

ngx_queue_t free;	// 哨兵节点
ngx_queue_init(&free);	// 需要进行初始化,通过宏ngx_queue_init()来实现


// 那么如何声明一个具有数据元素的链表节点呢?只要在相应的结构体中加上一个 ngx_queue_t 的成员就行了。
// 比如ngx_http_upstream_keepalive_module中的ngx_http_upstream_keepalive_cache_t:
typedef struct {
    
    
	ngx_http_upstream_keepalive_srv_conf_t *conf;
	ngx_queue_t queue;			// 用一个哨兵节点与之对接
	ngx_connection_t *connection;
	socklen_t socklen;
	u_char sockaddr[NGX_SOCKADDRLEN];
} ngx_http_upstream_keepalive_cache_t;
// 对于每一个这样的数据节点,可以通过ngx_queue_insert_head()来添加到链表中,第一个参数是哨兵节点,第二个参数是数据节点,
#define ngx_queue_insert_head(h, x) \
		(x)->next = (h)->next; \
		(x)->next->prev = x; \
		(x)->prev = h; \
		(h)->next = x
#define ngx_queue_insert_after ngx_queue_insert_head
#define ngx_queue_insert_tail(h, x) \
		(x)->prev = (h)->prev; \
		(x)->prev->next = x; \
		(x)->next = h; \
		(h)->prev = x
// 比如:
ngx_http_upstream_keepalive_cache_t cache;
ngx_queue_insert_head(&free, &cache.queue);
// 可以看出哨兵节点的 prev 指向链表的尾数据节点,next 指向链表的头数据节点。
// 另外ngx_queue_head()和ngx_queue_last()这两个宏分别可以得到头节点和尾节点。

// 那假如现在有一个ngx_queue_t *q 指向的是链表中的数据节点的queue成员,如何得到ngx_http_upstream_keepalive_cache_t的数据呢?
#define ngx_queue_data(q, type, link)                                         \
    (type *) ((u_char *) q - offsetof(type, link))
// nginx提供了ngx_queue_data()宏来得到ngx_http_upstream_keepalive_cache_t的指针。
ngx_http_upstream_keepalive_cache_t *cache = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

// 另外nginx也提供了ngx_queue_remove()宏来从链表中删除一个数据节点,以及ngx_queue_add()用来将一个链表添加到另一个链表。

おすすめ

転載: blog.csdn.net/Stars____/article/details/129668563