• Nginx(四):http服务器静态文件查找的实现


      上一篇nginx的文章中,我们理解了整个http正向代理的运行流程原理,主要就是事件机制接入,header解析,body解析,然后遍历各种checker,直到处理成功为止。

      我们以访问一个普通文件为例,nginx到底是如何找到这个文件并返回信息的呢?它会不会有些什么限制呢?

      按我们自己的理解,它应该是uri栏里截取出相应的路径,然后对应到root下,查找到相应文件,返回即可。那么,它又是如何处理html和其他的各种不同格式的文件呢?

      就让我们一起来探秘nginx文件的查找实现吧!

    0. nginx 静态文件配置

      要配置静态文件处理,只需在http server中配置root路径即可。(当然了,你可以根据前缀配置许多不同的root) 

    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
        #                  '$status $body_bytes_sent "$http_referer" '
        #                  '"$http_user_agent" "$http_x_forwarded_for"';
    
        #access_log  logs/access.log  main;
    
        sendfile        on;
        #tcp_nopush     on;
    
        #keepalive_timeout  0;
        keepalive_timeout  65;
    
        #gzip  on;
    
        server {
            listen       8085;
            server_name  localhost;
    
            #charset koi8-r;
    
            #access_log  logs/host.access.log  main;
            
            location /hello {
                root   /www/hello;
                index  index.html index.htm;
            }
    
            location / {
                root   html;
                index  index.html index.htm;
            }
        }
        # 后续可以添加无数个server 扩展
    }

      配置简单吧,实际核心就两三行代码搞定:监听端口号 listen、访问域名 server_name、服务器根路径 root。明显这是nginx成功的原因之一。

      本文要讨论的场景是,如果我访问 http://localhost:8085/1.txt?d=xxx, nginx将如何干成这件事?

    1. checker的遍历回顾

      我们先来回顾下,nginx是如何遍历各个checker的吧!

    // http/ngx_http_core_module.c
    // 响应客户端操作, 多阶段式操作
    void
    ngx_http_core_run_phases(ngx_http_request_t *r)
    {
        ngx_int_t                   rc;
        ngx_http_phase_handler_t   *ph;
        ngx_http_core_main_conf_t  *cmcf;
    
        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);
    
        ph = cmcf->phase_engine.handlers;
        // 依次调用各 checker, 直到有响应 OK 的checker为止
        while (ph[r->phase_handler].checker) {
            // 每次调用 checker 之后, 内部都会将 r->phase_handler++, 即迭代下一个
            // 此处的 checker 非常之多, 是在各模块启动时, 自动向 ngx_http_core_module.main_conf 中进行注册的
            /** 
             * 定义如下:
            typedef enum {
                NGX_HTTP_POST_READ_PHASE = 0,
    
                NGX_HTTP_SERVER_REWRITE_PHASE,
    
                NGX_HTTP_FIND_CONFIG_PHASE,
                NGX_HTTP_REWRITE_PHASE,
                NGX_HTTP_POST_REWRITE_PHASE,
    
                NGX_HTTP_PREACCESS_PHASE,
    
                NGX_HTTP_ACCESS_PHASE,
                NGX_HTTP_POST_ACCESS_PHASE,
    
                NGX_HTTP_PRECONTENT_PHASE,
    
                NGX_HTTP_CONTENT_PHASE,
    
                NGX_HTTP_LOG_PHASE
            } ngx_http_phases;
    
            // 注册方式
            cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
            h = ngx_array_push(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers);
            *h = ngx_http_access_handler;
            */
            // 将请求信息和 handler 本身传入调用(不是面向, 只能这么做了)
            rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);
            // 只要有一个处理成功, 则后续不再调用
            if (rc == NGX_OK) {
                return;
            }
        }
    }

      可以说,它的各checker是非常复杂的,各模块都可以向其中注册处理器。这也是nginx灵活性的体现。不过我们不想关注太多。

      文件的查找是在 static_module 中完成的,我们只需关注这个即可。

    2. 静态文件模块的注册

      静态文件模块主要就是负责静态文件的查找处理。几乎所有的http处理模块,都是先进行注册,然后再被调用的过程。static_module 自然不例外。

    // http/modules/ngx_http_static_handler.c    
    static ngx_int_t
    ngx_http_static_init(ngx_conf_t *cf)
    {
        ngx_http_handler_pt        *h;
        ngx_http_core_main_conf_t  *cmcf;
    
        cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
        // 获取http_core_module的配置
        // 将ngx_http_static_handler 添加到 NGX_HTTP_CONTENT_PHASE 的handlers中
        h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
        if (h == NULL) {
            return NGX_ERROR;
        }
    
        *h = ngx_http_static_handler;
    
        return NGX_OK;
    }
    
    // 下面是一些nginx的模块暴露规范,只有这样,这个模块才会被接入到整个系统中
    static ngx_http_module_t  ngx_http_static_module_ctx = {
        NULL,                                  /* preconfiguration */
        ngx_http_static_init,                  /* postconfiguration */
    
        NULL,                                  /* create main configuration */
        NULL,                                  /* init main configuration */
    
        NULL,                                  /* create server configuration */
        NULL,                                  /* merge server configuration */
    
        NULL,                                  /* create location configuration */
        NULL                                   /* merge location configuration */
    };
    
    
    ngx_module_t  ngx_http_static_module = {
        NGX_MODULE_V1,
        &ngx_http_static_module_ctx,           /* module context */
        NULL,                                  /* module directives */
        NGX_HTTP_MODULE,                       /* module type */
        NULL,                                  /* init master */
        NULL,                                  /* init module */
        NULL,                                  /* init process */
        NULL,                                  /* init thread */
        NULL,                                  /* exit thread */
        NULL,                                  /* exit process */
        NULL,                                  /* exit master */
        NGX_MODULE_V1_PADDING
    };

      

    3. 文件查找实现

      经过前面的模块注册到 NGX_HTTP_CONTENT_PHASE 中,就会被nginx调用。前提是没有其他更合适的处理器的时候。而因为 static_module 是在 NGX_HTTP_CONTENT_PHASE 中,所以都会走content的处理器:

    // http/ngx_http_core_module.c    
    ngx_int_t
    ngx_http_core_content_phase(ngx_http_request_t *r,
        ngx_http_phase_handler_t *ph)
    {
        size_t     root;
        ngx_int_t  rc;
        ngx_str_t  path;
    
        if (r->content_handler) {
            r->write_event_handler = ngx_http_request_empty_handler;
            ngx_http_finalize_request(r, r->content_handler(r));
            return NGX_OK;
        }
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "content phase: %ui", r->phase_handler);
    
        rc = ph->handler(r);
        // 处理成功,则返回ok
        if (rc != NGX_DECLINED) {
            ngx_http_finalize_request(r, rc);
            return NGX_OK;
        }
    
        /* rc == NGX_DECLINED */
    
        ph++;
    
        if (ph->checker) {
            r->phase_handler++;
            return NGX_AGAIN;
        }
    
        /* no content handler was found */
    
        if (r->uri.data[r->uri.len - 1] == '/') {
    
            if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {
                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                              "directory index of "%s" is forbidden", path.data);
            }
    
            ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);
            return NGX_OK;
        }
    
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no handler found");
    
        ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);
        return NGX_OK;
    }
    
    
    // http/modules/ngx_http_static_handler.c
    static ngx_int_t
    ngx_http_static_handler(ngx_http_request_t *r)
    {
        u_char                    *last, *location;
        size_t                     root, len;
        ngx_str_t                  path;
        ngx_int_t                  rc;
        ngx_uint_t                 level;
        ngx_log_t                 *log;
        ngx_buf_t                 *b;
        ngx_chain_t                out;
        ngx_open_file_info_t       of;
        ngx_http_core_loc_conf_t  *clcf;
        // 仅支持 get/head/post 方法进行静态文件处理
        if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {
            return NGX_HTTP_NOT_ALLOWED;
        }
        // 要求uri不能以'/'结尾,否则走其他协议
        if (r->uri.data[r->uri.len - 1] == '/') {
            return NGX_DECLINED;
        }
    
        log = r->connection->log;
    
        /*
         * ngx_http_map_uri_to_path() allocates memory for terminating ''
         * so we do not need to reserve memory for '/' for possible redirect
         */
        // 解析文件路径到path中
        last = ngx_http_map_uri_to_path(r, &path, &root, 0);
        if (last == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        path.len = last - path.data;
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,
                       "http filename: "%s"", path.data);
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
        // 初始化文件实例
        ngx_memzero(&of, sizeof(ngx_open_file_info_t));
    
        of.read_ahead = clcf->read_ahead;
        of.directio = clcf->directio;
        of.valid = clcf->open_file_cache_valid;
        of.min_uses = clcf->open_file_cache_min_uses;
        of.errors = clcf->open_file_cache_errors;
        of.events = clcf->open_file_cache_events;
    
        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)
            != NGX_OK)
        {
            switch (of.err) {
    
            case 0:
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
    
            case NGX_ENOENT:
            case NGX_ENOTDIR:
            case NGX_ENAMETOOLONG:
    
                level = NGX_LOG_ERR;
                rc = NGX_HTTP_NOT_FOUND;
                break;
    
            case NGX_EACCES:
    #if (NGX_HAVE_OPENAT)
            case NGX_EMLINK:
            case NGX_ELOOP:
    #endif
    
                level = NGX_LOG_ERR;
                rc = NGX_HTTP_FORBIDDEN;
                break;
    
            default:
    
                level = NGX_LOG_CRIT;
                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
                break;
            }
    
            if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
                ngx_log_error(level, log, of.err,
                              "%s "%s" failed", of.failed, path.data);
            }
    
            return rc;
        }
    
        r->root_tested = !r->error_page;
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);
    
        if (of.is_dir) {
    
            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");
    
            ngx_http_clear_location(r);
    
            r->headers_out.location = ngx_list_push(&r->headers_out.headers);
            if (r->headers_out.location == NULL) {
                return NGX_HTTP_INTERNAL_SERVER_ERROR;
            }
    
            len = r->uri.len + 1;
    
            if (!clcf->alias && r->args.len == 0) {
                location = path.data + root;
    
                *last = '/';
    
            } else {
                if (r->args.len) {
                    len += r->args.len + 1;
                }
    
                location = ngx_pnalloc(r->pool, len);
                if (location == NULL) {
                    ngx_http_clear_location(r);
                    return NGX_HTTP_INTERNAL_SERVER_ERROR;
                }
    
                last = ngx_copy(location, r->uri.data, r->uri.len);
    
                *last = '/';
    
                if (r->args.len) {
                    *++last = '?';
                    ngx_memcpy(++last, r->args.data, r->args.len);
                }
            }
    
            r->headers_out.location->hash = 1;
            ngx_str_set(&r->headers_out.location->key, "Location");
            r->headers_out.location->value.len = len;
            r->headers_out.location->value.data = location;
    
            return NGX_HTTP_MOVED_PERMANENTLY;
        }
    
    #if !(NGX_WIN32) /* the not regular files are probably Unix specific */
    
        if (!of.is_file) {
            ngx_log_error(NGX_LOG_CRIT, log, 0,
                          ""%s" is not a regular file", path.data);
    
            return NGX_HTTP_NOT_FOUND;
        }
    
    #endif
        // 真正到内容输出的时候,post又是不被允许的方法
        if (r->method == NGX_HTTP_POST) {
            return NGX_HTTP_NOT_ALLOWED;
        }
        // 静态文件处理,将会忽略所有请求body参数
        rc = ngx_http_discard_request_body(r);
    
        if (rc != NGX_OK) {
            return rc;
        }
        // 输出文件内容到客户端
        log->action = "sending response to client";
    
        r->headers_out.status = NGX_HTTP_OK;
        r->headers_out.content_length_n = of.size;
        r->headers_out.last_modified_time = of.mtime;
        // 设置 ETag header
        if (ngx_http_set_etag(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        // content-type 设置,大概就是根据文件后缀找到相应的content-type 输出即可
        if (ngx_http_set_content_type(r) != NGX_OK) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        if (r != r->main && of.size == 0) {
            return ngx_http_send_header(r);
        }
    
        r->allow_ranges = 1;
    
        /* we need to allocate all before the header would be sent */
    
        b = ngx_calloc_buf(r->pool);
        if (b == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
    
        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));
        if (b->file == NULL) {
            return NGX_HTTP_INTERNAL_SERVER_ERROR;
        }
        // 发送头信息,如果是 head 请求,则到此为止
        // 该header响应会添加一些公共的请求头一并返回
       // 其中content-type是根据文件类型做的映射返回,如txt文件映射为 text/plain
       // 具体可以由用户指定,在 mime.types 中添加映射即可
    // 使用一系列的filter过滤器链进行处理 // 当文件未发生变化时,会返回304, 即不再返回更多信息 /** Accept-Ranges: bytes Connection: keep-alive Content-Length: 18 Content-Type: text/plain Date: Wed, 07 Oct 2020 09:01:12 GMT ETag: "5f66fc46-12" Last-Modified: Sun, 20 Sep 2020 06:52:54 GMT Server: nginx/1.19.2 */ rc = ngx_http_send_header(r); if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { return rc; } // 封装要响应的文件描述符,输出响应 b->file_pos = 0; b->file_last = of.size; b->in_file = b->file_last ? 1: 0; b->last_buf = (r == r->main) ? 1: 0; b->last_in_chain = 1; b->file->fd = of.fd; b->file->name = path; b->file->log = log; b->file->directio = of.is_directio; out.buf = b; out.next = NULL; // 一系列的响应过滤器处理,响应body /** * range_filter * copy_filter * output_chain -> output_chain_copy_buf */ return ngx_http_output_filter(r, &out); }

      整体可以简单用一句话说明,根据uri路径,结合root路径配置,得到文件信息,响应客户端。只处理 get/head请求,对文件内容无变化情况将会返回304。上面更多的是,只限于c语言的表达能力问题,做的许多变化,无须多看。

          对于静态文件的处理,后续所有给定的参数都将被忽略掉。当然了也不是完全无用,不同参数也是有意义的,它用于检测文件是否发生变化。至于是html文件还是pdf或者txt,nginx并没有做特别的处理,它只是负责将内容返回给浏览器,浏览器做进一步解析从而达到html展现的效果。

      另外,再次需要说明的一点是,nginx的性能优势是在于其巧妙地利用系统的非阻塞io,从而提升了处理能力,而且其扩展能力非常强,配置也非常友好。但其整体流程,与其他http服务器并无二致。

      

      对于想要了解更多细节的同学,可以点开下面的代码,查看C语言的实现细节。

    // http/ngx_http_core_module.c
    // 解析文件路径到path中
    u_char *
    ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,
        size_t *root_length, size_t reserved)
    {
        u_char                    *last;
        size_t                     alias;
        ngx_http_core_loc_conf_t  *clcf;
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        alias = clcf->alias;
    
        if (alias && !r->valid_location) {
            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,
                          ""alias" cannot be used in location "%V" "
                          "where URI was rewritten", &clcf->name);
            return NULL;
        }
    
        if (clcf->root_lengths == NULL) {
    
            *root_length = clcf->root.len;
    
            path->len = clcf->root.len + reserved + r->uri.len - alias + 1;
    
            path->data = ngx_pnalloc(r->pool, path->len);
            if (path->data == NULL) {
                return NULL;
            }
            // 将root根目录赋值给 path
            last = ngx_copy(path->data, clcf->root.data, clcf->root.len);
    
        } else {
    
            if (alias == NGX_MAX_SIZE_T_VALUE) {
                reserved += r->add_uri_to_alias ? r->uri.len + 1 : 1;
    
            } else {
                reserved += r->uri.len - alias + 1;
            }
    
            if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,
                                    clcf->root_values->elts)
                == NULL)
            {
                return NULL;
            }
    
            if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, path)
                != NGX_OK)
            {
                return NULL;
            }
    
            *root_length = path->len - reserved;
            last = path->data + *root_length;
    
            if (alias == NGX_MAX_SIZE_T_VALUE) {
                if (!r->add_uri_to_alias) {
                    *last = '';
                    return last;
                }
    
                alias = 0;
            }
        }
        // 将uri的相对路径拼接到 path 中
        last = ngx_copy(last, r->uri.data + alias, r->uri.len - alias);
        *last = '';
    
        return last;
    }
    
    // http/ngx_http_core_module.c
    ngx_int_t
    ngx_http_set_disable_symlinks(ngx_http_request_t *r,
        ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of)
    {
    #if (NGX_HAVE_OPENAT)
        u_char     *p;
        ngx_str_t   from;
    
        of->disable_symlinks = clcf->disable_symlinks;
    
        if (clcf->disable_symlinks_from == NULL) {
            return NGX_OK;
        }
    
        if (ngx_http_complex_value(r, clcf->disable_symlinks_from, &from)
            != NGX_OK)
        {
            return NGX_ERROR;
        }
    
        if (from.len == 0
            || from.len > path->len
            || ngx_memcmp(path->data, from.data, from.len) != 0)
        {
            return NGX_OK;
        }
    
        if (from.len == path->len) {
            of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;
            return NGX_OK;
        }
    
        p = path->data + from.len;
    
        if (*p == '/') {
            of->disable_symlinks_from = from.len;
            return NGX_OK;
        }
    
        p--;
    
        if (*p == '/') {
            of->disable_symlinks_from = from.len - 1;
        }
    #endif
    
        return NGX_OK;
    }
    
    
    // core/ngx_open_file_cache.c
    // 尝试使用缓存文件
    ngx_int_t
    ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,
        ngx_open_file_info_t *of, ngx_pool_t *pool)
    {
        time_t                          now;
        uint32_t                        hash;
        ngx_int_t                       rc;
        ngx_file_info_t                 fi;
        ngx_pool_cleanup_t             *cln;
        ngx_cached_open_file_t         *file;
        ngx_pool_cleanup_file_t        *clnf;
        ngx_open_file_cache_cleanup_t  *ofcln;
    
        of->fd = NGX_INVALID_FILE;
        of->err = 0;
        // 没有缓存,则重新打开文件
        if (cache == NULL) {
    
            if (of->test_only) {
    
                if (ngx_file_info_wrapper(name, of, &fi, pool->log)
                    == NGX_FILE_ERROR)
                {
                    return NGX_ERROR;
                }
    
                of->uniq = ngx_file_uniq(&fi);
                of->mtime = ngx_file_mtime(&fi);
                of->size = ngx_file_size(&fi);
                of->fs_size = ngx_file_fs_size(&fi);
                of->is_dir = ngx_is_dir(&fi);
                of->is_file = ngx_is_file(&fi);
                of->is_link = ngx_is_link(&fi);
                of->is_exec = ngx_is_exec(&fi);
    
                return NGX_OK;
            }
    
            cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));
            if (cln == NULL) {
                return NGX_ERROR;
            }
            // 打开文件
            rc = ngx_open_and_stat_file(name, of, pool->log);
            // 找到对应文件,设置 handler 为 ngx_pool_cleanup_file
            if (rc == NGX_OK && !of->is_dir) {
                cln->handler = ngx_pool_cleanup_file;
                clnf = cln->data;
    
                clnf->fd = of->fd;
                clnf->name = name->data;
                clnf->log = pool->log;
            }
    
            return rc;
        }
    
        cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));
        if (cln == NULL) {
            return NGX_ERROR;
        }
    
        now = ngx_time();
    
        hash = ngx_crc32_long(name->data, name->len);
    
        file = ngx_open_file_lookup(cache, name, hash);
    
        if (file) {
    
            file->uses++;
    
            ngx_queue_remove(&file->queue);
    
            if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {
    
                /* file was not used often enough to keep open */
    
                rc = ngx_open_and_stat_file(name, of, pool->log);
    
                if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
                    goto failed;
                }
    
                goto add_event;
            }
    
            if (file->use_event
                || (file->event == NULL
                    && (of->uniq == 0 || of->uniq == file->uniq)
                    && now - file->created < of->valid
    #if (NGX_HAVE_OPENAT)
                    && of->disable_symlinks == file->disable_symlinks
                    && of->disable_symlinks_from == file->disable_symlinks_from
    #endif
                ))
            {
                if (file->err == 0) {
    
                    of->fd = file->fd;
                    of->uniq = file->uniq;
                    of->mtime = file->mtime;
                    of->size = file->size;
    
                    of->is_dir = file->is_dir;
                    of->is_file = file->is_file;
                    of->is_link = file->is_link;
                    of->is_exec = file->is_exec;
                    of->is_directio = file->is_directio;
    
                    if (!file->is_dir) {
                        file->count++;
                        ngx_open_file_add_event(cache, file, of, pool->log);
                    }
    
                } else {
                    of->err = file->err;
    #if (NGX_HAVE_OPENAT)
                    of->failed = file->disable_symlinks ? ngx_openat_file_n
                                                        : ngx_open_file_n;
    #else
                    of->failed = ngx_open_file_n;
    #endif
                }
    
                goto found;
            }
    
            ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,
                           "retest open file: %s, fd:%d, c:%d, e:%d",
                           file->name, file->fd, file->count, file->err);
    
            if (file->is_dir) {
    
                /*
                 * chances that directory became file are very small
                 * so test_dir flag allows to use a single syscall
                 * in ngx_file_info() instead of three syscalls
                 */
    
                of->test_dir = 1;
            }
    
            of->fd = file->fd;
            of->uniq = file->uniq;
    
            rc = ngx_open_and_stat_file(name, of, pool->log);
    
            if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
                goto failed;
            }
    
            if (of->is_dir) {
    
                if (file->is_dir || file->err) {
                    goto update;
                }
    
                /* file became directory */
    
            } else if (of->err == 0) {  /* file */
    
                if (file->is_dir || file->err) {
                    goto add_event;
                }
    
                if (of->uniq == file->uniq) {
    
                    if (file->event) {
                        file->use_event = 1;
                    }
    
                    of->is_directio = file->is_directio;
    
                    goto update;
                }
    
                /* file was changed */
    
            } else { /* error to cache */
    
                if (file->err || file->is_dir) {
                    goto update;
                }
    
                /* file was removed, etc. */
            }
    
            if (file->count == 0) {
    
                ngx_open_file_del_event(file);
    
                if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                                  ngx_close_file_n " "%V" failed", name);
                }
    
                goto add_event;
            }
    
            ngx_rbtree_delete(&cache->rbtree, &file->node);
    
            cache->current--;
    
            file->close = 1;
    
            goto create;
        }
    
        /* not found */
    
        rc = ngx_open_and_stat_file(name, of, pool->log);
    
        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
            goto failed;
        }
    
    create:
    
        if (cache->current >= cache->max) {
            ngx_expire_old_cached_files(cache, 0, pool->log);
        }
    
        file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);
    
        if (file == NULL) {
            goto failed;
        }
    
        file->name = ngx_alloc(name->len + 1, pool->log);
    
        if (file->name == NULL) {
            ngx_free(file);
            file = NULL;
            goto failed;
        }
    
        ngx_cpystrn(file->name, name->data, name->len + 1);
    
        file->node.key = hash;
    
        ngx_rbtree_insert(&cache->rbtree, &file->node);
    
        cache->current++;
    
        file->uses = 1;
        file->count = 0;
        file->use_event = 0;
        file->event = NULL;
    
    add_event:
    
        ngx_open_file_add_event(cache, file, of, pool->log);
    
    update:
    
        file->fd = of->fd;
        file->err = of->err;
    #if (NGX_HAVE_OPENAT)
        file->disable_symlinks = of->disable_symlinks;
        file->disable_symlinks_from = of->disable_symlinks_from;
    #endif
    
        if (of->err == 0) {
            file->uniq = of->uniq;
            file->mtime = of->mtime;
            file->size = of->size;
    
            file->close = 0;
    
            file->is_dir = of->is_dir;
            file->is_file = of->is_file;
            file->is_link = of->is_link;
            file->is_exec = of->is_exec;
            file->is_directio = of->is_directio;
    
            if (!of->is_dir) {
                file->count++;
            }
        }
    
        file->created = now;
    
    found:
    
        file->accessed = now;
    
        ngx_queue_insert_head(&cache->expire_queue, &file->queue);
    
        ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,
                       "cached open file: %s, fd:%d, c:%d, e:%d, u:%d",
                       file->name, file->fd, file->count, file->err, file->uses);
    
        if (of->err == 0) {
    
            if (!of->is_dir) {
                cln->handler = ngx_open_file_cleanup;
                ofcln = cln->data;
    
                ofcln->cache = cache;
                ofcln->file = file;
                ofcln->min_uses = of->min_uses;
                ofcln->log = pool->log;
            }
    
            return NGX_OK;
        }
    
        return NGX_ERROR;
    
    failed:
    
        if (file) {
            ngx_rbtree_delete(&cache->rbtree, &file->node);
    
            cache->current--;
    
            if (file->count == 0) {
    
                if (file->fd != NGX_INVALID_FILE) {
                    if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {
                        ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                                      ngx_close_file_n " "%s" failed",
                                      file->name);
                    }
                }
    
                ngx_free(file->name);
                ngx_free(file);
    
            } else {
                file->close = 1;
            }
        }
    
        if (of->fd != NGX_INVALID_FILE) {
            if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,
                              ngx_close_file_n " "%V" failed", name);
            }
        }
    
        return NGX_ERROR;
    }
    
    // core/ngx_open_file_cache.c
    // 打开文件
    static ngx_int_t
    ngx_open_and_stat_file(ngx_str_t *name, ngx_open_file_info_t *of,
        ngx_log_t *log)
    {
        ngx_fd_t         fd;
        ngx_file_info_t  fi;
    
        if (of->fd != NGX_INVALID_FILE) {
    
            if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
                of->fd = NGX_INVALID_FILE;
                return NGX_ERROR;
            }
    
            if (of->uniq == ngx_file_uniq(&fi)) {
                goto done;
            }
    
        } else if (of->test_dir) {
    
            if (ngx_file_info_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {
                of->fd = NGX_INVALID_FILE;
                return NGX_ERROR;
            }
    
            if (ngx_is_dir(&fi)) {
                goto done;
            }
        }
    
        if (!of->log) {
    
            /*
             * Use non-blocking open() not to hang on FIFO files, etc.
             * This flag has no effect on a regular files.
             */
            // 使用非阻塞io打开文件
            fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,
                                       NGX_FILE_OPEN, 0, log);
    
        } else {
            fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,
                                       NGX_FILE_CREATE_OR_OPEN,
                                       NGX_FILE_DEFAULT_ACCESS, log);
        }
    
        if (fd == NGX_INVALID_FILE) {
            of->fd = NGX_INVALID_FILE;
            return NGX_ERROR;
        }
        // 检测文件有效性
        if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_CRIT, log, ngx_errno,
                          ngx_fd_info_n " "%V" failed", name);
    
            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                              ngx_close_file_n " "%V" failed", name);
            }
    
            of->fd = NGX_INVALID_FILE;
    
            return NGX_ERROR;
        }
    
        if (ngx_is_dir(&fi)) {
            if (ngx_close_file(fd) == NGX_FILE_ERROR) {
                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                              ngx_close_file_n " "%V" failed", name);
            }
    
            of->fd = NGX_INVALID_FILE;
    
        } else {
            of->fd = fd;
            // 如果文件过大,则先预读取部分数据
            if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {
                if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {
                    ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                                  ngx_read_ahead_n " "%V" failed", name);
                }
            }
    
            if (of->directio <= ngx_file_size(&fi)) {
                if (ngx_directio_on(fd) == NGX_FILE_ERROR) {
                    ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                                  ngx_directio_on_n " "%V" failed", name);
    
                } else {
                    of->is_directio = 1;
                }
            }
        }
    
    done:
        // 复制文件信息返回
        of->uniq = ngx_file_uniq(&fi);
        of->mtime = ngx_file_mtime(&fi);
        of->size = ngx_file_size(&fi);
        of->fs_size = ngx_file_fs_size(&fi);
        of->is_dir = ngx_is_dir(&fi);
        of->is_file = ngx_is_file(&fi);
        of->is_link = ngx_is_link(&fi);
        of->is_exec = ngx_is_exec(&fi);
    
        return NGX_OK;
    }
    View Code
  • 相关阅读:
    Web 2.0网站命名的7个建议
    梦猪课堂视频系列
    计算机英文术语完全介绍
    PPT高手的思路
    在线RSS阅读器大比拼
    【百度现有服务】
    转载VFW编程实例(详)
    实现MFC扩展DLL中导出类和对话框 (转)
    Windows下编译 OpenSceneGraph(转)
    OSG静态编译 (转)
  • 原文地址:https://www.cnblogs.com/yougewe/p/13778108.html
Copyright © 2020-2023  润新知