本文源码:https://github.com/axin1240101543/netty(有什么问题可以提issue给我,一起学习,共同进步。)
一、流程图
二、源码狙击 - Selector.open()
调用openSelector方法,返回一个EpollSelectorImpl的实现
实现了一个数组用来存放fd
创建epoll文件描述符
调用native方法
C语言实现的调用Linux内核的函数epoll_create
三、源码狙击 - serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
将fd(socketChannel)添加到数组中
四、源码狙击 - selector.select();
调用native方法
C语言实现的调用Linux内核的函数epoll_ctl
调用native方法
C语言实现的调用Linux内核的函数epoll_wait
五、Epoll内核函数解析
1、epoll_create
int epoll_create(int size);
创建一个epoll实例,并返回一个非负数作为文件描述符,用于对epoll接口的所有后续调用。参数size代表可能会容纳size个描述符,但size不是一个最大值,只是提示操作系统它的数量级,现在这个参数基本上已经弃用了。
2、epoll_ctl
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
使用文件描述符epfd引用的epoll实例,对目标文件描述符fd执行op操作。
参数epfd表示epoll对应的文件描述符,参数fd表示socket对应的文件描述符。
参数op有以下几个值:
EPOLL_CTL_ADD:注册新的fd到epfd中,并关联事件event;
EPOLL_CTL_MOD:修改已经注册的fd的监听事件;
EPOLL_CTL_DEL:从epfd中移除fd,并且忽略掉绑定的event,这时event可以为null;
参数event是一个结构体
1 struct epoll_event {
2 __uint32_t events; /* Epoll events */
3 epoll_data_t data; /* User data variable */
4 };
5
6 typedef union epoll_data {
7 void *ptr;
8 int fd;
9 __uint32_t u32;
10 __uint64_t u64;
11 } epoll_data_t;
events有很多可选值,这里只举例最常见的几个:
EPOLLIN :表示对应的文件描述符是可读的;
EPOLLOUT:表示对应的文件描述符是可写的;
EPOLLERR:表示对应的文件描述符发生了错误;
成功则返回0,失败返回-1
3、epoll_wait
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
等待文件描述符epfd上的事件。
epfd是Epoll对应的文件描述符,events表示调用者所有可用事件的集合,maxevents表示最多等到多少个事件就返回,
timeout是超时时间。
六、小结
I/O多路复用底层主要用的Linux 内核函数(select,poll,epoll)来实现,windows不支持epoll实现,windows底层是基于winsock2的
select函数实现的(不开源)
select(jdk1.4) | poll | epoll(jdk1.5及以上) | |
操作方式 | 遍历 | 遍历 | 回调 |
底层实现 | 数组 | 链表 | 哈希表 |
IO效率 | 每次调用都进行线性遍历,时间复杂度为O(N) | 每次调用都进行线性遍历,时间复杂度为O(N) | 事件通知方式,每当有IO事件就绪,系统注册的回调函数就会被调用,事件复杂度O(1) |
最大连接 | 有上限 1024 | 无上限 | 无上限 |