WebServer项目——epoller详解
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 | //将描述符fd加入epoll监控 |
返回准备就绪的fd集合
这部分功能已经由epoll_wait方法实现了,也只是需要在这基础上封装一下就可以了:
1 | int wait(int timewait = -1); |
返回就绪fd的信息
主要就是提供对外接口,获取就绪IO的描述符和事件:
1 | //获取fd的函数 |
这部分主要在服务器需要处理就绪IO事件的时候需要调用。
epoller的实现
1 | class Epoller{ |