ngx_open_and_stat_file 1 定义ngx_open_and_stat_file 函数 定义在 ./nginx-1.24.0/src/core/ngx_open_file_cache.cstaticngx_int_tngx_open_and_stat_file(ngx_str_t*name,ngx_open_file_info_t*of,ngx_log_t*log){ngx_fd_tfd;ngx_file_info_tfi;if(of-fd!NGX_INVALID_FILE){if(ngx_file_info_wrapper(name,of,fi,log)NGX_FILE_ERROR){of-fdNGX_INVALID_FILE;returnNGX_ERROR;}if(of-uniqngx_file_uniq(fi)){gotodone;}}elseif(of-test_dir){if(ngx_file_info_wrapper(name,of,fi,log)NGX_FILE_ERROR){of-fdNGX_INVALID_FILE;returnNGX_ERROR;}if(ngx_is_dir(fi)){gotodone;}}if(!of-log){/* * Use non-blocking open() not to hang on FIFO files, etc. * This flag has no effect on a regular files. */fdngx_open_file_wrapper(name,of,NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0,log);}else{fdngx_open_file_wrapper(name,of,NGX_FILE_APPEND,NGX_FILE_CREATE_OR_OPEN,NGX_FILE_DEFAULT_ACCESS,log);}if(fdNGX_INVALID_FILE){of-fdNGX_INVALID_FILE;returnNGX_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-fdNGX_INVALID_FILE;returnNGX_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-fdNGX_INVALID_FILE;}else{of-fdfd;if(of-read_aheadngx_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-directiongx_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_directio1;}}}done:of-uniqngx_file_uniq(fi);of-mtimengx_file_mtime(fi);of-sizengx_file_size(fi);of-fs_sizengx_file_fs_size(fi);of-is_dirngx_is_dir(fi);of-is_filengx_is_file(fi);of-is_linkngx_is_link(fi);of-is_execngx_is_exec(fi);returnNGX_OK;}ngx_open_and_stat_file 函数是 Nginx 中打开文件并获取其元数据的核心函数。 它根据输入参数灵活处理多种情况 如果已有有效的文件描述符如来自缓存则检查文件是否变更并直接复用 如果仅需测试目录属性则只获取元数据而不打开 否则实际打开文件普通文件或日志文件并在打开后立即获取文件信息 同时可配置预读和直接 I/O 优化。 最终统一填充文件类型、大小、inode 等元数据。2 详解1 函数签名staticngx_int_tngx_open_and_stat_file(ngx_str_t*name,ngx_open_file_info_t*of,ngx_log_t*log)返回值 NGX_OK文件成功打开 NGX_ERROR操作失败参数1 ngx_str_t *name 完整文件路径参数2 ngx_open_file_info_t *of 指向 ngx_open_file_info_t 结构体的指针 承载输入控制参数和输出结果参数3 ngx_log_t *log 指向要使用的日志对象2 逻辑流程1 捷径 1 复用已有 fd 若文件 inode 未变则直接跳转到 done 标签复用原有描述符。 否则 走捷径失败继续后续流程 2 目录测试模式 当 of-test_dir 为真时仅通过 stat 获取元数据不打开文件。 如果确实是目录则跳转到 done 标签 否则 走捷径失败继续后续流程。 2 实际打开文件并获取元数据 3 处理结果并输出{ngx_fd_tfd;ngx_file_info_tfi;局部变量声明 fd临时存储新打开的文件描述符。 fi临时存储文件的 stat 信息。1 捷径if(of-fd!NGX_INVALID_FILE){if(ngx_file_info_wrapper(name,of,fi,log)NGX_FILE_ERROR){of-fdNGX_INVALID_FILE;returnNGX_ERROR;}if(of-uniqngx_file_uniq(fi)){gotodone;}尝试复用已有的文件描述符 检查 of 中是否已存在有效的文件描述符 逻辑若已有 fd则优先验证该文件是否仍有效尝试避免重新打开。调用 ngx_file_info_wrapper 获取文件的最新元数据 若获取失败说明文件无法访问旧 fd 已无效 将 of-fd 置为无效并返回错误避免调用者误用失效的描述符获取成功 比较当前文件的 inode 与 of-uniq 是否相同。 逻辑若相同说明文件未被替换旧 fd 仍然有效可直接复用跳转到 done 填充元数据。}elseif(of-test_dir){if(ngx_file_info_wrapper(name,of,fi,log)NGX_FILE_ERROR){of-fdNGX_INVALID_FILE;returnNGX_ERROR;}if(ngx_is_dir(fi)){gotodone;}}目录测试模式仅获取元数据 当 of-fd 无效且 of-test_dir 为真时进入目录测试模式。 逻辑调用者只想确认路径是否为目录并获取其属性而不是打开它。获取文件元数据 失败则清理并返回错误。获取文件元数据成功 如果确实是目录跳转到 done 填充目录属性。 逻辑目录无需也不应被打开因此直接使用 stat 信息即可。2 实际打开文件并获取元数据if(!of-log){/* * Use non-blocking open() not to hang on FIFO files, etc. * This flag has no effect on a regular files. */fdngx_open_file_wrapper(name,of,NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,NGX_FILE_OPEN,0,log);}else{fdngx_open_file_wrapper(name,of,NGX_FILE_APPEND,NGX_FILE_CREATE_OR_OPEN,NGX_FILE_DEFAULT_ACCESS,log);}实际打开文件 根据 of-log 是否为空决定打开模式。 若 of-log 为空表示不是日志文件 以只读NGX_FILE_RDONLY和非阻塞NGX_FILE_NONBLOCK模式打开文件。 of-log 非空说明文件是日志文件需要特殊打开方式。 以追加模式NGX_FILE_APPEND打开日志文件。 若不存在则创建NGX_FILE_CREATE_OR_OPEN权限为默认值。if(fdNGX_INVALID_FILE){of-fdNGX_INVALID_FILE;returnNGX_ERROR;}若打开失败清理 of-fd 并返回错误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-fdNGX_INVALID_FILE;returnNGX_ERROR;}获取已打开文件的元数据 通过文件描述符调用 fstat 获取文件信息。若 fstat 失败记录日志 关闭文件描述符 将 of-fd 置为无效并返回错误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-fdNGX_INVALID_FILE;判断是否为目录 目录不能作为普通文件使用因此关闭描述符并将 of-fd 设为无效}else{of-fdfd;if(of-read_aheadngx_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-directiongx_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_directio1;}}}对于普通文件保留有效的文件描述符供调用者使用如发送静态文件。若配置了 read_ahead 且文件足够大 设置内核预读窗口以提升顺序读取性能。 失败仅记录日志不影响文件使用。若文件大小达到 directio 阈值启用直接 I/OO_DIRECT。 失败仅记录日志 成功则设置 of-is_directio 标志3 处理结果并输出done:of-uniqngx_file_uniq(fi);of-mtimengx_file_mtime(fi);of-sizengx_file_size(fi);of-fs_sizengx_file_fs_size(fi);of-is_dirngx_is_dir(fi);of-is_filengx_is_file(fi);of-is_linkngx_is_link(fi);of-is_execngx_is_exec(fi);填充文件元数据returnNGX_OK;}返回 NGX_OK 表示操作成功完成