您的位置:首页 > 运维架构 > Nginx

Nginx 源码分析-- ngx_string 的一些简单分析

2012-06-13 09:01 239 查看
  对于一个web服务器程序来说,对字符串处理的需求是必须的。由于web环境下的各种编码,也导致了web服务器程序字符串处理的繁杂性。在nginx源码中,ngx_string.c 这个文件就是来应对字符串处理的一些源码,源码中经常的使用到了这里中的函数,本文对ngx_string.c 进行一些简单的分析,以方便阅读其他源码。

  我们来看它的基本数据结构:

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


比较明显的可以看出,ngx_str_t 只是将字符串添加了一个标志长度的字段,并无其他特殊结构。

  再来看它的功能函数的特点,和前面分析内存池(pool)中功能管理函数一样许多函数直接以宏定义的形式给出,如:

#define ngx_string(str) { sizeof(str) - 1, (u_char *) str }


初始化ngx_str_t,len中保存str字符串的出去结束符的长度,data中保存str字符串。

#define ngx_tolower(c) (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)


如果字符c是大写字符就将其转为小写,直接用位操作进行。(为什么 |0x20 就能实现?有兴趣的把大写字符用二进制表示,而后试试就明白了)

#define ngx_strcmp(s1, s2) strcmp((const char *) s1, (const char *) s2)


比较,s1 和 s2 两个字符串,实质就是调用了strcmp。

#define ngx_memzero(buf, n) (void) memset(buf, 0, n)


初始化,buf为0。

等等,这样的一些宏定义函数,在这里就不一一详细说明了,都比较简单容易看懂。

接下来就是一些nginx自己编写的函数了,如:

void  ngx_strlow(u_char *dst, u_char *src, size_t n)
{
while (n) {
*dst = ngx_tolower(*src);
dst++;
src++;
n--;
}
}


将字符串src中前n个字符全部变成小写,放在dst中,调用的 ngx_tolower 就是前面介绍了的宏定义的函数,想想如果dst=src 呢?把本身的字符串中前n个变成小写。还有ngx_cpystrn、ngx_pstrdup等函数,这部分函数代码结构比较清晰易懂,不详细一一说明了。

再来看,这样的一组函数:

ngx_sprintf(u_char *buf, const char *fmt, …)

ngx_snprintf(u_char *buf, size_t max, const char *fmt, …)

ngx_slprintf(u_char *buf, u_char *last, const char *fmt, …)

ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)

从命名来看,就能略知一二,这些函数是对字符串进行特定的标准格式的输出到buf中。取ngx_sprintf(u_char *buf, const char *fmt, …)来瞧一瞧:

u_char * ngx_cdecl
ngx_sprintf(u_char *buf, const char *fmt, ...)
{
u_char   *p;
va_list   args;

va_start(args, fmt);
p = ngx_vslprintf(buf, (void *) -1, fmt, args);
va_end(args);

return p;
}


这是C语言标准的不定参数的写法:

va_list args;

va_start(args, fmt);

p = ngx_vslprintf(buf, (void *) -1, fmt, args);

va_end(args);

可能有不是很了解这种写法结构的,特别做下说明。va_start 是用来获取不定参数,实际上就是获取不定参数内存起始地址 放在va_list中,它的第一个参数为 va_list 保存地址,第二个参数为 最后一个确定参数名,具体到ngx_sprintf 这个函数就是,两个确定参数u_char *buf, const char *fmt,所以最后一个就是fmt。接下来将获取的va_list 和一些参数传给 ngx_vslprintf。值得注意下的是 (void *) -1 这种写法,指的是将整数-1 转化为空指针地址,实际上就是 0xFFFFFFFF(对于32位机)这个 参数干嘛什么用呢?这个得看 ngx_vslprintf。 ngx_vslprintf的代码就比较长了,但是功能很明确就是对于nginx自定义的数据结构进行标准格式化输出,就像vprintf 一样。而ngx_sprintf就相当于sprintf 命名应该也是这么来的。源码中的这段注释也是很明细的告诉我们这点:

/*
* supported formats:
*    %[0][width][x][X]O        off_t
*    %[0][width]T              time_t
*    %[0][width][u][x|X]z      ssize_t/size_t
*    %[0][width][u][x|X]d      int/u_int
*    %[0][width][u][x|X]l      long
*    %[0][width|m][u][x|X]i    ngx_int_t/ngx_uint_t
*    %[0][width][u][x|X]D      int32_t/uint32_t
*    %[0][width][u][x|X]L      int64_t/uint64_t
*    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t
*    %[0][width][.width]f      double, max valid number fits to %18.15f
*    %P                        ngx_pid_t
*    %M                        ngx_msec_t
*    %r                        rlim_t
*    %p                        void *
*    %V                        ngx_str_t *
*    %v                        ngx_variable_value_t *
*    %s                        null-terminated string
*    %*s                       length and string
*    %Z                        '\0'
*    %N                        '\n'
*    %c                        char
*    %%                        %
*
*  reserved:
*    %t                        ptrdiff_t
*    %S                        null-terminated wchar string
*    %C                        wchar
*/


那么参数 0xFFFFFFFF 功能呢?来截取部分ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args) 代码来看,看到开头的这句:while (*fmt && buf < last)。明白了吧last指的就是格式后输出到buf中的尾部界定指针,那么如果last= 0xFFFFFFFF 即代表了 buf < last 永远成立,也就是说将fmt 的所有内容格式化后输出的buf中去。看完这里我们也可以知道ngx_snprintf(u_char *buf, size_t max, const char *fmt, …),ngx_slprintf(u_char *buf, u_char *last, const char *fmt, …)这两个函数的功用了,一个是给定buf的最大值,一个是给定buf的界定。为了更为明晰的理解,ngx_vslprintf 这个函数,我们给出解析 %V 也就是nginx中ngx_str_t * 这个结构体的标准格式化输出过程,的代码分析图,如图1所示:



图1 解析 %V 示意图

  而在 case V 中我们可以看到,获取不定参数中ngx_str_t 的指针,代码为va_arg(args,ngx_str_t),然后确定进入buf的长度,拷贝ngx_str_t 中data的字符串,最后完成%V的格式化,继续下面的字符的分析。其他的格式化也是相似的过程,不过由于各个的特殊性而有所不同。

  ngx_string.c中还包括了对uft8 编码、urf的解析等操作,涉及到编码的规则和一些相关标准,其实现的功能一般都能从其函数命名中得知,源码中遇到也能理解一二,本篇对于ngx_string.c简单分析就到此结束。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: