Nginx源码阅读-笔记分享
Nginx的架构
Nginx的源码主要分布在src/目录下,而src/目录下主要包含三部分比较重要的模块。
core:包含了Nginx的最基础的库和框架。包括了内存池、链表、hashmap、String等常用的数据结构。
event:事件模块。Nginx自己实现了事件模型。而我们所熟悉的Memcached是使用了Libevent的事件库。自己实现event会性能和效率方便更加高效。
http:实现HTTP的模块。实现了HTTP的具体协议的各种模块,该部分内容量比较大。
…………
Nginx的基础数据结构
数据结构定义
内存池
ngx_pool_t 内存池主结构
点击查看代码
/**
* Nginx 内存池数据结构
*/
struct ngx_pool_s {
ngx_pool_data_t d; /* 内存池的数据区域*/
size_t max; /* 最大每次可分配内存 */
ngx_pool_t *current; /* 指向当前的内存池指针地址。ngx_pool_t链表上最后一个缓存池结构*/
ngx_chain_t *chain; /* 缓冲区链表 */
ngx_pool_large_t *large; /* 存储大数据的链表 */
ngx_pool_cleanup_t *cleanup; /* 可自定义回调函数,清除内存块分配的内存 */
ngx_log_t *log; /* 日志 */
};
ngx_pool_data_t 数据区域结构
点击查看代码
typedef struct {
u_char *last; /* 内存池中未使用内存的开始节点地址 */
u_char *end; /* 内存池的结束地址 */
ngx_pool_t *next; /* 指向下一个内存池 */
ngx_uint_t failed;/* 失败次数 */
} ngx_pool_data_t;
ngx_pool_large_t 大数据块结构
点击查看代码
struct ngx_pool_large_s {
ngx_pool_large_t *next; /* 指向下一个存储地址 通过这个地址可以知道当前块长度 */
void *alloc; /* 数据块指针地址 */
};
ngx_pool_cleanup_t 自定义清理回调的数据结构
点击查看代码
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler; /* 清理的回调函数 */
void *data; /* 指向存储的数据地址 */
ngx_pool_cleanup_t *next; /* 下一个ngx_pool_cleanup_t */
};
数组
ngx_array_t 数组的基础数据结构
点击查看代码
/* 数组Array数据结构 */
typedef struct {
void *elts; /* 指向数组第一个元素指针*/
ngx_uint_t nelts; /* 未使用元素的索引*/
size_t size; /* 每个元素的大小,元素大小固定*/
ngx_uint_t nalloc; /* 分配多少个元素 */
ngx_pool_t *pool; /* 内存池*/
} ngx_array_t;
*elts:指向数组的第一个元素的指针地址
nelts:未使用的元素的计数器
size:每个元素的大小,元素大小是固定的。
nalloc:一共分配了多少个元素。如果元素不够用,Nginx会数组会进行自动扩容
pool:数组的数据结构ngx_array_t和元素所需要的内存都会分配在pool内存池上。
缓冲区结构
Nginx的buf缓冲区数据结构,主要用来存储非常大块的内存。ngx_buf_t数据结构也贯穿了整个Nginx。
Nginx的缓冲区设计是比较灵活的,可以自定义管理业务层面的缓冲区链表,也可以将空闲的缓冲区链表交还给内存池pool->chain结构。
缓冲区ngx_buf_t是nginx处理大数据的关键数据结构,它既应用于内存数据也应用于磁盘数据。
缓冲区内存块的数据结构 ngx_buf_t
点击查看代码
typedef struct ngx_buf_s ngx_buf_t;
/**
* Nginx缓冲区
*/
struct ngx_buf_s {
u_char *pos; /* 待处理数据的开始标记 */
u_char *last; /* 待处理数据的结尾标记 */
off_t file_pos; /* 处理文件时,待处理的文件开始标记 */
off_t file_last; /* 处理文件时,待处理的文件结尾标记 */
u_char *start; /* 缓冲区开始的指针地址 */
u_char *end; /* 缓冲区结尾的指针地址 */
ngx_buf_tag_t tag; /* 缓冲区标记地址,是一个void类型的指针。 */
ngx_file_t *file; /* 引用的文件 */
ngx_buf_t *shadow;
/* the buf's content could be changed */
unsigned temporary:1; /* 标志位,为1时,内存可修改 */
/*
* the buf's content is in a memory cache or in a read only memory
* and must not be changed
*/
unsigned memory:1; /* 标志位,为1时,内存只读 */
/* the buf's content is mmap()ed and must not be changed */
unsigned mmap:1; /* 标志位,为1时,mmap映射过来的内存,不可修改 */
unsigned recycled:1; /* 标志位,为1时,可回收 */
unsigned in_file:1; /* 标志位,为1时,表示处理的是文件 */
unsigned flush:1; /* 标志位,为1时,表示需要进行flush操作 */
unsigned sync:1; /* 标志位,为1时,表示可以进行同步操作,容易引起堵塞 */
unsigned last_buf:1; /* 标志位,为1时,表示为缓冲区链表ngx_chain_t上的最后一块待处理缓冲区 */
unsigned last_in_chain:1;/* 标志位,为1时,表示为缓冲区链表ngx_chain_t上的最后一块缓冲区 */
unsigned last_shadow:1; /* 标志位,为1时,表示是否是最后一个影子缓冲区 */
unsigned temp_file:1; /* 标志位,为1时,表示当前缓冲区是否属于临时文件 */
/* STUB */ int num;
};
缓冲区链表结构 ngx_chain_t
点击查看代码
typedef struct ngx_chain_s ngx_chain_t;
/**
* 缓冲区链表结构,放在pool内存池上面
*/
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
双向链表
Nginx的链表结构非常小巧和简单。设计的非常精巧。
通过链表的简单和精巧的设计,让Nginx的链表的数据结构和具体业务依赖进行了解耦。
链表数据结构 ngx_queue_t
点击查看代码
typedef struct ngx_queue_s ngx_queue_t;
/**
* 链表的数据结构非常简单,ngx_queue_s会挂载到实体
* 结构上。然后通过ngx_queue_s来做成链表
*/
struct ngx_queue_s {
ngx_queue_t *prev;
ngx_queue_t *next;
};
使用举例
点击查看代码
/**
* 该结构体用于描述一个网络连接
*/
struct ngx_connection_s {
void *data; //连接未使用时,data用于充当连接池中空闲链表中的next指针。连接使用时由模块而定,HTTP中,data指向ngx_http_request_t
ngx_event_t *read; //连接对应的读事件
ngx_event_t *write; //连接对应的写事件
ngx_socket_t fd; //套接字句柄
ngx_recv_pt recv; //直接接受网络字节流
ngx_send_pt send; //直接发送网络字节流
ngx_recv_chain_pt recv_chain; //网络字节流接收链表
ngx_send_chain_pt send_chain; //网络字节流发送链表
/*用来将当前连接以双向链表元素的形式添加到ngx_cycle_t核心结构体
* 的reuseable_connection_queue双向链表中,表示可以重用的连接*/
ngx_queue_t queue;
/* 省去部分 */
};
单向链表
Nginx的list单向链表的结构和Nginx的数组结构Array有点类似,总体来说,数据结构也是非常简单清晰的。
Nginx的单向链表也是固定了每个元素的大小,并且用单向链表的方式连接。
gx_list_part_t 链表节点定义
点击查看代码
typedef struct ngx_list_part_s ngx_list_part_t;
/**
* 链表节点 每个节点大小 = size * nelts
* 节点元素用完后,每次就会分配一个新的节点
*/
struct ngx_list_part_s {
void *elts; /* 节点的内存起始位置 */
ngx_uint_t nelts; /* 已经使用的元素*/
ngx_list_part_t *next; /* 指向下一个链表节点*/
};
ngx_list_t 链表结构
点击查看代码
/**
* 链表结构
*/
typedef struct {
ngx_list_part_t *last; /* 指向最新的链表节点*/
ngx_list_part_t part; /* 第一个链表节点*/
size_t size; /* 这个链表默认的每个元素大小 */
ngx_uint_t nalloc; /* 每个节点part 可以支持多少个元素*/
ngx_pool_t *pool; /* 线程池*/
} ngx_list_t;
Hash表结构
Nginx的hash模块包含了对内存利用最大化、CPU利用最大化的很多设计细节,非常值得推荐和学习。
Nginx的hash表结构主要几个特点:
- 静态只读。当初始化生成hash表结构后,是不能动态修改这个hash表结构的内容。
- 将内存利用最大化。Nginx的hash表,将内存利用率发挥到了极致,并且很多设计上面都是可以供我们学习和参考的。
- 查询速度快。Nginx的hash表做了内存对齐等优化。
- 主要解析配置数据。
数据结构定义
点击查看代码
ngx_hash_elt_t hash表的元素结构
/**
* 存储hash的元素
*/
typedef struct {
void *value; /* 指向value的指针 */
u_short len; /* key的长度 */
u_char name[1]; /* 指向key的第一个地址,key长度为变长(设计上的亮点)*/
} ngx_hash_elt_t;
ngx_hash_t hash表结构
点击查看代码
/**
* Hash的桶
*/
typedef struct {
ngx_hash_elt_t **buckets; /* hash表的桶指针地址值 */
ngx_uint_t size; /* hash表的桶的个数*/
} ngx_hash_t;
ngx_hash_init_t hash表初始化结构
点击查看代码
/**
* hash表主体结构
*/
typedef struct {
ngx_hash_t *hash; /* 指向hash数组结构 */
ngx_hash_key_pt key; /* 计算key散列的方法 */
ngx_uint_t max_size; /* 最大多少个 */
ngx_uint_t bucket_size; /* 桶的存储空间大小 */
char *name; /* hash表名称 */
ngx_pool_t *pool; /* 内存池 */
ngx_pool_t *temp_pool; /* 临时内存池*/
} ngx_hash_init_t;
字符串结构
ngx_str_t 字符串结构
点击查看代码
/**
* 字符串结构
*/
typedef struct {
size_t len; //字符串长度
u_char *data; //具体的指针地址
} ngx_str_t;
ngx_keyval_t 字符串K V结构
点击查看代码
/**
* 字符串的K V结构
*/
typedef struct {
ngx_str_t key;
ngx_str_t value;
} ngx_keyval_t;