select的原理以下:linux
用户程序发起读操做后,将阻塞查询读数据是否可用,直到内核准备好数据后,用户程序才会真正的读取数据。windows
poll与select的原理类似,用户程序都要阻塞查询事件是否就绪,但poll没有最大文件描述符的限制。
epoll是select和poll的改进,原理图以下:
epoll使用“事件”的方式通知用户程序数据就绪,而且使用内存拷贝的方式使用户程序直接读取内核准备好的数据,不用再读取数据
使用Netty构建服务器时,须要指定parent线程池和child线程池,parent线程负责监听端口,一旦有链接接入,则注册到child线程池中的一个线程上,该链接的IO操做/任务都由该线程完成。
换句话说,一个线程会负责多个链接的IO操做,也就是多路复用。Netty底层是使用系统提供的select或者epoll来实现多路复用的。数组
先来科普下select/poll/epoll。服务器
服务端创建每一个链接,至关于打开文件,会得到对应的文件描述符(fd),相同的源IP/源端口/目标IP/目标端口对应同一个fd。app
select和poll是类似的,不同的地方是,select是使用数组,有链接数限制,而poll使用链表,无链接数限制。ide
监听链接时,从用户层的角度看,
(1)会构建3个fd数组,分别关注读/写/异常事件,设置超时时间,调用系统提供的select方法。
(2)调用select方法时,须要将fd数组传到内核态,等待部分fd就绪后,把fd数组(包含就绪状态)返回到用户态
(3)用户程序对fd数组进行遍历,处理就绪的fd
(4)从新调用select方法。oop
能够看出很差的地方是(1)每次都要传入fd数组,返回整个fd数组,致使了大量在用户空间和内核空间的相互拷贝。
(2)用户程序仍须要遍历fd数组才能找出就绪的fdspa
从系统层的角度看,调用select方法时
(1)遍历fd数组,对于每一个fd,调用其对应的poll方法(由设备对应的驱动程序实现),将fd所在线程加入等待队列,而且检查就绪状态,记录感兴趣的就绪状态。
(2)若是存在感兴趣的就绪状态,直接返回
(3)若是不存在感兴趣的就绪状态,进入休眠,等待fd就绪后,会唤醒等待队列中的线程
(4)被唤醒后,重复1-4的操做。.net
能够看出很差的地方是每次都须要检查全部fd。线程
epoll
epoll相对select改善了不少。
(1)在使用epoll时,首先会构建epoll对象。
(2)有链接接入时,会插入到epoll对象中,epoll对象里实际是一个红黑树+双向链表,fd插入到红黑树中,经过红黑树查找到是否重复
(3)一旦fd就绪,会触发回调把fd的插入到就绪链表中,并唤醒等待队列中的线程。
(4)调用epoll_wait方法时只须要检查就绪链表,若有则返回给用户程序,如没有进入等待队列。
因为epoll把fd管理起来,不须要每次都重复传入,并且只返回就绪的fd,所以减小了用户空间和内核空间的相互拷贝,在fd数量庞大的时候更加高效。
Netty能够选择使用不一样的多路复用技术。
NioEventLoop
NioEventLoop底层会根据系统选择select或者epoll。若是是windows系统,则底层使用WindowsSelectorProvider(select)实现多路复用;若是是linux,则使用epoll
当为select模式,在NioEventLoop对应的Selector中会维护着newKeys,updateKeys,cancelledKeys,分别是新增的fd,更新fd的感兴趣状态,取消fd监听。每当链接接入,或者断连,都会调用NioEventLoop的注册/解除注册方法,更新这几个集合。(这里是JDK11的实现,在JDK8中则直接更新PollArrayWrapper)
NioEventLoop在运行的时候,会不断的监听注册的链接,核心逻辑在doSelect方法中,主要的几个操做是
(1)processUpdateQueue,将newKeys,updateKeys,cancelledKeys中的key更新到PollArrayWrapper中
(2)subSelector.poll(),这里实际是调用native方法,会将PollArrayWrapper的fd拷贝至readFds/writeFds/exceptFds,并监听这些fd。
private native int poll0(long pollAddress, int numfds,
int[] readFds, int[] writeFds, int[] exceptFds, long timeout);
1
2
(3)updateSelectedKeys,遍历freadFds/writeFds/exceptFds,将就绪的fd存储到selectedKey中
当存在fd就绪后,doSelect方法返回,应用程序能够遍历selectedKey进行处理。
EpollEventLoop
EpollEventLoop底层使用epoll实现多路复用。
EpollEventLoop中会初始化epollFd、eventFd、timerFd。
epollFd是调用系统方法生成的epoll对象,后续会使用其管理全部须要监听的fd
eventFd是用于线程通信,程序会把eventFd添加到epollFd中,监听eventFd,一旦eventFd有操做,则会唤醒调用epoll_wait的线程。
timerFd是用于计时,一样的监听timerFd,一旦时间到达,则会唤醒调用epoll_wait的线程。
每当链接接入或者断连,都会调用epoll_ctl_add/epoll_ctl_del方法来操做epoll对象。
在EpollEventLoop的run方法中,会调用epoll_wait来监听全部fd,一旦有fd就绪,会拷贝至EpollEventArray中,应用程序遍历EpollEventArray处理全部就绪事件。
————————————————
版权声明:本文为CSDN博主「lbl2018」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处连接及本声明。
原文连接:https://blog.csdn.net/lblblbl...