WebServer项目——epoller详解

epoller的简介

web服务器需要与客户端之间发生大量的IO操作,这也是性能的瓶颈之一。在这个项目中,我们用IO多路复用技术中的epoll来尽可能地提高一下性能。

epoll区别于select和poll,不需要每次轮询整个描述符集合来查找哪个描述符对应的IO已经做好准备了,epoll采用事件驱动的方式,当有事件准备就绪后就会一次返回已经做好准备的所有描述符集合。

epoll提供的程序接口有:

1
int epoll_create(int size);

在内核中创建epoll实例并返回一个epoll文件描述符。 在最初的实现中,调用者通过 size 参数告知内核需要监听的文件描述符数量。如果监听的文件描述符数量超过 size, 则内核会自动扩容。而现在 size 已经没有这种语义了,但是调用者调用时 size 依然必须大于 0,以保证后向兼容性。

1
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

向 epfd 对应的内核epoll 实例添加、修改或删除对 fd 上事件 event 的监听。op 可以为 EPOLL_CTL_ADD, EPOLL_CTL_MOD, EPOLL_CTL_DEL 分别对应的是添加新的事件,修改文件描述符上监听的事件类型,从实例上删除一个事件。如果 event 的 events 属性设置了 EPOLLET flag,那么监听该事件的方式是边缘触发。

1
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

当 timeout 为 0 时,epoll_wait 永远会立即返回。而 timeout 为 -1 时,epoll_wait 会一直阻塞直到任一已注册的事件变为就绪。当 timeout 为一正整数时,epoll 会阻塞直到计时 timeout 毫秒终了或已注册的事件变为就绪。因为内核调度延迟,阻塞的时间可能会略微超过 timeout 毫秒。

这个类抽象了epoll的操作,为上层的需求提供支撑。

epoller的组成

所需的变量和自定义的数据结构

我们需要用一个变量epollerFd_唯一地标记这个epoll,同时还需要一个vector来存储需要返回的准备就绪的事件。

构造函数与析构函数

析构函数默认即可,构造函数需要调用epoll_create函数来对epoll进行初始化。

对单个fd的操作

主要涉及的是将某个fd添加进epoll进行监控,将某个fd移除epoll取消监控和改变某个fd对应的事件,这些都是通过调用epoll_ctl方法实现的:

1
2
3
4
5
6
//将描述符fd加入epoll监控
bool addFd(int fd,uint32_t events);
//修改描述符fd对应的事件
bool modFd(int fd,uint32_t events);
//将描述符fd移除epoll的监控
bool delFd(int fd);

返回准备就绪的fd集合

这部分功能已经由epoll_wait方法实现了,也只是需要在这基础上封装一下就可以了:

1
int wait(int timewait = -1);

返回就绪fd的信息

主要就是提供对外接口,获取就绪IO的描述符和事件:

1
2
3
4
//获取fd的函数
int getEventFd(size_t i) const;
//获取events的函数
uint32_t getEvents(size_t i) const;

这部分主要在服务器需要处理就绪IO事件的时候需要调用。

epoller的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Epoller{
public:
explicit Epoller(int maxEvent=1024);
~Epoller();

//将描述符fd加入epoll监控
bool addFd(int fd,uint32_t events);
//修改描述符fd对应的事件
bool modFd(int fd,uint32_t events);
//将描述符fd移除epoll的监控
bool delFd(int fd);
//用于返回监控的结果,成功时返回就绪的文件描述符的个数
int wait(int timewait = -1);

//获取fd的函数
int getEventFd(size_t i) const;
//获取events的函数
uint32_t getEvents(size_t i) const;

private:
int epollerFd_;//这是标志epoll的描述符
std::vector<struct epoll_event>events_; //就绪的事件
};