为了进行平台无关层(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 | }; |