Java NIO分析(5): I/O多路复用之epoll系统调用
1. epoll概念
poll
系统调用相比于select
主要解决了文件描述符的数量限制,但是在高并发场景下没有解决根本问题:
- fd数组整体在内核空间和用户空间之间拷贝
- 遍历整个fd数组找事件浪费资源
这俩性能问题在Banga在1999年写了篇论文A Scalable and Explicit Event
Delivery Mechanism for UNIX,提出select
和poll
都是无状态的,需要用户空间的进程自行遍历查找事件, 一种改进方案是内核内部自己维护事件集合.通过一个类似declare_interest
的系统调用,内核能够增量得更新进程感兴趣的事件集合列表, 应用进程通过使用get_next_event
调用能派发新事件给内核。
根据论文的研究成果,LINUX
和FreeBSD
各自给出的解决方案:epoll
和kqueue
.我们主要讨论epoll, 毕竟日常服务端环境都是LINUX.
在LINUX内核2.6以上,epoll
才受到支持。
2. epoll函数
epoll操作过程有3个函数
#include <sys/epoll.h>
int epoll_create(int size);
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
epoll_create
创建一个epoll fd
和事件表, 在LINUX2.6.8以后是使用红黑树来管理epoll事件表,所以size没有太大作用epoll_ctl
操作上面创建的epoll事件表, 可以加入socket读写事件epoll_wait
类似于以前的select
和poll
,得到发生的事件, 如socket可读可写
epoll_ctl
的第二个参数使用3个宏来表示动作:
EPOLL_CTL_ADD
: 注册新的fd到epfd中EPOLL_CTL_MOD
: 修改意见注册的fd的监听事件EPOLL_CTL_DEL
: 从epfd中删除一个fd
第四个参数用来告诉内核需要监听什么事件, epoll_event
结构如下
|
|
epoll事件和以前poll
的事件类型差不多,主要还是这仨:
- EPOLLIN: 对应的fd可读
- EPOLLOUT: 对应的fd可写
- EPOLLERR: 对应的fd发送初五
3. epoll实战
首先,MacOS是基于BSD的,所以是没有epoll
函数的,在Mac下开发,得去LINUX环境编译。依然是老5步
- 新建socket, 用于监听端口
- socket的fd绑定端口
- socket监听
- 创建epoll fd
- 发起epoll_wait获取事件,使用epoll_ctl管理事件
|
|
使用gcc编译,运行
|
|
然后使用nc
测试就好了
4. epoll总结
epoll
解决了select
和poll
时代遗留的2个性能问题,不需要使fd数组整体在内核空间和用户空间之间来回拷贝,同时
不需要应用进程遍历整个fd数组以查找发生的事件。epoll
使用mmap
加速了内核和用户空间的消息传递,避免不必要的内存拷贝。
epoll
只会返回活跃的socket fd,所以I/O效率不会随着fd数目增加而显著下降。
epoll
还支持ET(边缘触发)
和LT(水平触发)
,这些就不细讲了。
- 原文作者:Chris Wang
- 原文链接:https://www.sound2gd.wang/post/java-nio%E5%88%86%E6%9E%905-i/o%E5%A4%9A%E8%B7%AF%E5%A4%8D%E7%94%A8%E4%B9%8Bepoll%E7%B3%BB%E7%BB%9F%E8%B0%83%E7%94%A8/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。