nginx 命令解析

阅读了一段时间的nginx,中间也有很多坑,把自己阅读得到的东西写出来也算是一个笔记吧。

命令解析

    - nginx的命令解析是通过ngx_conf_s来扫描文件然后通过匹配命令来配合ngx_command_s来实现的。我们先来看一下ngx_commands;

ngx_command_s

struct ngx_command_s {
    ngx_str_t             name;//command 名字
    ngx_uint_t            type;//command 类型 接受参数的个数
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); //设置数据
    ngx_uint_t            conf;//指定当前配置项存储的内存位置 和后面读取对应
    ngx_uint_t            offset;//存放信息的offset
    void                 *post;//一个指针 可以指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理
};

说明

其实可以发现 command命令的数据结构特别简单,一般来说都是用来保存配置文件里面的信息
  1. name: 就是是命令的名字,也是写在配置文件中的命令,ngx_conf_s 读取完成之后就是靠这个来找到正确的命令回调函数
  2. type: 这个是代表命令的类型和接受参数的个数。(位置就是在哪个括号的下面 参数个数就是后面有多少个值)
  3. set:回调函数 cf:就是解析器, cmd 当前的命令,conf 当前cmd需要的配置(conf的获得与当前命令的类型息息相关,后面会有)
  4. conf :当前配置项存储的内存位置,因为在nginx的规则里,当前cmd的conf是保存在他包含他的模块里面(main特殊点)所以当一个配置里面有很多小项的时候,允许进行二次偏移。
  5. offset: 一般来说是使用nginx的内置读取函数的时候会用到,直接定位到信息存储的位置
  6. post: 一般在自定义函数中使用,比如保存一个函数指针来对快要过时的命令进行警告

#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL } 构造一个空的command 一般用于结束标记

type含有的值:
  1. NGX_DIRECT_CONF 单纯用来指定配置存储区的寻址方法,只用于core模块
  2. NGX_MAIN_CONF 有两重含义,其一是指定指令的使用上下文是main(其实还是指core模块),其二是指定配置存储区的寻址方法
  3. NGX_ANY_CONF 哪里都行
  4. NGX_CONF_BLOCK 代表着是一个块 就是{}包着
  5. 一些模块内的比如NGX_EVENT_CONF 都是在块解析的时候改变模块信息
  6. 接受参数的个数 NGX_CONF_NOARGS代表后面没有参数,NGX_CONF_MAX_ARGS 参数的最大个数也就是八个 NGX_CONF_TAKE1 .... NGX_CONF_TAKE7分布代表一到七个参数。还支持组合(NGX_CONF_TAKE1|NGX_CONF_TAKE7)表示接受一个或七个参数

ngx_conf_s

struct ngx_conf_s {
    char                 *name;
    ngx_array_t          *args;//解析一行得到的东西
    ngx_cycle_t          *cycle;
    ngx_pool_t           *pool;
    ngx_pool_t           *temp_pool;
    ngx_conf_file_t      *conf_file;//文件
    ngx_log_t            *log;
    void                 *ctx;//当前的主ctx信息
    ngx_uint_t            module_type;//模块的类型
    ngx_uint_t            cmd_type;//当前是哪类解析
    ngx_conf_handler_pt   handler;
    char                 *handler_conf;
};

解析流程

  1. 在cycle里面配置当前的解析上下文
     for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_CORE_MODULE) {//core module
            continue;
        }
        module = ngx_modules[i]->ctx; //获取上下文
        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            cycle->conf_ctx[ngx_modules[i]->index] = rv;//保存core_module的返回信息
        }
    }
    conf.ctx = cycle->conf_ctx;
    conf.cycle = cycle;
    conf.pool = pool;
    conf.log = log;
    conf.module_type = NGX_CORE_MODULE;
    conf.cmd_type = NGX_MAIN_CONF;

在解析开始的时候只允许是main的出现在解析命令中 其实这个时候nginx只对属于是ngx_core_module的模块进行了create_conf操作,也是就是只有core模块才初始化了存放配置信息的内存,也只允许是core模块的出现在main_conf

  1. 读取文件 后调用static ngx_int_t ngx_conf_read_token(ngx_conf_t *cf)来一个一个的读取命令,读取到的内容放在args里面
  2. 在不是NGX_CONF_BLOCK_START的情况下,如果配置了自定义处理的handler 就交由handler处理,否则就调用static ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)来处理.

