互联网程序设计
姓名:黄健锋 学号:202221080930
基于 io_uring 实现事件处理引擎
1. 总体要求
模仿 muduo 中 epoll 的事件处理方案,采用 io_uring 实现一个事件循环
安装 liburing,阅读 io_uring_setup,io_uring_enter 手册
采用 io_uring 实现一个事件分发器,角色相当于 epoll
实现两个 channel,FileChannel、TcpChannel
做功能测试、性能测试
2. io_uring 介绍:
(1) io_uring 是 linux 平台提供的异步 io 机制,io_uring 的设计目标是提供一个统一、易
用、可扩展、功能丰富、高效的网络和磁盘系统接口。
(2) Io_uring 提供了 3 个系统调用 API
1 io_uring_setup
int io_uring_setup(unsigned entries, struct io_uring_params *params);
entries: queue depth,表示队列深度。
io_uring_params: 初始化时候的参数。
2
3
Io_uring_enter
int io_uring_enter(unsigned int fd, u32 to_submit, u32 min_complete, u32 flags);
io_uring_enter 用于提交 io 请求,收割完成好的 io 操作。
io_uring_register
int io_uring_register(unsigned int fd, unsigned int opcode, void * arg, unsigned int
nr_args)
此接口用于设置 io_uring 的一些行为。
3. liburing 库
想使用 io_uring 框架比较繁琐,为了简化 io_uring 的使用,io_uring 的作者提供了一个
io_uring 的库 liburing,通过使用 liburing,大大减轻了繁琐的配置
liburing 的接口:
int io_uring_queue_init(unsigned entries, struct io_uring *ring, unsigned flags);
用于初始化一个 io_uring 队列
void io_uring_queue_exit(struct io_uring *ring);
清理 io_uring
struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring);
得到一个 sqe,用于提交 io 请求
void io_uring_prep_writev(struct io_uring_sqe *sqe, int fd,const struct iovec *iovecs,
unsigned nr_vecs, off_t offset)
准备一个写请求
void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd, const struct iovec *iovecs,
unsigned nr_vecs, off_t offset)
准备一个读请求
int io_uring_submit(struct io_uring *ring);
提交请求
int io_uring_submit_and_wait(struct io_uring *ring, unsigned wait_nr);
获取 io 结果
4. eventloop 设计
eventloop 负责 channel 的管理,每个 channel 负责一个文件描述符 fd,提高对该 fd 的
读写,channel 允许用户设置回调函数,在完成读写等 io 操作后,eventloop 自动调用用户绑
定到 channel 上的回调函数。
Eventloop 通过 io_uring_submit_and_wait 函数实现事件分发功能
Eventloop 实现事件分发的关键代码,eventloop 通过 io_uring_wait_cqe 获取已经完成的
io 事件,然后调用对应的事件回调,即 chan->handleEvent()
5. Channel
Channel 是对文件描述符的抽象,通过 channel 可以更好的管理 io 操作,eventloop 通
过 channel 完成事件分发。
Channel 即可以读也可以写,channel 的数据成员定义:
可以看到 channel 有三个回调函数 readCallback,writeCallback,listenCallback。用户设
置好回调之后,eventloop 会在对应的事件发生后调用对应的回调。
6. 测试:
测试异步文件读取
测试代码如下:
void readcb(Channel* chan)
{
fprintf(stdout,"i am readcb\n");
char* buf = (char*)(chan->readbuf);
int size = chan->res;
buf[size] = '\0';
// fprintf(stdout,"size=%d\n",size);
fprintf(stdout,"%s\n",buf);
fflush(stdout);
}
int main(int argc, char* argv[])
{
Eventloop loop;
int fd = open("./test/data.txt",O_RDONLY);
Channel chan(fd,&loop);
char* buf = new char[1024];
chan.read(100,buf,readcb);
loop.loop();
delete buf;
}
Main 函数中读取一个文件 data.txt 的内容,并打印到 console 上。
运行结果
Data.txt 的内容:
附录:
#ifndef EVENTLOOP
#define EVENTLOOP
#include <liburing.h>
#include "Channel.hpp"
#include "ServerChannel.h"
#include <vector>
#include <memory>
#define QUEUE_SIZE 1024
class Channel;
class ServerChannel;
class Eventloop{
public:
Eventloop();
void submitRead(Channel* channel);
void submitWrite(Channel* channel);
void submitServerRead(Channel* channel);
void addChannel(int fd);
void loop();
~Eventloop();
// data member
std::vector<std::shared_ptr<Channel>> channel_list_;
struct io_uring ring_;
};
#endif
#include
#include
#include
#include
#include
"Eventloop.h"
<errno.h>
<stdio.h>
<liburing.h>
<string.h>
Eventloop::Eventloop()
{
io_uring_queue_init(QUEUE_SIZE,&ring_,0);
}
void Eventloop::submitRead(Channel* chan)
{
io_uring_sqe* sqe = io_uring_get_sqe(&ring_);
io_uring_sqe_set_data(sqe,chan);
io_uring_prep_read(sqe,chan->fd_,chan->readbuf,chan->readsize,0);
io_uring_submit(&ring_);
}
void Eventloop::submitWrite(Channel* chan)
{
io_uring_sqe* sqe = io_uring_get_sqe(&ring_);
io_uring_sqe_set_data(sqe,chan);
io_uring_prep_write(sqe,chan->fd_,chan->writebuf,chan->writesize,-1)
;
io_uring_submit(&ring_);
}
Eventloop::~Eventloop()
{
io_uring_queue_exit(&ring_);
}
void Eventloop::loop()
{
struct io_uring_cqe* cqe;
int ret=0;
while(true)
{
if((ret=io_uring_wait_cqe(&ring_, &cqe)!=0))
{
fprintf(stderr,"io_uring_wait_cqe failed:%s", strerror(ret));
}
Channel* chan =
static_cast<Channel*>(io_uring_cqe_get_data(cqe));
// fprintf(stdout,"cqe->res=%d\n",cqe->res);
chan->handleEvent(cqe->res);
io_uring_cqe_seen(&ring_,cqe);
// io_uring_submit(&ring_);
}
}
void Eventloop::submitServerRead(Channel* chan)
{
io_uring_sqe* sqe = io_uring_get_sqe(&ring_);
io_uring_sqe_set_data(sqe,chan);
io_uring_prep_accept(sqe,chan->fd_,nullptr,0,0);
io_uring_submit(&ring_);
}
void Eventloop::addChannel(int fd)
{
std::shared_ptr<Channel> ptr = std::make_shared<Channel>(fd,this);
this->channel_list_.push_back(ptr);
}
#ifndef CHANNEL
#define CHANNEL
#include <liburing.h>
#include <vector>
#include <functional>
#include "Eventloop.h"
class Eventloop;
class Channel
{
public:
Channel(int fd, Eventloop* loop);
void setReadCallback(std::function<void(Channel*)> callback)
{
this->readCallback = callback;
}
void setWriteCallback(std::function<void(Channel*)> callback)
{
this->writeCallback = callback;
}
void setListenCallback(std::function<void(Channel*)> callback)
{
this->listenCallback = callback;
}
void handleEvent(int res);
void setReadState();
void setWriteState();
void setListenState()
{
this->state = LISTEN;
}
void write(void* buf,int size,std::function<void(Channel*)> cb);
void read(int size,void* buf,std::function<void(Channel*)> cb);
void accept();
// data member
enum State{READ,WRITE,LISTEN};
State state;
int fd_;
Eventloop* loop_;
void* readbuf;
int readsize;
void* writebuf;
int writesize;
int res;
std::function<void(Channel*)> readCallback;
std::function<void(Channel*)> writeCallback;
std::function<void(Channel*)> listenCallback;
};
#endif
#include "Channel.hpp"
#include "Eventloop.h"
#include <stdio.h>
#include <stdlib.h>
Channel::Channel(int fd, Eventloop* loop)
{
fd_ = fd;
loop_ = loop;
}
void Channel::setReadState()
{
state = READ;
}
void Channel::setWriteState()
{
state = WRITE;
}
void Channel::handleEvent(int res)
{
// fprintf(stdout,"channel::handleEvent:res = %d\n",res);
if (res < 0) {
fprintf(stderr, "Async readq failed.\n");
exit(1);
}
this->res = res;
switch(state)
{
case READ:
readCallback(this);
break;
case WRITE:
writeCallback(this);
break;
case LISTEN:
listenCallback(this);
default:
;
}
}
void Channel::write(void* buf, int size,std::function<void(Channel*)> cb)
{
writebuf = buf;
writesize = size;
loop_->submitWrite(this);
writeCallback = cb;
setWriteState();
}
void Channel::read(int size,void* buf,std::function<void(Channel*)> cb)
{
this->readbuf = buf;
this->readsize = size;
this->readCallback = cb;
this->loop_->submitRead(this);
setReadState();
}
void Channel::accept()
{
loop_->submitServerRead(this);
setListenState();
}
#include
#include
#include
#include
<iostream>
"Eventloop.h"
<unistd.h>
<stdio.h>
void readcb(Channel* chan)
{
fprintf(stdout,"i am readcb\n");
char* buf = (char*)(chan->readbuf);
int size = chan->res;
buf[size] = '\0';
// fprintf(stdout,"size=%d\n",size);
fprintf(stdout,"%s\n",buf);
fflush(stdout);
}
int main(int argc, char* argv[])
{
Eventloop loop;
int fd = open("./test/data.txt",O_RDONLY);
Channel chan(fd,&loop);
char* buf = new char[1024];
chan.read(100,buf,readcb);
loop.loop();
delete buf;
}
0
You can add this document to your study collection(s)
Sign in Available only to authorized usersYou can add this document to your saved list
Sign in Available only to authorized users(For complaints, use another form )