为了进行平台无关层(PAL, Platform Abstraction Layer)抽象, 优化数据结构对内存空间的占用以及同一代码风格等因素, Nginx为此定义,封装了一些基本数据结构。
整型的封装
1 | typedef intptr_t    ngx_int_t; | 
2 | typedef uintptr_t   ngx_uint_t; | 
ngx_str_t字符串封装
该结构定义在/src/core/ngx_string.h文件中,   ngx_str_t只有两个成员,其中data指针指向字符串起始地址,len表示字符串的有效长度。注意,ngx_str_t的data成员指向的并不是普通的字符串,
因为这段字符串未必会以’\0’作为结尾,所以使用时必须根据长度len来使用data成员。
1 | typedef struct { | 
2 |     size_t  len; | 
3 |     u_char  *data; | 
4 | } | 
5 | |
6 | if (0 == ngx_strncmp(r->method_name.data,"PUT", r->method_name.len)) { | 
7 |     /* ... */ | 
8 | } | 
9 | |
10 |  | 
ngx_list_t链表容器
ngx_list_t不是一个单纯的链表,是一种存储数组的链表,定义在/src/core/ngx_list.h文件中。这样设计的目的是为了:
- 存储元素更加灵活,可以是任何一种数据结构。
 - 链表元素需要占用的内存由ngx_list_t管理,它已经通过数组分配好了。
 - 通过数组偏移量来访问元素,比零散的小块内存效率更高。
 
1 | typedef struct ngx_list_part_s ngx_list_part_t; | 
2 | |
3 | struct ngx_list_part_s { | 
4 |     void              *elts;    /* 指向数组起始地址 */ | 
5 |     ngx_uint_t        nelts;    /* 表示数组中已经使用了多少个元素 */ | 
6 |     ngx_list_part_t   *next;    /* 下一个链表元素ngx_list_part_t的地址 */ | 
7 | } | 
8 | |
9 | typedef struct { | 
10 |     ngx_list_part_t     *last;  /* 指向链表的最后一个数组元素 */ | 
11 |     ngx_list_part_t     part;   /* 链表的首个数组元素 */ | 
12 |     size_t              size;   /* 每个数组元素占用空间大小 */ | 
13 |     /* 每个ngx_list_part_t数组的容量,即最多可存储多少个数据 */ | 
14 |     ngx_uint_t          nalloc;  | 
15 |     ngx_pool_t          *pool;  /* 管理内存分配的内存池对象 */ | 
16 | } ngx_list_t; | 

