libuv之三:文件系统

Posted 风吹大风车

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了libuv之三:文件系统相关的知识,希望对你有一定的参考价值。

文件系统

简单的文件系统读写操作使用uv_fs*函数和uv_fs_t结构体完成。

注意:libuv文件系统操作不同于套接字操作。套接字操作使用操作系统提供的非阻塞操作。文件系统操作在内部使用阻塞函数,但是在线程池中调用这些函数,并在需要应用程序交互时通知注册到事件循环中的监视者。

所有文件系统函数都有两种形式——同步和异步。
如果回调为空,同步表单会自动被调用(并阻塞)。函数的返回值是一个libuv错误代码。这通常只对同步调用有用。当传递回调且返回值为0时,将调用异步表单。

读/写文件

打开文件描述符可以用

int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb)

flags和mode是标准的Unix标志。libuv负责转换为适当的Windows标志。
关闭文件描述符可以用

int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb)

文件系统操作回调具有如下特征:

void callback(uv_fs_t* req)

让我们看看cat的一个简单实现。我们首先注册一个回调函数,用于文件打开时:
uvcat/main.c - 打开一个文件

void on_open(uv_fs_t *req) {
    // The request passed to the callback is the same as the one the call setup
    // function was passed.
    assert(req == &open_req);
    if (req->result >= 0) {
        iov = uv_buf_init(buffer, sizeof(buffer));
        uv_fs_read(uv_default_loop(), &read_req, req->result,
                   &iov, 1, -1, on_read);
    }
    else {
        fprintf(stderr, "error opening file: %s\\n", uv_strerror((int)req->result));
    }
}

uv_fs_t的result字段是uv_fs_open回调时的文件描述符。如果文件成功打开,我们就开始读文件。
uvcat/main.c - 读回调

void on_read(uv_fs_t *req) {
    if (req->result < 0) {
        fprintf(stderr, "Read error: %s\\n", uv_strerror(req->result));
    }
    else if (req->result == 0) {
        uv_fs_t close_req;
        // synchronous
        uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
    }
    else if (req->result > 0) {
        iov.len = req->result;
        uv_fs_write(uv_default_loop(), &write_req, 1, &iov, 1, -1, on_write);
    }
}

在read调用的情况下,你应该在read回调触发之前传一个已经初始化被填充数据的buffer。uv_fs_*操作几乎直接映射到某些POSIX函数,所以EOF通常用result=0表示。在流或者管道下,UV_EOF通常最为一个状态被传递出去。
在这里您可以看到编写异步程序时的一个常见模式。调用uv_fs_close()是同步执行的。通常任务都是一次性的,或者作为启动或关闭阶段的一部分同步执行的,当程序正在进行它的主要任务和处理多个I/O源时我们最感兴趣的是快速的I/O。对单独的任务,性能差异通常可以忽略不计,并且可能产生更简洁的代码。
使用uv_fs_write()编写文件系统也很简单。您的回调将在写入完成后触发。在我们的例子中,回调只是驱动着下一个的读取。因此,读写进程通过回调紧密执行。
uvcat/main.c - write callback

void on_write(uv_fs_t *req) {
    if (req->result < 0) {
        fprintf(stderr, "Write error: %s\\n", uv_strerror((int)req->result));
    }
    else {
        uv_fs_read(uv_default_loop(), &read_req, open_req.result, &iov, 1, -1, on_read);
    }
}
警告:由于文件系统和磁盘驱动器为了性能而进行的配置,一次写成功可能还没有被提交到磁盘上。

我们在main函数中试一次重读读写
uvcat/main.c

int main(int argc, char **argv) {
    uv_fs_open(uv_default_loop(), &open_req, argv[1], O_RDONLY, 0, on_open);
    uv_run(uv_default_loop(), UV_RUN_DEFAULT);

    uv_fs_req_cleanup(&open_req);
    uv_fs_req_cleanup(&read_req);
    uv_fs_req_cleanup(&write_req);
    return 0;
}
警告:uv_fs_req_cleanup()函数必须在文件系统请求释放libuv中的内部内存时调用。

