当前位置: 首页 > 科技观察

谈谈在Libuv中使用Io_Uring

时间:2023-03-21 10:40:49 科技观察

本文转载请联系编程杂技公众号。本文介绍如何在Libuv中使用io_uring。逻辑:1申请一个io_uring对应的fd。2初始化一个poll句柄,将fd封装在1中。3注册到Libuv的epoll。4读取文件列表,向io_uring提交请求5io_uring完成,1中的fd可读,于是epoll返回。6Libuv的pollio阶段执行回调。7在回调中获取io_uring的任务完成列表,获取每个任务关联的请求,执行回调。#include#include#include#include#include#include#include#include#defineQUEUE_DEPTH1#defineBLOCK_SZ1024//前向声明structfile_info;//定义回调typedefvoid(*file_callback)(structfile_info*);//管理文件读取请求的结构structfile_info{//文件大小off_tfile_sz;//回调file_callbackcb;//读取大小intcount;//文件名char*name;//读取数据structioveciovecs[];};//获取文件大小off_tget_file_size(intfd){structstatst;if(fstat(fd,&st)<0){perror("fstat");return-1;}if(S_ISBLK(st.st_mode)){unsignedlonglongbytes;if(ioctl(fd,BLKGETSIZE64,&bytes)!=0){perror("ioctl");return-1;}returnbytes;}elseif(S_ISREG(st.st_mode))returnst.st_size;return-1;}//向内核提交请求intsubmit_read_request(char*file_path,file_callbackcb,structio_uring*ring){//打开文件intfile_fd=open(file_path,O_RDONLY);if(file_fd<0){perror("open");return1;}//获取大小off_tfile_sz=get_file_size(file_fd);off_tbytes_remaining=file_sz;intcurrent_block=0;intblocks=(int)file_sz/BLOCK_SZ;if(file_sz%BLOCK_SZ)blocks++;//申请内存structfile_info*fi=malloc(sizeof(*fi)+(sizeof(structiovec)*blocks));//保存文件名fi->name=file_path;//计算并申请内存保存文件内容while(bytes_remaining){//剩余大小off_tbytes_to_read=bytes_remaining;//一个buffer最多可以保存BLOCK_SZsizeif(bytes_to_read>BLOCK_SZ)bytes_to_read=BLOCK_SZ;//记录缓冲区大小fi->iovecs[current_block].iov_len=bytes_to_read;//申请内存void*buf;if(posix_memalign(&buf,BLOCK_SZ,BLOCK_SZ)){错误(“posix_memalign”);return1;}//记录内存地址fi->iovecs[current_block].iov_base=buf;//下一个块current_block++;//更新剩余大小bytes_remaining-=bytes_to_read;}??//保存文件大小fi->file_sz=file_sz;//获取一个io_uring请求结构structio_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);return0;}//io_uring相关结构structio_uring_info{intfd;int32_tpending;structio_uringring;uv_poll_tpoll_handle;};//io_uring完成任务后,Libuv执行的回调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;//记录读取的大小ofreq->count=cqe->res;io_uring_cq_advance(ring,1);//执行回调req->cb(req);}//处理完退出if(io_uring_data->pending==0)uv_stop(uv_default_loop());}//文件读取后业务回调voidfiledone(structfile_info*info){printf("读取大小:%d,文件信息:%s=>%d\n",(int)info->count,info->name,(int)info->file_sz);}intmain(intargc,char*argv[]){if(argc<2){fprintf(stderr,"请输入文件名\n");return1;}//申请一个io_uring相关结构structio_uring_info*io_uring_data=malloc(sizeof(*io_uring_data));//初始化io_uringio_uring_queue_init(1,&io_uring_data->ring,0);//初始化pollhandle,保存监听的fduv_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的上下文uv_default_loop()->data=(void*)io_uring_data;//处理每个文件for(inti=1;iring);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);return0;}编译过程1gitclonehttps://github.com/axboe/liburing.gitexecute./configure&&make-j2&&sudomakeinstall(makej2根据自己的核心数,启动两个编译线程)。2git克隆https://github.com/libuv/libuv.git。执行./autogen.sh&&./configure&&make-j2&&sudomakeinstall。3安装依赖后,新建一个test.cc。然后编译gcc-xctest2.cc-luring-luv(xc指定用c语言编译,如果c++有不同的限制会报错)。4创建两个测试文件hello.cc和world.cc。执行./a.outhello.ccworld.cc。5输出读取大小:6997,文件信息:hello.cc=>6997读取大小:11019,文件信息:world.cc=>11019代码仓库:https://github.com/theanarkh/learn-io_uring。可以参考1.https://github.com/shuveb/io_uring-by-example/blob/master/03_cat_liburing/main.c2https://github.com/libuv/libuv/pull/2322