来看下核心的处理方法

static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)//config的处理
{
    char           *rv;
    void           *conf, **confp;
    ngx_uint_t      i, found;
    ngx_str_t      *name;
    ngx_command_t  *cmd;

    name = cf->args->elts;//args的第一个字段保存的是名字信息

    found = 0;//是否找到

    for (i = 0; ngx_modules[i]; i++) {//其实nginx就是去遍历所有的模块

        cmd = ngx_modules[i]->commands;//拿到model的command
        if (cmd == NULL) {
            continue;
        }

        for ( /* void */ ; cmd->name.len; cmd++) {

            if (name->len != cmd->name.len) {
                continue;
            }

            if (ngx_strcmp(name->data, cmd->name.data) != 0) {
                continue;
            }

            found = 1;//代表找到一个名字符合的

            if (ngx_modules[i]->type != NGX_CONF_MODULE
                && ngx_modules[i]->type != cf->module_type)//对自己进行排除
            {
                continue;
            }

            /* is the directive's location right ? */

            if (!(cmd->type & cf->cmd_type)) { //判断出现的位置对不对
                continue;
            }

            if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                  "directive \"%s\" is not terminated by \";\"",
                                  name->data);
                return NGX_ERROR;
            }//对块解析进行限制

            if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "directive \"%s\" has no opening \"{\"",
                                   name->data);
                return NGX_ERROR;
            }

            /* is the directive's argument count right ? */

            if (!(cmd->type & NGX_CONF_ANY)) {//参数个数的验证

                if (cmd->type & NGX_CONF_FLAG) {

                    if (cf->args->nelts != 2) {
                        goto invalid;
                    }

                } else if (cmd->type & NGX_CONF_1MORE) {

                    if (cf->args->nelts < 2) {
                        goto invalid;
                    }

                } else if (cmd->type & NGX_CONF_2MORE) {

                    if (cf->args->nelts < 3) {
                        goto invalid;
                    }

                } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {

                    goto invalid;

                } else if (!(cmd->type & argument_number[cf->args->nelts - 1]))
                {
                    goto invalid;
                }
            }

            /* set up the directive's configuration context */

            conf = NULL;
//module->index 是代表模块位置的下标 module->ctx_index代表在他所属模块组的下标
            if (cmd->type & NGX_DIRECT_CONF) {//配置存储区寻址
                conf = ((void **) cf->ctx)[ngx_modules[i]->index];//直接取值

            } else if (cmd->type & NGX_MAIN_CONF) {
                
                conf = &(((void **) cf->ctx)[ngx_modules[i]->index]);//取地址
/*
一般来说core_moudle执行的命令都是进行的block解析,他们属于一个功能模块,如果不用的话是不需要进行初始化的,所以都是把指向他配置的指针给他,在模块真正调用的时候去设置自己的配置文件信息,然后保存回去就好
*/

            } else if (cf->ctx) {//自定义的存储区允许进行二次偏移
                confp = *(void **) ((char *) cf->ctx + cmd->conf);
   /*
    举例 :ngx_event_moudle里面
    ctx = ngx_pcalloc(cf->pool, sizeof(void *));
   *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
    cf->ctx = ctx;
    cf->module_type = NGX_EVENT_MODULE;
    cf->cmd_type = NGX_EVENT_CONF;
    这时候cmd->conf =0;
    对ctx取* 得到一个数组 长度是ngx_event_max_module 每个的内容是void *   再来取下标 就得到了具体的conf
  */
                if (confp) {//有些模块没有配置文件
                    conf = confp[ngx_modules[i]->ctx_index];//通过组下标找到位置
                }
            }

            rv = cmd->set(cf, cmd, conf);//调用配置的方法

            if (rv == NGX_CONF_OK) {
                return NGX_OK;
            }

            if (rv == NGX_CONF_ERROR) {
                return NGX_ERROR;
            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                               "\"%s\" directive %s", name->data, rv);

            return NGX_ERROR;
        }
    }

    if (found) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "\"%s\" directive is not allowed here", name->data);

        return NGX_ERROR;
    }

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "unknown directive \"%s\"", name->data);

    return NGX_ERROR;

invalid:

    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                       "invalid number of arguments in \"%s\" directive",
                       name->data);

    return NGX_ERROR;
}

一般来说block里面都是调用改变上下文后 调用ngx_conf_parse递归处理,然后处理完成之后还原到原来的状态。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容