为了进行平台无关层(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
#define ngx_strcmp(s1, s2, n) strncmp((const char *)s1, (const char*)s2, n)

ngx_list_t链表容器

ngx_list_t不是一个单纯的链表,是一种存储数组的链表,定义在/src/core/ngx_list.h文件中。这样设计的目的是为了:

  1. 存储元素更加灵活,可以是任何一种数据结构。
  2. 链表元素需要占用的内存由ngx_list_t管理,它已经通过数组分配好了。
  3. 通过数组偏移量来访问元素,比零散的小块内存效率更高。
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中的sizenalloc

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
};