Skip to content

Instantly share code, notes, and snippets.

@JeOam
Created December 15, 2015 09:59
Show Gist options
  • Save JeOam/fca30ae150a6f8f03475 to your computer and use it in GitHub Desktop.
Save JeOam/fca30ae150a6f8f03475 to your computer and use it in GitHub Desktop.
Nginx 数据结构和方法
@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

int => ngx_int_t

typedef intptr_t    ngx_int_t;

unsigned int => ngx_uint_t

typedef uintptr_t    ngx_uint_t; 

@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

字符串 => ngx_str_t

typedef struct{  
        size_t len;  
        u_char *data;
} ngx_str_t; 

比较字符串:ngx_strncmp

// ngx_strncmp 的定义
#define ngx_strncmp(s1,s2,n) strncmp((const char *)s1, (const char *)s2, n)

// ngx_strncmp 的使用,假设 r->method_name 为一个 ngx_int_t 类型的字符串
if(0==ngx_strncmp(r->method_name.data,  "PUT",  r->method_name.len)  ){
        //...
} 

@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

链表容器 => 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_part_t *next;  // 下一个链表元素 ngx_list_part_t 的地址
};
typedef struct {  
        ngx_list_part_t *last;  // 指向链表的最后一个数组元素
        ngx_list_part_t part;  // 链表的首个数组元素
        size_t size;  // 每一个数组元素的占用的空间大小
        ngx_uint_t nalloc;  // 每个 ngx_list_part_t 数组的容量,即最多可存储多少个数据
        ngx_pool_t *pool;  // 链表中管理内存分配的内存池对象。用户要存放的数据占用的内存都是由pool分配的
} ngx_list_t; 

@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

ngx_list_t 链表操作:

  • ngx_list_create: 创建新的链表
  • ngx_list_init: 初始化一个已有的链表
  • ngx_list_push: 添加新的元素

函数定义:

/**
 * pool 参数是内存池对象
 * n 是每个链表数组可容纳元素的个数(相当于 ngx_list_t 结构中的 nalloc 成员)
 * size 是每个元素的大小
 * return: 成功则返回新创建的链表地址,如果创建失败,则返回NULL空指针
 */
ngx_list_t *ngx_list_create (ngx_pool_t *pool, ngx_uint_t n ,size_t size);

/**
 * 若 ngx_list_init 返回 NGX_OK,则表示初始化成功,若返回 NGX_ERROR,则表示失败。
 */
static ngx_inline ngx_int_t ngx_list_init (ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size);

/**
 * 正常情况下,返回的是新分配的元素首地址。如果返回NULL空指针,则表示添加失败。
 * 在使用它时通常先调用 ngx_list_push 得到返回的元素地址,再对返回的地址进行赋值。
 */
void *ngx_list_push (ngx_list_t *list); 

使用示例:

// 建立一个链表,它存储的元素是 ngx_str_t,其中每个链表数组中存储 4 个元素
ngx_list_t *testlist = ngx_list_create(r->pool, 4, sizeof(ngx_str_t));
if (testlist == NULL) {  
        return NGX_ERROR;
} 
if (NGX_ERROR == ngx_list_init(testlist, r->pool, 4, sizeof(ngx_str_t))) {
        return NGX_ERROR
}

ngx_list_part_t *part = ngx_list_push(testlist);
if (str==NULL){  
        return NGX_ERROR;
}
ngx_str_t *str = part->elts;
str->len = sizeof("Hello world");
str->value = "Hello world"; 

@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

key/value 对 => ngx_table_elt_t

typedef struct{
        ngx_uint_t hash;  // 快速检索
        ngx_str_t key;
        ngx_str_t value;
        u_char *lowcase_key;  // 全小写的 key 字符串
}ngx_table_elt_t;

@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

缓冲区相关的数据结构:ngx_buf_t

