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

  • 相关阅读:
    PID算法控制直流电机笔记
    HAL库直流电机编码测速(L298N驱动)笔记
    HAl库控制L298N直流电机旋转笔记
    MQTT介绍与使用(转载)
    STM32中STD、HAL、LL库比较
    rpm检验是否被改动过
    find命令 参数
    会话管理测试时的注意点
    无权限修改用户组权限的状况一
    burpsuite + sqlmap 日志导出批量扫描
  • 原文地址:https://www.cnblogs.com/jzhlin/p/nginx_string.html
Copyright © 2020-2023  润新知