文件系统操作

所有标准文件系统操作(如unlink、rmdir、stat)都是支持异步的,并且具有直观的参数顺序。它们遵循与读/写/打开调用相同的模式,在uv_fs_t中返回result字段。完整列表如下:
文件系统操作列表

int uv_fs_close(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_open(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, int mode, uv_fs_cb cb);
int uv_fs_read(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb);
int uv_fs_unlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_write(uv_loop_t* loop, uv_fs_t* req, uv_file file, const uv_buf_t bufs[], unsigned int nbufs, int64_t offset, uv_fs_cb cb);
int uv_fs_copyfile(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb);
int uv_fs_mkdir(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb);
int uv_fs_mkdtemp(uv_loop_t* loop, uv_fs_t* req, const char* tpl, uv_fs_cb cb);
int uv_fs_rmdir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_scandir(uv_loop_t* loop, uv_fs_t* req, const char* path, int flags, uv_fs_cb cb);
int uv_fs_scandir_next(uv_fs_t* req, uv_dirent_t* ent);
int uv_fs_opendir(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_readdir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb);
int uv_fs_closedir(uv_loop_t* loop, uv_fs_t* req, uv_dir_t* dir, uv_fs_cb cb);
int uv_fs_stat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_fstat(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_rename(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb);
int uv_fs_fsync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_fdatasync(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_fs_cb cb);
int uv_fs_ftruncate(uv_loop_t* loop, uv_fs_t* req, uv_file file, int64_t offset, uv_fs_cb cb);
int uv_fs_sendfile(uv_loop_t* loop, uv_fs_t* req, uv_file out_fd, uv_file in_fd, int64_t in_offset, size_t length, uv_fs_cb cb);
int uv_fs_access(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb);
int uv_fs_chmod(uv_loop_t* loop, uv_fs_t* req, const char* path, int mode, uv_fs_cb cb);
int uv_fs_utime(uv_loop_t* loop, uv_fs_t* req, const char* path, double atime, double mtime, uv_fs_cb cb);
int uv_fs_futime(uv_loop_t* loop, uv_fs_t* req, uv_file file, double atime, double mtime, uv_fs_cb cb);
int uv_fs_lstat(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_link(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, uv_fs_cb cb);
int uv_fs_symlink(uv_loop_t* loop, uv_fs_t* req, const char* path, const char* new_path, int flags, uv_fs_cb cb);
int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_realpath(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_fs_cb cb);
int uv_fs_fchmod(uv_loop_t* loop, uv_fs_t* req, uv_file file, int mode, uv_fs_cb cb);
int uv_fs_chown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb);
int uv_fs_fchown(uv_loop_t* loop, uv_fs_t* req, uv_file file, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb);
int uv_fs_lchown(uv_loop_t* loop, uv_fs_t* req, const char* path, uv_uid_t uid, uv_gid_t gid, uv_fs_cb cb);

缓冲区和流

libuv中的基本I/O句柄是流(uv_stream_t)。TCP套接字、UDP套接字、用于文件I/O的管道和IPC都是流的子类。
流使用每个子类的自定义函数进行初始化,然后在使用时进行操作

int uv_read_start(uv_stream_t*, uv_alloc_cb alloc_cb, uv_read_cb read_cb);
int uv_read_stop(uv_stream_t*);
int uv_write(uv_write_t* req, uv_stream_t* handle,
             const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);

基于流的函数比文件系统的函数更容易使用,当uv_read_start()被调用一次时,libuv会自动从流中读取数据,直到uv_read_stop()被调用。
数据的不连续单元是缓冲区——uv_buf_t。

以上是关于libuv之三:文件系统的主要内容,如果未能解决你的问题,请参考以下文章

libuv之二:libuv基础

第862期成为一名函数式码农之三

libuv之一:libuv介绍

磁盘及文件系统管理之三

使用 IO 操作时如何不让任务过度饱和 libuv

libuv编译启动