序言
开发nginx http模块时,有时候需要从指定的文件读取信息,这个时候,如果是新手,则肯定用操作系统文件系统函数来操作文件,比如用fopen打开文件,用fseek,fread等读取文件信息,最后用fclose关闭文件。这样操作文件也是可以工作的,但是如果代码中间有return的时候,我们需要确保打开的文件关闭,否则会造成文件句柄没有关闭,资源浪费,而且这种方式和nginx的异步回调风格有点不符。所以我们可以利用nginx提供的文件函数来操作文件,而在请求结束的时候一并关闭打开的文件。
使用nginx自带的机制操作文件
参考fopen函数,那么肯定需要用到nginx的file结构体(src/core/ngx_file.h )。结构如下
struct ngx_file_s {
ngx_fd_t fd;
ngx_str_t name;
ngx_file_info_t info;
off_t offset;
off_t sys_offset;
ngx_log_t *log;
#if (NGX_HAVE_FILE_AIO)
ngx_event_aio_t *aio;
#endif
unsigned valid_info:;
unsigned directio:;
};
通常我们用到的字段有,fd,name,info, log。其他的字段nginx框架会自动赋值并使用。
nginx 定义了一个宏用来打开文件(src/os/unix/ngx_files.h)
#define ngx_open_file(name, mode, create, access) \
open((const char *) name, mode|create|O_BINARY, access)
另外,nginx的资源管理池会在请求结束时,一并回收所有资源。结果如下
struct ngx_pool_cleanup_s {
ngx_pool_cleanup_pt handler;
void *data;
ngx_pool_cleanup_t *next;
};
其中data域存放要回收资源的关键信息,handler是回收资源的函数,next是只想下一个带回收的资源。
因为我们要回收的资源是文件,所以nginx定义要回收的文件资源结构为
typedef struct {
ngx_fd_t fd;
u_char *name;
ngx_log_t *log;
} ngx_pool_cleanup_file_t;
可以看到有fd这一项。顺便也可以看看nginx自带的关闭文件的回调函数
void
ngx_pool_cleanup_file(void *data)
{
ngx_pool_cleanup_file_t *c = data;
ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, , "file cleanup: fd:%d",
c->fd);
if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {
ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
ngx_close_file_n " \"%s\" failed", c->name);
}
}
有了以上的分析,那么我们操作文件的代码就是如下了
ngx_pool_cleanup_t *cln;
ngx_pool_cleanup_file_t *clfn;
ngx_file_t *file;
file = ngx_palloc(r->pool, sizeof(ngx_file_t));
if (file == NULL ) {
return NGX_ERROR;
}
char *realfilename = "/path/to/your/input.info";
file->fd = ngx_open_file(realfilename, NGX_FILE_RDONLY, NGX_FILE_OPEN, );
file->name.len = ngx_strlen(realfilename);
file->name.data = realfilename;
file->log = r->pool->log;
cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
if (cln == NULL) {
return NGX_ERROR;
}
clfn = cln->data;//此处要特别注意,让clfn指针指向上面ngx_pool_cleanup_add函数内分配的存放ngx_pool_cleanup_file_t的空间。
clfn->fd = file->fd;
clfn->name = realfilename;
clfn->log = r->pool->log;
cln->handler = ngx_pool_cleanup_file;
经过上面短短的代码,文件操作就完成了,重要的是,不管你的代码,由于什么原因中途return,也不用担心文件句柄没有关闭。