nginx 源码学习笔记(十三)——文件读写和配置文件读取

对于c语言来说,如果需要支持多个操作系统,就需要封装一下文件的读写。封装文件的读写还有一个益处就是能够把读写异常,读写的内存控制,日志的记录封装起来,以便于其他的模块更好的应用。文件的读写一般会封装成打开文件,关闭打开的文件,读写文件。

在nginx的源码中,文件读写主要放在core/ngx_file.c,core/ngx_file.h,src/os/unix/ngx_files.h和src/os/unix/ngx_files.c中。由于nginx的文件读写函数较多,我们只是详细介绍比较重要,经常使用,实现很有技巧的函数。

typedef struct {
    ngx_str_t                  name;             //路径数据字符串
    size_t                     len;              //目录长度
    size_t                     level[3];         //临时目录的三级目录大小

    ngx_path_manager_pt        manager;
    ngx_path_loader_pt         loader;
    void                      *data;            

    u_char                    *conf_file;        //该路径的来源的配置文件
    ngx_uint_t                 line;             //该路径在来源的配置文件中的行数,主要用于记录日志,排查错误
} ngx_path_t;


我们首先介绍简单封装的函数,再介绍复杂封装的函数,简单封装的函数有如下函数:
ngx_open_file/ngx_close_file

src/os/unix/ngx_files.h
#define ngx_open_file(name, mode, create, access)                            \
    open((const char *) name, mode|create|O_BINARY, access)

#else

#define ngx_open_file(name, mode, create, access)                            \
    open((const char *) name, mode|create, access)

#endif

#define ngx_close_file           close


这里可以看到单纯的对c语言的open和close进行了封装,是不是非常简单呢?

src/os/unix/ngx_files.h
#define ngx_delete_file(name)    unlink((const char *) name)


ngx_delete_file删除文件函数,调用系统的unlink函数,unlink()会删除pathname指定的文件。如果该文件名最后连接点,但有其他进程打开了此文件,则在所有关于此文件的文件描述词皆关闭后才会删除。如果参数pathname为一符号连接(symboliclink),则此连接会被删除。

在ngx_files.c文件中存在大量的文件读写操作,这里就不一一讲解;

配置文件一般有三个作用:

根据配置文件启用某段程序,

用配置文件传入参数,

设定启动程序程序之间的相互关系。

Nginx的配置文件也实现了这三部分的功能,nginx的配置文件主要有以下几个主要的结构体和函数配置文件的结构体有:

结构体说明
ngx_command_s           定义了配置文件中的 tag的配置,以及遇到该tag该怎么处理的函数
ngx_open_file_s            定义了打开文件的参数的结构体
ngx_conf_file_t             定义了缓存配置文件的数据的结构体
ngx_conf_s                    定义了配置文件解析过程中需要缓存的数据

ngx_command_s          这个结构体定义了配置文件中的tag,以及遇到该tag,该怎么处理,其结构如下表:

结构体说明
name 配置文件中的
tag
Type                   
配置类型,这个参数中会定义这个配置是什么范围内的配置(核心配置或是普通配置),以及有多少参数, 是块配置,还是行配置
Set                       解析该配置的函数
ConfOffset           指定配置存储的位置
Post                     指向模块在读配置的时候需要的一些零碎变量。一般它是NULL

在每一个ngx_command_s数组的结尾必须有一个ngx_null_command,以用于判断是否结束。
配置文件解析的入口为ngx_conf_parse,如果了解了ngx_conf_parse,对nginx的配置文件的原理就有了基本的了解,下面我们详细的解释一下

src/ngx_conf_file.h

