Nginx学习笔记(四):基本数据结构 – 陈心朔

目录

Nginx 的一些特性 Nginx 自定义整数类型

跨平台兼容性

// 定义在 core/ngx_config.h
typedef intptr_t 	ngx_int_t;		// 有符号整数
typedef uintptr_t	ngx_uint_t;		// 无符号整数
typedef intptr_t	ngx_flag_t;		// 标志整数类型

// 定义在 core/ngx_rbtree.h 红黑树的键(key)类型
typedef ngx_uint_t	ngx_rbtree_key_t;
typedef ngx_int_t	ngx_rbtree_key_int_t;
// 定义在 os/unix/ngx_time.h 毫秒的整数类型
typedef ngx_rbtree_key_t	ngx_msec_t;
typedef ngx_rbtree_key_int_t	ngx_msec_int_t;

无效值:

在Nginx中,定义了类似的None,初始化变量为UNSET = -1表示未初始化

由于C/C++是强类型语言,Nginx为-1定义了不同的类型转换宏

// 定义在 core/ngx_conf_file.h
#define NGX_CONF_UNSET							-1	// 通用无效值
#define NGX_CONF_UNSET_UINT		(ngx_uint_t)	-1	// 无符号整数的无效值
#define NGX_CONF_UNSET_PTR		(void *)		-1	// 指针类型的无效值
...

借助UNSET概念,Nginx以宏的形式提供初始化和条件赋值函数:

// 定义在 core/ngx_conf_file.h
#define ngx_conf_init_value(conf, default)		
	if(conf == NGX_CONF_UNSET)					
	{											
		conf = default;							
	}

conf未初始化时,初始化为默认值

异常机制错误处理

将代码逻辑中的正常部分和异常部分分开,让代码结构更清晰

Nginx用宏定义了七种常见的错误码,类型为

// 定义在 core/ngx_core.h
#define NGX_OK			0 	// 执行成功,无错误

#define NGX_ERROR	   -1	// 执行失败,最常见的错误
#define NGX_AGAIN	   -2	// 未准备好,需要重试
#define NGX_BUSY	   -3	// 后端服务正忙
#define NGX_DONE	   -4	// 执行成功,但还需要有后序操作
#define NGX_DECLINED   -5	// 执行成功,但未做处理
#define NGX_ABORT	   -6	// 发生了严重的错误

内存池

减少系统调用次数,避免内存碎片和泄漏

// 定义在 ngx_core.h
typedef struct ngx_pool_s	ngx_pool_t;		// 简化定义
// 定义在 ngx_palloc.h
struct ngx_pool_s
{
	...
	ngx_pool_cleanup_t	*cleanup;			// 析构时的清理动作
	ngx_log_t			*log;				// 关联的日志对象
}

Nginx 会为每个 TCP/HTTP 请求创建一个单独的内存池—即对象

请求结束时自动销毁对象,释放内存池及其持有的所有内存

// 使用了内存对齐,速度快,但可能会有少量内存浪费
void * ngx_palloc(ngx_pool_t  * pool, size_t size);
// 没有使用内存对齐
void * ngx_pnalloc(ngx_pool_t * pool, size_t size);
// 内部调用了 ngx_palloc(),并且把内存块清零
void * ngx_pcalloc(ngx_pool_t * pool, size_t size);
// 释放内存
ngx_int_t ngx_free(ngx_pool_t * pool, void * p);

清理机制:

Nginx 框架自动管理内存池的生命周期。当请求结束时,内存池中的内存将全部归还给系统;

内存只是系统资源的一方面,其他系统资源(如文件句柄)不会随着内存池的销毁而被释放;

如果不做特殊操作,可能会导致资源泄露。

这种清理机制是C++中的析构函数思想,当对象被销毁时会自动调用析构函数

Nginx 定义了一个结构来保存清理信息,以便在内存被破坏时执行清理操作

// 定义在 ngx_palloc.h
typedef void (*ngx_pool_cleanup_t) void * data;		// 清理函数指针原型

struct ngx_pool_cleanup_s							// 清理信息结构体
{
	ngx_pool_cleanup_pt		handler;				// 清理动作,函数指针
	void 				   	*data;					// 清理所需数据
	ngx_pool_cleanup_t		*next;					// 后续链表指针
};
// 定义在 ngx_palloc.h
ngx_pool_cleanup_pt * ngx_pool_cleanup_add(ngx_pool_t * p, size_t size);

此函数使用 size 为 ::data 分配内存并返回一个清理信息对象,

通过设置其和数据,可以达到将清理功能“注册”到内存池的目的

std:: 有两个模板参数:

template<class T,								// 容纳的元素类型 
		class Alloctor = std::allocator		// 内存配置器
		> class vector

我们可以自定义内存配置器替换第二个参数,可以使用Nginx的内存池

字符串

不是传统意义上的字符串,准确来说应该是内存块引用

// 定义在 ngx_string.h
typedef struct
{
	size_t 		len;	// 字符串长度
	u_char * 	data;	// 字符串所在地址
}ngx_str_t;

这种设计的好处是对字符串的操作非常便宜,只需要两个整数的开销,

不需要复制大量的数据,所以复制修改效率很高,而且还节省内存使用

(类似于 boost:: 或 std:: C++17)

缺点也很明显。由于只指内存,所以尽量以只读方式使用

多个共享一块内存,未经授权的修改会影响其他引用

可能同时引用的内存地址无效,访问了错误的内存区域

初始化和赋值:

