由于最近再看nginx
,其采用了优于apache
的select
网络io
模型,所以稍微了解了下网络io
的模型的基础知识。
首先,介绍几种常见的I/O
模型及其区别,如下:
blocking I/O
nonblocking I/O
I/O multiplexing (select and poll)
signal driven I/O (SIGIO)
asynchronous I/O (the POSIX aio_functions)
不管Linux
的IO
模型的阻塞同步分类是如何分类,几种IO
模型的具体实现是确定的。这里借用《Unix 网络编程:卷一》的图片说明。
1 阻塞式IO模型
这个模型也是最容易理解的
程序调用和我们基本的程序编写是一致的:
fd = connect()
write(fd)
read(fd)
close(fd)
程序的read
必须在write
之后执行,当write
阻塞住了,read
就不能执行下去
2 非阻塞IO模型
从图中可以看出来,这是一个轮询的过程
每次用户询问内核是否有数据报准备好(文件描述符缓冲区是否就绪),当数据报准备好的时候,就进行拷贝数据报的操作。当数据报没有准备好的时候,也不阻塞程序,内核直接返回未准备就绪的信号,等待用户程序的下一次轮询。
3 I/O复用模型
IO
复用模型是多了一个select
函数,select
函数有一个参数是文件描述符集合,意思就是对这些的文件描述符进行循环监听,当某个文件描述符就绪的时候,就对这个文件描述符进行处理。
这种IO
模型是属于阻塞的IO
。但是由于它可以对多个文件描述符进行阻塞监听,所以它的效率比阻塞IO
模型高效。
4 信号驱动IO模型
信号驱动IO
模型是应用进程告诉内核:当你的数据报准备好的时候,给我发送一个信号哈,并且调用我的信号处理函数来获取数据报。这个模型是由信号进行驱动。
5 异步IO模型
异步IO
使用的不再是read
和write
的系统接口了,应用工程序调用aio_XXXX
系列的内核接口。
当应用程序调用aio_read
的时候,内核一方面去取数据报内容返回,另外一方面将程序控制权还给应用进程,应用进程继续处理其他事务。这样应用进程就是一种非阻塞的状态。
当内核的数据报就绪的时候,是由内核将数据报拷贝到应用进程中,返回给aio_read
中定义好的函数处理程序。很少有linux系统支持,windows
的IOCP
则是此模型
完全异步的I/O
复用机制,因为纵观上面其它四种模型,至少都会在由kernel copy data to appliction
时阻塞。而该模型是当copy
完成后才通知application
,可见是纯异步的。好像只有windows
的完成端口是这个模型,效率也很出色。
下面是以上五种模型的比较
可以看出,越往后,阻塞越少,理论上效率也是最优。
=====================分割线==================================
5种模型的比较比较清晰了,剩下的就是把select,epoll,iocp,kqueue
按号入座那就OK
了。
select
和iocp
分别对应第3种与第5种模型,那么epoll
与kqueue
呢?其实也于select
属于同一种模型,只是更高级一些,可以看作有了第4种模型的某些特性,如callback
机制。
那么,为什么epoll,kqueue
比select
高级?
答案是,他们无轮询。因为他们用callback
取代了。想想看,当套接字比较多的时候,每次select()
都要通过遍历FD_SETSIZE
个Socket
来完成调度,不管哪个Socket
是活跃的,都遍历一遍。这会浪费很多CPU时间。如果能给套接字注册某个回调函数,当他们活跃时,自动完成相关操作,那就避免了轮询,这正是epoll
与kqueue
做的。
windows or *nix (IOCP or kqueue/epoll)?
诚然,Windows
的IOCP
非常出色,目前很少有支持asynchronous I/O
的系统,但是由于其系统本身的局限性,大型服务器还是在UNIX
下。而且正如上面所述,kqueue/epoll
与 IOCP
相比,就是多了一层从内核copy
数据到应用层的阻塞,从而不能算作asynchronous I/O
类。但是,这层小小的阻塞无足轻重,kqueue
与epoll
已经做得很优秀了。
提供一致的接口,IO Design Patterns
实际上,不管是哪种模型,都可以抽象一层出来,提供一致的接口,广为人知的有ACE
,Libevent
这些,他们都是跨平台的,而且他们自动选择最优的I/O
复用机制,用户只需调用接口即可。说到这里又得说说2个设计模式,Reactor and Proactor
。有一篇经典文章http://www.artima.com/articles/io_design_patterns.html值得阅读,Libevent
是Reactor
模型,ACE
提供Proactor
模型。实际都是对各种I/O
复用机制的封装。
Java nio包是什么I/O机制?
我曾天真的认为java nio
封装的是IOCP
。。现在可以确定,目前的java
本质是select()
模型,可以检查/jre/bin/nio.dll
得知。至于java
服务器为什么效率还不错。。我也不得而知,可能是设计得比较好吧。。-_-。
=====================分割线==================================
总结一些重点:
- 只有
IOCP
是asynchronous I/O
,其他机制或多或少都会有一点阻塞。 select
低效是因为每次它都需要轮询。但低效也是相对的,视情况而定,也可通过良好的设计改善epoll, kqueue
是Reacor
模式,IOCP
是Proactor
模式。java nio
包是select
模型。。