文件系统¶
在 Libuv 中,文件读写等操作是通过 文件系统(File System,简称 FS)API 实现的。Libuv 提供了异步的文件 I/O 操作,能够高效地处理文件读写、文件属性获取、目录操作等任务。
Libuv 的文件系统操作与套接字操作不同。套接字操作使用的是操作系统提供的非阻塞操作,而文件系统操作在内部使用的是阻塞函数(操作系统提供的文件读写函数是阻塞的),这些函数是在线程池中调用的,并且在需要与应用程序交互时通知注册到事件循环的观察者,从而实现非阻塞调用。
核心 API¶
Libuv 的文件系统 API 主要包括以下函数:
打开文件 uv_fs_open¶
异步打开文件。
参数说明:
loop
:事件循环对象。req
:文件系统请求对象。path
:文件路径。flags
:打开文件的标志(如O_RDONLY
、O_WRONLY
、O_RDWR
等)。mode
:文件权限(如0644
)。cb
:回调函数,操作完成后调用。
读取文件 uv_fs_read¶
异步读取文件。
void 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);
参数说明:
loop
:事件循环对象。req
:文件系统请求对象。file
:文件描述符(由uv_fs_open
返回)。bufs
:缓冲区数组。nbufs
:缓冲区数量。offset
:读取的起始偏移量(-1
表示从当前位置读取)。cb
:回调函数。
写入文件 uv_fs_write¶
异步写入文件。
void 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);
参数说明:
loop
:事件循环对象。req
:文件系统请求对象。file
:文件描述符。bufs
:缓冲区数组。nbufs
:缓冲区数量。offset
:写入的起始偏移量(-1
表示从当前位置写入)。cb
:回调函数。
关闭文件 uv_fs_close¶
异步关闭文件。
参数说明:
loop
:事件循环说明。req
:文件系统请求对象。file
:文件描述符。cb
:回调函数。
其他常用 API¶
uv_fs_stat
:获取文件属性。uv_fs_unlink
:删除文件。uv_fs_mkdir
:创建目录。uv_fs_readdir
:读取目录内容。uv_fs_rename
:重命名文件或目录。
Buffer(缓冲区)和 Stream(流)¶
Libuv 中 Buffer 是用来存储数据的内存区域,通常以 uv_buf_t
类型表示:
创建 uv_buf_t
的方法一般包括动态分配和静态分配:
-
静态分配(适合小数据块):
-
动态分配(适合大数据块):
Libuv 中 Buffer 通常配合 Stream 使用,Stream 是 Libuv 中用于表示I/O操作的抽象,uv_stream_t
是所有流类型的基类,包括TCP套接字(uv_tcp_t
)、UDP套接字(uv_udp_t
)和管道(uv_pipe_t
)等。流操作主要包括:
- 开始读取:
uv_read_start
,用于启动异步读取操作。 - 停止读取:
uv_read_stop
,用于停止读取操作。 - 写入数据:
uv_write
,用于将数据写入流。
uv_read_start¶
启动异步读取操作。当有数据可读时,会调用指定的回调函数。
参数说明:
uv_stream_t* stream
:要进行读取操作的流对象。uv_alloc_cb alloc_cb
:分配缓冲区的回调函数。当有数据可读时,libuv 会调用此回调函数来分配一个缓冲区用于存储读取的数据。-
回调函数原型:
```c void alloc_cb(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf); ```
- handle:当前流的句柄。
- suggested_size:建议的缓冲区大小。
- buf:指向 uv_buf_t 的指针,用于存储分配的缓冲区。
-
uv_read_cb read_cb
:读取数据的回调函数。当读取操作完成时,libuv 会调用此回调函数。 -
回调函数原型:
```c void read_cb(uv_stream_t* stream, ssize_t nread, const uv_buf_t* buf); ```
- stream:当前流对象。
- nread:实际读取的字节数。如果为负值,表示读取失败或到达文件末尾(UV_EOF)。
- buf:指向 uv_buf_t 的指针,包含读取的数据。
当运行成功时返回 0,失败时返回错误码(负值)。
uv_read_stop¶
停止当前流的读取操作。如果流正在读取数据,调用此函数会立即停止读取。
参数说明:
uv_stream_t* stream
:要停止读取操作的流对象。
当运行成功时返回 0,失败时返回错误码(负值)。
uv_write¶
将数据写入流。这是一个异步操作,当写入完成时会调用指定的回调函数。
int uv_write(uv_write_t* req, uv_stream_t* stream, const uv_buf_t bufs[], unsigned int nbufs, uv_write_cb cb);
参数说明:
uv_write_t* req
:写入请求对象,用于跟踪写入操作的状态。uv_stream_t* stream
:要写入数据的流对象。const uv_buf_t bufs[]
:包含要写入数据的缓冲区数组。unsigned int nbufs
:缓冲区数组的大小。uv_write_cb cb
:写入完成的回调函数。当写入操作完成时,libuv 会调用此回调函数。-
回调函数原型:
```c void write_cb(uv_write_t* req, int status); ```
- req:写入请求对象。
- status:写入操作的状态。如果为 0,表示成功;如果为负值,表示失败。
当运行成功时返回 0。失败时返回错误码(负值)。
使用示例¶
简单异步读写操作¶
以下是一个完整的示例,展示了如何使用 Libuv 异步读取文件并写入到另一个文件:
#include <stdio.h>
#include <stdlib.h>
#include <uv.h>
void on_read(uv_fs_t* req);
void on_write(uv_fs_t* req);
uv_fs_t open_req;
uv_fs_t read_req;
uv_fs_t write_req;
uv_fs_t close_req;
uv_buf_t iov;
char buffer[1024];
void on_open(uv_fs_t* req) {
if (req->result < 0) {
fprintf(stderr, "Error opening file: %s\n", uv_strerror(req->result));
return;
}
printf("File opened successfully.\n");
// 读取文件
iov = uv_buf_init(buffer, sizeof(buffer));
uv_fs_read(uv_default_loop(), &read_req, req->result, &iov, 1, -1, on_read);
}
void on_read(uv_fs_t* req) {
if (req->result < 0) {
fprintf(stderr, "Error reading file: %s\n", uv_strerror(req->result));
return;
}
printf("File read successfully.\n");
// 关闭读取的文件
uv_fs_close(uv_default_loop(), &close_req, open_req.result, NULL);
// 打开目标文件
uv_fs_open(uv_default_loop(), &open_req, "output.txt", O_WRONLY | O_CREAT,
0644, on_write);
}
void on_write(uv_fs_t* req) {
if (req->result < 0) {
fprintf(stderr, "Error opening output file: %s\n",
uv_strerror(req->result));
return;
}
printf("Output file opened successfully.\n");
// 写入数据
uv_fs_write(uv_default_loop(), &write_req, req->result, &iov, 1, -1, NULL);
printf("Data written to output file.\n");
// 关闭目标文件
uv_fs_close(uv_default_loop(), &close_req, req->result, NULL);
}
int main() {
// 初始化事件循环
uv_loop_t* loop = uv_default_loop();
// 打开文件
uv_fs_open(loop, &open_req, "input.txt", O_RDONLY, 0, on_open);
// 运行事件循环
uv_run(loop, UV_RUN_DEFAULT);
// 清理请求
uv_fs_req_cleanup(&open_req);
uv_fs_req_cleanup(&read_req);
uv_fs_req_cleanup(&write_req);
uv_fs_req_cleanup(&close_req);
return 0;
}