首先介绍多路复用:一个进程监听多个文件描述符,Linux中一切皆文件,也就是一个进程管理多个客户端连接。
Select模型不断扫描文件描述符集合
Select模型缺点:
一个进程打开的fd(文件描述符)是有限制的,默认1024,所以select模型最大的并发数就是1024个。
每次select调用都会线性扫描全部的fd集合,时间复杂度O(n)。
用户/内核空间 内存拷贝问题。
对上面三个缺点 后序的poll和epoll给出了优化
1.Poll相对Select比较,提高了可打开文件描述符的数量。一个进程打开fd的限制------>整个系统可用打开的文件数目。
在1G内存的服务器,限制是10w左右。
2.内核查找红黑树中ready的socket放到就绪列表,这个过程使用了内核的中断机制,因为有就绪列表 此后 用户态使用的时候就不需要在遍历所有的fd
类似事件通知,只把准备好的fd告诉你,算是事件通知的方式。
3.和select轮询不断的从内核态向用户态复制fd相比,epoll在内核态中,准备了一个就绪链表起到缓冲作用,一次性复制多个就绪的fd.
epoll数据结构
使用红黑树存放了所有文件描述符节点。
使用链表存放就绪的文件描述符
AIO希望的是,你select,poll,epoll都需要用一个函数去监控一大堆fd,那么我AIO不需要了,你把fd告诉内核,你应用程序无需等待,内核会通过信号等软中断告诉应用程序,数据来了,你直接读了,所以,用了AIO可以废弃select,poll,epoll。
但linux的AIO的实现方式是内核和应用共享一片内存区域,应用通过检测这个内存区域(避免调用nonblocking的read、write函数来测试是否来数据,
因为即便调用nonblocking的read和write由于进程要切换用户态和内核态,仍旧效率不高)来得知fd是否有数据,可是检测内存区域毕竟不是实时的,你需要在线程里构造一个监控内存的循环,设置sleep,总的效率不如epoll这样的实时通知。
所以,AIO是渣,适合低并发的IO操作。所以java7引入的NIO.2引入的AIO对高并发的网络IO设计程序来说,也是渣,只有Netty的epoll+edge-triggered notification最牛,能在linux让应用和OS取得最高效率的沟通。