typedef struct ngx_buf_s ngx_buf_t;
typedef void *ngx_buf_tag_t;
struct ngx_buf_s{
        /*pos通常是用来告诉使用者本次应该从pos这个位置开始处理内存中的数据, 这样设置是因为同一个ngx_buf_t可能被多次反复处理。当然, pos的含义是由使用它的模块定义的*/
        u_char *pos;
        /*last通常表示有效的内容到此为止, 注意, pos与last之间的内存是希望nginx处理的内容*/
        u_char *last;
        /*处理文件时, file_pos与file_last的含义与处理内存时的pos与last相同, file_pos表示将要处理的文件位置, file_last表示截止的文件位置*/
        off_t file_pos;
        off_t file_last;
        //如果ngx_buf_t缓冲区用于内存, 那么start指向这段内存的起始地址
        u_char *start;
        //与start成员对应, 指向缓冲区内存的末尾
        u_char *end;
        /*表示当前缓冲区的类型, 例如由哪个模块使用就指向这个模块ngx_module_t变量的地址*/
        ngx_buf_tag_t tag;
        //引用的文件
        ngx_file_t *file;
        /*当前缓冲区的影子缓冲区, 该成员很少用到, 仅仅在12.8节描述的使用缓冲区转发上游服务器的响应时才使用了shadow成员, 这是因为Nginx太节约内存了, 分配一块内存并使用ngx_buf_t表示接收到的上游服务器响应后, 在向下游客户端转发时可能会把这块内存存储到文件中, 也可能直接向下游发送, 此时Nginx绝不会重新复制一份内存用于新的目的, 而是再次建立一个ngx_buf_t结构体指向原内存, 这样多个ngx_buf_t结构体指向了同一块内存, 它们之间的关系就通过shadow成员来引用。这种设计过于复杂, 通常不建议使用*/
        ngx_buf_t *shadow;
        //临时内存标志位, 为1时表示数据在内存中且这段内存可以修改
        unsigned temporary:1;
        //标志位, 为1时表示数据在内存中且这段内存不可以被修改
        unsigned memory:1;
        //标志位, 为1时表示这段内存是用mmap系统调用映射过来的, 不可以被修改
        unsigned mmap:1;
        //标志位, 为1时表示可回收
        unsigned recycled:1;
        //标志位, 为1时表示这段缓冲区处理的是文件而不是内存
        unsigned in_file:1;
        //标志位, 为1时表示需要执行flush操作
        unsigned flush:1;
        /*标志位, 对于操作这块缓冲区时是否使用同步方式, 需谨慎考虑, 这可能会阻塞Nginx进程, Nginx中所有操作几乎都是异步的, 这是它支持高并发的关键。有些框架代码在sync为1时可能会有阻塞的方式进行I/O操作, 它的意义视使用它的Nginx模块而定*/
        unsigned sync:1;
        /*标志位, 表示是否是最后一块缓冲区, 因为ngx_buf_t可以由ngx_chain_t链表串联起来, 因此, 当last_buf为1时, 表示当前是最后一块待处理的缓冲区*/
        unsigned last_buf:1;
        //标志位, 表示是否是ngx_chain_t中的最后一块缓冲区
        unsigned last_in_chain:1;
        /*标志位, 表示是否是最后一个影子缓冲区, 与shadow域配合使用。通常不建议使用它*/
        unsigned last_shadow:1;
        //标志位, 表示当前缓冲区是否属于临时文件
        unsigned temp_file:1;
};

@JeOam
Copy link
Author

JeOam commented Dec 15, 2015

ngx_chain_t 是与 ngx_buf_t 配合使用的链表数据结构

typedef struct ngx_chain_s ngx_chain_t;
struct ngx_chain_s{
        ngx_buf_t *buf;  // 指向当前的 ngx_buf_t 缓冲区
        ngx_chain_t *next;  // 指向下一个ngx_chain_t。如果这是最后一个ngx_chain_t,则需要把next置为NULL。
}; 

在向用户发送 HTTP 包体时,就要传入 ngx_chain_t 链表对象,注意,如果是最后一个 ngx_chain_t,那么必须将 next 置为 NULL,否则永远不会发送成功,而且这个请求将一直不会结束(Nginx框架的要求)。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment