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

  对于一个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_tlen中保存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)

       初始化,buf0

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

       接下来就是一些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_cpystrnngx_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_vslprintfngx_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 也就是nginxngx_str_t * 这个结构体的标准格式化输出过程,的代码分析图,如1所示:

解析 %V 示意图

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简单分析就到此结束。

posted @ 2012-06-13 09:01  Java研究者  阅读(6050)  评论(1编辑  收藏  举报