char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename) 
//cf是输入参数也是输出参数,在文件名为空的情况下,第二个
//参数是文件名,可能为空,一般在解析配置块或是一行的时候。
{
    char             *rv;
    ngx_fd_t          fd;
    ngx_int_t         rc;
    ngx_buf_t         buf;
    ngx_conf_file_t  *prev, conf_file;
    enum {
        parse_file = 0,
        parse_block,
        parse_param
    } type;

#if (NGX_SUPPRESS_WARN)
    fd = NGX_INVALID_FILE;
    prev = NULL;
#endif

    if (filename) { //如果文件名不为空 则打开配置文件

        /* open configuration file */

        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);//打开配置文件
        if (fd == NGX_INVALID_FILE) {
            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,
                               ngx_open_file_n " \"%s\" failed",
                               filename->data);
            return NGX_CONF_ERROR;
        }

        prev = cf->conf_file;

        cf->conf_file = &conf_file;

        if (ngx_fd_info(fd, &cf->conf_file->file.info) == -1) {
            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,
                          ngx_fd_info_n " \"%s\" failed", filename->data);
        }

        cf->conf_file->buffer = &buf;

        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);
        if (buf.start == NULL) {
            goto failed;
        }

        buf.pos = buf.start;
        buf.last = buf.start;
        buf.end = buf.last + NGX_CONF_BUFFER;
        buf.temporary = 1;

        cf->conf_file->file.fd = fd;
        cf->conf_file->file.name.len = filename->len;
        cf->conf_file->file.name.data = filename->data;
        cf->conf_file->file.offset = 0;
        cf->conf_file->file.log = cf->log;
        cf->conf_file->line = 1;

        type = parse_file;

    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {

        type = parse_block;

    } else {
        type = parse_param;
    }


    for ( ;; ) {
        rc = ngx_conf_read_token(cf);    
       //ngx_conf_read_token函数的主要功能是读取并解析配置文件,
        //解析的参数存到cf->args中,读取到";"标点则返回
        //读取到"{",则接着读取,读取到"}",这直接完成。

        /*
         * ngx_conf_read_token() may return
         *
         *    NGX_ERROR             there is error
         *    NGX_OK                the token terminated by ";" was found
         *    NGX_CONF_BLOCK_START  the token terminated by "{" was found
         *    NGX_CONF_BLOCK_DONE   the "}" was found
         *    NGX_CONF_FILE_DONE    the configuration file is done
         */

        if (rc == NGX_ERROR) {
            goto done;                    //如果错误 结束
        }

        if (rc == NGX_CONF_BLOCK_DONE) {   //如果发现“}”

            if (type != parse_block) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unexpected \"}\"");
                goto failed;
            }

            goto done;
        }

        if (rc == NGX_CONF_FILE_DONE) {     //如果发现文件结束

            if (type == parse_block) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "unexpected end of file, expecting \"}\"");
                goto failed;
            }

            goto done;
        }

        if (rc == NGX_CONF_BLOCK_START) {   //如果发现“{”

            if (type == parse_param) {
                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                                   "block directives are not supported "
                                   "in -g option");
                goto failed;
            }
        }

        /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */

        if (cf->handler) {  //这个函数只在模块的时候使用type

            /*
             * the custom handler, i.e., that is used in the http's
             * "types { ... }" directive
             */

            rv = (*cf->handler)(cf, NULL, cf->handler_conf); 
            if (rv == NGX_CONF_OK) {
                continue;
            }

            if (rv == NGX_CONF_ERROR) {
                goto failed;
            }

            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, rv);

            goto failed;
        }


        rc = ngx_conf_handler(cf, rc);
        //ngx_conf_handler函数主要功能是先验证配置有没有问题,然后调用cmd->set函数
        if (rc == NGX_ERROR) {
            goto failed;
        }
    }

failed:

    rc = NGX_ERROR;

done:

    if (filename) {
        if (cf->conf_file->buffer->start) {
            ngx_free(cf->conf_file->buffer->start);
        }

        if (ngx_close_file(fd) == NGX_FILE_ERROR) { //关闭文件
            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,
                          ngx_close_file_n " %s failed",
                          filename->data);
            return NGX_CONF_ERROR;
        }

        cf->conf_file = prev;
    }

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

    return NGX_CONF_OK;
}



ngx_conf_parse函数除了在ngx_init_cycle函数中调用外,在配置块的解析中,即cmd->set函数(例如解析http配置块的ngx_http_block函数)中也会调用该函数,从某种程度上理解该函数其实是一个递归函数,只不过中间调用了其他函数。在讲解http的配置文件那一章我们会根据http的配置例子详细的讲解。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值