Linux的 网络IO
一、简介
1、Linux上所有外部设备都抽象为file。
2、对一个文件的读写会调用kernel命令,返回file descriptor
(fd);
3、对一个socket的读写会调用kernel命令,返回socketfd
4、描述符实际就是一个数字,指向内核中一个结构体(文件路径、数据区等一些属性)
二、Linux 对Socket IO模型的抽象
1、阻塞IO
缺省条件,所有file操作都是阻塞的(以socket为例)::在进程空间中调用recvfrom
,调用在数据包到达并被复制到应用进程的缓冲区中或者发生错误返回前 都会等待,即阻塞。
2、非阻塞IO
进程反复调用recvFrom
(轮询)确认缓冲区数据是否准备好。若没有准备好,返回EWOULDBLOCK 错误, 而当数据准备好后,系统进程开始从内核复制数据到用户空间。可见,在“等待数据准备就绪”期间,应用进程并不是“阻塞”的状态,而是不断“轮询”的。
(同步非阻塞?)
3、IO复用模型
Linux提供select/poll ,进程通过将一个或多个fd传给系统的selct /poll 调用,阻塞在select操作上,如此,select/poll可侦测多个fd是否处于就绪状态。
select/poll **顺序扫描fd是否就绪,且支持的fd数量有限,**因此使用受限。
而epoll:基于事件驱动方式代替顺序扫描,性能更好。若有fd就绪,立刻回调
(注意:IO复用模型中,在等待数据就绪期间,进程会阻塞在select
调用,等待socket
变可读上;
当数据可读时,OS将数据从内核复制到用户空间的过程也是阻塞的, 这个过程非常快,属于memory copy
,带宽通常在1GB/s级别以上,可以理解为基本不耗时)
4、信号驱动IO
先开启套接口信号驱动IO,再通过系统调用sigaction执行信号处理函数(调用后立刻返回,非阻塞的)。当数据准备就绪,为进程生成SIGIO信号,通过信号回调通过recvfrom读数据,通知主循环函数处理数据。
5 AIO
进程告知内核启动某操作,并让内核完成整个操作后(包括数据从内核复制到用户空间)再通知应用进程。
与信号驱动IO不同的是:信号驱动IO由内核通知何时开始一个IO;AIO由内核通知何时一个IO已经完成
JavaNIO是基于epoll的多路复用技术。
三、IO多路复用
多个client接入请求,可以利用多线程或者IO多路复用技术处理。
后者,是通过把多个IO的阻塞复用到同一个select阻塞上,从而使系统在单线程情况下能同时处理多个客户端请求。
与传统多线程或多进程模型相比: 不需要额外创建进程、线程,也不需要维护这些进程、线程,降低系统维护量、系统开销。
IO多路复用的场景:
1)服务器同时处理多个监听中、多个连接状态的套接字
2)服务器要同时处理多种网络协议的套接字
Linux中支持IO多路复用的有select pselect poll epoll
,最终还是选择了epoll
.epoll
好处:
1)支持一个进程打开的fd不受限制(当然小于OS的最大支持句柄)
select
最大缺陷:单进程打开的进程FD有限制,默认1024.对于要支持万个TCP的服务器来说太少。
cat /proc/sys/fs/file -max
看最大句柄,1G内存机器约10W句柄
2)IO不随FD数目增加线性下降
传统select/poll
:socket
集合很大,由于网络延时、链路空闲,少部分socket活跃,select/poll
线性扫描,效率线性下降。
epoll
只对活跃的socket操作::epoll根据fd上的callback函数实现,只有活跃的socket才主动调用
callback,idle的socket不会。如此,epoll 实现了伪AIO。
当活跃socket
多时,select/poll
效率比epoll更高,当活跃socket
少时,epoll
效率更高
3)使用mmap加速内核与用户空间的消息。
epoll通过内核和用户空间mmap
同一块内存来实现
4)epoll API比较简单
四、JavaIO演进
Java4 -NIO
Java7 -NIO 2.0 ::提供AIO,异步操作文件、socket