ngx_list_t接口
ngx_list_init接口用于初始化一个已有的链表,size,n分别对应ngx_list_t中的size和nalloc。
1 | static ngx_inline ngx_int_t ngx_list_init(ngx_list_t *list, \ | 
2 |             ngx_pool_t *pool, ngx_uint_t n, size_t size)  | 
3 | { | 
4 |     list->part.elts = ngx_palloc(pool, n * size);  | 
5 |     if (list->part.elts == NULL) { | 
6 |         return NGX_ERROR; | 
7 |     } | 
8 | |
9 |     list->part.nelts = 0; | 
10 |     list->part.next = NULL; | 
11 |     list->last = &list->part; | 
12 |     list->size = size; | 
13 |     list->nalloc = n; | 
14 |     list->pool = pool; | 
15 | |
16 |     return NGX_OK; | 
17 | } | 
ngx_list_create接口用于创建新的链表,size是每个元素的大小,n是每个链表数组可容纳元素的个数。
1 | ngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)  | 
2 | { | 
3 |     ngx_list_t  *list; | 
4 | |
5 |     list = ngx_palloc(pool, sizeof(ngx_list_t)); | 
6 |     if (list == NULL) { | 
7 |         return NULL; | 
8 |     } | 
9 | |
10 |     if (ngx_list_init(list, pool, n, size) != NGX_OK) { | 
11 |         return NULL; | 
12 |     } | 
13 | |
14 |     return list; | 
15 | } | 
ngx_list_push表示添加新的元素,传入参数是ngx_list_t链表。
1 | void *ngx_list_push(ngx_list_t *l) | 
2 | { | 
3 |     void             *elt; | 
4 |     ngx_list_part_t  *last; | 
5 | |
6 |     last = l->last; | 
7 | |
8 |     if (last->nelts == l->nalloc) { | 
9 | |
10 |         /* the last part is full, allocate a new list part */ | 
11 | |
12 |         last = ngx_palloc(l->pool, sizeof(ngx_list_part_t)); | 
13 |         if (last == NULL) { | 
14 |             return NULL; | 
15 |         } | 
16 | |
17 |         last->elts = ngx_palloc(l->pool, l->nalloc * l->size); | 
18 |         if (last->elts == NULL) { | 
19 |             return NULL; | 
20 |         } | 
21 | |
22 |         last->nelts = 0; | 
23 |         last->next = NULL; | 
24 | |
25 |         l->last->next = last; | 
26 |         l->last = last; | 
27 |     } | 
28 | |
29 |     elt = (char *) last->elts + l->size * last->nelts; | 
30 |     last->nelts++; | 
31 | |
32 |     return elt; | 
33 | } | 
34 | |
35 | ngx_str_t *str = ngx_list_push(testlist); | 
36 | if(str == NULL) { | 
37 |     return NGX_ERROR; | 
38 | } | 
39 | |
40 | str->len = sizeof("Hello World!"); | 
41 | str->data = "Hello World!"; | 
遍历链表容器
Nginx没有提供遍历链表容器的接口,实际上也没必要,可以用如下方法遍历链表中的元素:
1 | /* part用于指向链表中的每一个ngx_list_part_t数组 */ | 
2 | ngx_list_part* part = &testlist.part; | 
3 | |
4 | /* 根据链表中的数据类型,把数组里的elts转化为该类型使用 */ | 
5 | ngx_str_t* str = part->elts; | 
6 | |
7 | /* i表示元素在链表的每个ngx_list_part_t数组里的序号 */ | 
8 | for(i = 0; /* void */; i++) { | 
9 |     if(i >= part->nelts) { | 
10 |         if(part-next == NULL) { | 
11 |             /* 如果该指针为空,说明已经遍历完链表 */ | 
12 |             break; | 
13 |         } | 
14 |         /* 访问下一个ngx_list_part_t */ | 
15 |         part = part->next; | 
16 | |
17 |         str = part->elts; | 
18 | |
19 |         i = 0;  /* 准备访问下一个数组 */ | 
20 |     } | 
21 |     printf("list element: %*s\n", str[i].len, str[i].data); | 
22 | } | 
ngx_table_elt_t数据结构
1 | typedef struct { | 
2 |     ngx_uint_t  hash; | 
3 |     ngx_str_t   key; | 
4 |     ngx_str_t   value; | 
5 |     u_char      *lowcase_key; | 
6 | } | 
ngx_buf_t数据结构
ngx_buf_t是nginx处理大数据块的关键数据结构,定义在core/ngx_buf.h文件中,它即应用于内存数据也应用于磁盘数据,上一章中ngx_buf_t配置读取缓冲区是一个使用该结构的例子。
1 | typedef struct ngx_buf_s  ngx_buf_t; | 
2 | |
3 | struct ngx_buf_s { | 
4 |     /* pos通常是用来告诉使用者本次应该从pos这个位置开始处理内存中的 | 
5 |      * 数据,因为ngx_buf_t可能被多次反复处理。 */ | 
6 | |
7 |     u_char          *pos; | 
8 |     u_char          *last;          /* 表示有效内存到此为止 */ | 
9 | |
10 |     /* 处理文件时,file_pos与file_last的含义与处理内存时的post与last的 | 
11 |      * 含义相同 */ | 
12 | |
13 |     off_t            file_pos; | 
14 |     off_t            file_last; | 
15 | |
16 |     /* 如果ngx_buf_t缓冲区用于内存,则start表示内存起始地址 | 
17 |      * end指向内存末尾地址 */ | 
18 | |
19 |     u_char          *start;         /* start of buffer */ | 
20 |     u_char          *end;           /* end of buffer */ | 
21 | |
22 |     /* 表示当前缓冲区类型,例如由哪个模块使用就指向 | 
23 |      * 这个模块ngx_module_t变量的地址 */ | 
24 | |
25 |     ngx_buf_tag_t    tag; | 
26 |     ngx_file_t      *file;          /* 引用的文件 */ | 
27 |     ngx_buf_t       *shadow;        /* 影子缓冲区,当前不知道哪里用 */ | 
28 | |
29 |     /* 位域 */ | 
30 |     /* the buf's content could be changed */ | 
31 | |
32 |     unsigned         temporary:1;   /* 标志位,为1时表示数据在内存中且可修改 */ | 
33 | |
34 |     /* | 
35 |      * the buf's content is in a memory cache or in a read only memory | 
36 |      * and must not be changed | 
37 |      */ | 
38 |     unsigned         memory:1;      /* 为1时,表示这段内存不可修改 */ | 
39 | |
40 |     /* the buf's content is mmap()ed and must not be changed */ | 
41 |     unsigned         mmap:1;        /* 为1时表示这段内存mmap映射而来,不可被修改 */ | 
42 | |
43 |     unsigned         recycled:1;    /* 为1表示可回收 */ | 
44 |     unsigned         in_file:1;     /* 为1表示是文件而不是内存 */ | 
45 |     unsigned         flush:1;       /* 为1时表示需要指向flush操作 */ | 
46 |     unsigned         sync:1;        /*  */ | 
47 |     unsigned         last_buf:1;    /* 是否是最后一块缓冲区 */ | 
48 |     unsigned         last_in_chain:1;   /* 是否是当前最后一块待处理缓冲区 */ | 
49 | |
50 |     unsigned         last_shadow:1; /*  */ | 
51 |     unsigned         temp_file:1;   /* 表示当前缓冲区是否是临时文件 */ | 
52 | |
53 |     /* STUB */ int   num; | 
54 | }; | 
ngx_chain_t结构
ngx_chain_t是与ngx_buf_t配合使用的链表数据结构,如果处于最后一个ngx_chain_t结构,则需要把next置为NULL。
1 | typedef struct ngx_chain_s  ngx_chain_t; | 
2 | struct ngx_chain_s { | 
3 |     ngx_buf_t   *buf; | 
4 |     ngx_chain_t *next; | 
5 | }; |