本文转载自微信公众号「编程杂技 」,作者theanarkh 。转载本文请联系编程杂技公众号。
本文介绍如果在Libuv中使用io_uring。逻辑:
1 申请一个io_uring对应的fd。
2 初始化一个poll handle,封装1中的fd。
3 注册到Libuv的epoll中。
4 读取文件列表,给io_uring提交请求
5 io_uring完成,1中的fd可读,从而epoll返回。
6 Libuv的poll io阶段执行回调。
7 回调里获取io_uring的任务完成列表,拿到每个任务关联的请求,执行回调。
复制
#include <fcntl.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <liburing.h> #include <stdlib.h> #include <uv.h> #define QUEUE_DEPTH 1 #define BLOCK_SZ 1024 // 前向声明 struct file_info; // 定义回调 typedef void (*file_callback)(struct file_info*); // 管理一个文件读取请求的结构体 struct file_info { // 文件大小 off_t file_sz; // 回调 file_callback cb; // 读取的大小 int count; // 文件名 char *name; // 读取的数据 struct iovec iovecs[]; }; // 获取文件大小 off_t get_file_size(int fd) { struct stat st; if(fstat(fd, &st) < 0) { perror("fstat"); return -1; } if (S_ISBLK(st.st_mode)) { unsigned long long bytes; if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { perror("ioctl"); return -1; } return bytes; } else if (S_ISREG(st.st_mode)) return st.st_size; return -1; } // 向内核提交一个请求 int submit_read_request(char *file_path, file_callback cb, struct io_uring *ring) { // 打开文件 int file_fd = open(file_path, O_RDONLY); if (file_fd < 0) { perror("open"); return 1; } // 获取大小 off_t file_sz = get_file_size(file_fd); off_t bytes_remaining = file_sz; int current_block = 0; int blocks = (int) file_sz / BLOCK_SZ; if (file_sz % BLOCK_SZ) blocks++; // 申请内存 struct file_info *fi = malloc(sizeof(*fi) + (sizeof(struct iovec) * blocks)); // 保存文件名 fi->name = file_path; // 计算和申请保存文件内容的内存 while (bytes_remaining) { // 剩下的大小 off_t bytes_to_read = bytes_remaining; // 一个buffer最大保存BLOCK_SZ大小 if (bytes_to_read > BLOCK_SZ) bytes_to_read = BLOCK_SZ; // 记录buffer大小 fi->iovecs[current_block].iov_len = bytes_to_read; // 申请内存 void *buf; if( posix_memalign(&buf, BLOCK_SZ, BLOCK_SZ)) { perror("posix_memalign"); return 1; } // 记录内存地址 fi->iovecs[current_block].iov_base = buf; // 下一块 current_block++; // 更新剩下的大小 bytes_remaining -= bytes_to_read; } // 保存文件大小 fi->file_sz = file_sz; // 获取一个io_uring的请求结构体 struct io_uring_sqe *sqe = io_uring_get_sqe(ring); // 填充请求 io_uring_prep_readv(sqe, file_fd, fi->iovecs, blocks, 0); // 保存请求上下文,响应的时候用 io_uring_sqe_set_data(sqe, fi); // 保存回调 fi->cb = cb; // 提交请求给内核 io_uring_submit(ring); return 0; } // io_uring相关的结构体 struct io_uring_info { int fd; int32_t pending; struct io_uring ring; uv_poll_t poll_handle; }; // io_uring完成任务后,Libuv执行的回调 void uv__io_uring_done(uv_poll_t* handle, int status, int events) { struct io_uring* ring; struct io_uring_info* io_uring_data; struct io_uring_cqe* cqe; struct file_info* req; // 获取Libuv中保存的io_uring信息 io_uring_data = uv_default_loop()->data; ring = &io_uring_data->ring; // 处理每一个完成的请求 while (1) { io_uring_peek_cqe(ring, &cqe); if (cqe == NULL) break; // 全部处理完则注销事件 if (--io_uring_data->pending == 0) uv_poll_stop(handle); // 拿到请求上下文 req = (void*) (uintptr_t) cqe->user_data; // 记录读取的大小 req->count = cqe->res; io_uring_cq_advance(ring, 1); // 执行回调 req->cb(req); } // 处理完则退出 if (io_uring_data->pending == 0) uv_stop(uv_default_loop()); } // 文件读取后的业务回调 void filedone(struct file_info* info) { printf("读取的大小:%d,文件信息:%s => %d\n", (int)info->count, info->name, (int)info->file_sz);}int main(int argc, char *argv[]) { if (argc < 2) { fprintf(stderr, "请输入文件名称\n"); return 1; } // 申请一个io_uring相关的结构体 struct io_uring_info *io_uring_data = malloc(sizeof(*io_uring_data)); // 初始化io_uring io_uring_queue_init(1, &io_uring_data->ring, 0); // 初始化poll handle,保存监听的fd uv_poll_init(uv_default_loop(), &io_uring_data->poll_handle, io_uring_data->ring.ring_fd); // 注册事件和回调 uv_poll_start(&io_uring_data->poll_handle, UV_READABLE, uv__io_uring_done); // 保存io_uring的上下文在loop中 uv_default_loop()->data = (void *)io_uring_data; // 处理每一个文件 for (int i = 1; i < argc; i++) { submit_read_request(argv[i], filedone, &io_uring_data->ring); io_uring_data->pending++; } // 开始事件循环 uv_run(uv_default_loop(), UV_RUN_DEFAULT); // 退出 uv_loop_close(uv_default_loop()); io_uring_queue_exit(&io_uring_data->ring); return 0; }
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
编译过程
1 git clone https://github.com/axboe/liburing.git。执行./configure && make -j2 && sudo make install(make j2开启两个线程编译,根据自己的核数定)。
2 git clone https://github.com/libuv/libuv.git。执行./autogen.sh && ./configure && make -j2 && sudo make install。
3 安装完依赖后新建test.cc。然后编译 gcc -xc test2.cc -luring -luv(xc指定按c语言编译,c++的话限制不一样,会报错)。
4 新建两个测试文件hello.cc和world.cc 。执行 ./a.out hello.cc world.cc。
5 输出
复制
读取的大小:6997,文件信息:hello.cc => 6997 读取的大小:11019,文件信息:world.cc => 11019
1.
2.
代码仓库:https://github.com/theanarkh/learn-io_uring。
可以参考
1.https://github.com/shuveb/io_uring-by-example/blob/master/03_cat_liburing/main.c
2 https://github.com/libuv/libuv/pull/2322