Uploaded by 1554273827

互联网程序设计

advertisement
互联网程序设计
姓名:黄健锋 学号: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;
}
Download