#define ngx_string(str)		{sizeof(str) - 1, (u_char *) str}
#define ngx_null_string		{0, NULL}
#define ngx_str_set(str, text)	

...
#define ngx_str_null(str)		
...

基本操作:

#define ngx_strcmp(s1, s2)		// 大小写敏感比较,参数是 u_char*
#define ngx_strncmp(s1, s2, n)	// 大小写敏感比较,有长度参数
#define ngx_strstr(s1, s2)		// 查找子串
#define ngx_strlen(s)			// 使用''计算字符串长度
// 大小写不敏感字符串比较,参数是 u_char*
ngx_int_t ngx_strcasecmp(u_char * s1, u_char * s2);
ngx_int_t ngx_strncasecmp(u_char * s1, u_char * s2, size_t n);
// 字符串转整数类型,参数是 u_char*
ngx_int_t ngx_atoi(u_char * line, size_t n);
// 内存池复制字符串,参数是 ngx_str_t*
u_char * ngx_pstrdup(ngx_pool_t * pool, ngx_str_t * src);

格式化函数:

// 直接向 buf 输出格式化内容,不检查缓冲区的有效性
u_char * ngx_sprintf(u_char * buf, const char * fmt, ...);
// 参数 max 和 last 指明了缓冲区结束位置
u_char * ngx_snprintf(u_char * buf, size_t max, const char * fmt, ...);
u_char * ngx_slprintf(u_char * buf, u_char * last, const char * fmt, ...);

函数执行后返回*指针,表示格式化输出后在buf中的结束位置,可用于判断结果的长度

时间和日期

Nginx 定义了一个专用的时间数据结构:

// 定义在 core/ngx_times.h
typedef struct
{
	time_t		sec;		// 自 epoch 以来的秒数,即时间戳
	ngx_uint_t	msec;		// 秒数后的小数部分,单位是毫秒
	ngx_int_t	gmtoff;		// GMT时区偏移量
} ngx_time_t;

Nginx 内部使用了cache机制来存放时间值,使用一个全局指针 ngx_cached_time 指示当前缓存的时间
volatile ngx_time_t * ngx_cached_time;		// 当前缓存的时间
// 获取当前时间的秒数
#define ngx_time()		ngx_cached_time->sec
// 获取完整的时间结构体
#define timeofday()		(ngx_time_t *)ngx_cached_time
// 强制更新缓存时间(需要使用锁,成本较高,当必须获得当前精确时间时调用)
void ngx_time_update(void)

日期结构,即标准C结构中的tm

// 定义在 os/unix/ngx_time.h
typedef struct tm 	ngx_tm_t;

日期操作函数

// 定义在 core/ngx_times.h
void ngx_gmtime()time_t t, ngx_tm_t * tp);
void ngx_localtime(time_t t, ngx_tm_t * tp);

转换为 GMT/当地时间

u_char * ngx_http_time(u_char * buf, time_t t);
u_char * ngx_http_cookie_time(u_char * buf, time_t t);

以上两个函数调用()和()转换为日期字符串

// 定义在 core/ngx_parse_time.h
time_t ngx_parse_http_time(u_char * value, size_t len);

以字符串形式解析日期时间,转换为

同时,Nginx 使用全局变量提供缓存的日期字符串,以降低频繁调用的成本:

ngx_str_t ngx_cached_err_log_time;		// 错误日志的日期字符串
ngx_str_t ngx_cached_http_time;			// HTTP格式的日期字符串
ngx_str_t ngx_cached_http_log_time;		// HTTP日志的日期字符串
ngx_str_t ngx_cached_http_log_iso8601;	// ISO8601 格式日期字符串
ngx_str_t ngx_cached_syslog_time;		// 系统日志格式日期字符串

运行日志

Nginx 使用结构体表示运行日志

图片[1]-Nginx学习笔记(四):基本数据结构 – 陈心朔-唐朝资源网

// 定义在 core/ngx_log.h
struct ngx_log_s
{
	...
	ngx_uint_t 		log_level;		// 日志级别
	ngx_log_t * 	next;			// 日志对象链表指针
};
// 定义在 core/ngx_log.h
void ngx_log_error_core(ngx_uint_t level, ngx_log_t * log, ngx_err_t err,
						const char * fmt, ...);

使用对象记录级别级别的日志,字符串消息的格式语法同()

日志级别参数level取决于以下宏,对应配置文件中的宏

调试 |信息 | 警告 |错误 |暴击 |警报 |出现

#define NGX_LOG_STDERR		0	// 最高级别
#define NGX_LOG_EMERG		1
#define NGX_LOG_ALERT		2
#define NGX_LOG_CRIT		3
#define NGX_LOG_ERR			4	// 常用级别
#define NGX_LOG_WARN		5
#define NGX_LOG_NOTICE		6
#define NGX_LOG_INFO		7
#define NGX_LOG_DEBUG		8	// 最低级别

是比emerg更高的错误级别,如果这个级别用于日志记录,

然后 Nginx 将直接记录到标准错误输出(通常是终端屏幕)而不是写入日志文件。

常见的日志级别是和

err参数表示调用失败返回的错误码

// 定义在 os/unix/errno.h
typedef int 	ngx_err_t;

日志宏

#define ngx_log_error(level, log, ...)		
...

只有当消息的日志级别高于日志对象级别(即消息的级别值较小)时,才会调用该函数记录日志

C++ 包实现:

© 版权声明
THE END
喜欢就支持一下吧
点赞226赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容