天天看点

开发nginx模块时对普通文件的操作

序言

开发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,也不用担心文件句柄没有关闭。

继续阅读