Unix下共有五种网络I/O模型 :
a. 阻塞I/O:进程会一直阻塞,直到数据拷贝完成
b. 非阻塞I/O:I/O请求时加上O_NONBLOCK一类的标志位,立刻返回,I/O没有就绪会返回错误,需要请求进程主动轮询不断发I/O请求直到返回正确;在数据拷贝的过程中,进程是阻塞的
c. I/O复用(select和poll):与非阻塞I/O本质一样,不过利用了新的select系统调用,由内核来负责本来是请求进程该做的轮询操作。比非阻塞I/O多了一个系统调用开销,并没有什么优越性,关键是能实现同时对多个I/O端口进行监听。在这种模型中,配置的是非阻塞I/O,然后使用阻塞 select系统调用来确定一个I/O描述符何时有操作。
d. 信号驱动I/O(SIGIO):调用sigaltion系统调用,当内核中I/O数据就绪时以SIGIO信号通知请求进程,请求进程再把数据从内核读入到用户空间,这一步是阻塞的。
e. 异步I/O(Posix.1的aio_系列函数):不会因为I/O操作阻塞,I/O操作全部完成才通知请求进程。
5个I/O模型的比较:
前4个模型都是同步I/O,只有最后1个异步I/O模型是异步I/O。
一个I/O操作其实分成了两个步骤:发起I/O请求(等待数据)和实际的I/O操作(将数据从内核拷贝到用户空间)。
同步I/O和异步I/O的区别就在于第二个步骤是否阻塞:
1)如果实际的I/O读写阻塞请求进程,那么就是同步I/O,因此阻塞IO、非阻塞IO、IO服用、信号驱动I/O都是同步I/O;
2)如果不阻塞,而是操作系统帮你做完IO操作再将结果返回给你,那么就是异步I/O。
阻塞I/O和非阻塞I/O的区别在于第一步,发起I/O请求是否会被阻塞:
1)如果阻塞直到完成那么就是传统的阻塞I/O;
2)如果不阻塞,那么就是非阻塞I/O。
引用UNP第一卷中对各个模型使用的例子,以了解各个模型应用场景:
不同版本的str_cli执行时间:
1)354.0秒,阻塞版本
2)12.3秒,select加阻塞I/O版本
3)6.9秒,select加非阻塞I/O版本
4)8.7秒,fork版本
5)8.5秒,线程化版本
非阻塞几乎快出select加阻塞版本一倍;fork版本比非阻塞版本稍慢,然而fork版本比非阻塞版本代码简单的多。
技术演化:阻塞->多线程阻塞(优点:阻塞时只是一个线程阻塞而不会影响其他线程工作;缺点:大量连接时线程过多,竞争资源时线程间同步复杂)->select加阻塞->select加非阻塞->异步I/O
PS:DOS(denial of service,拒绝服务攻击)解决的方法:
1)使用非阻塞I/O
2)让每个客户由单独的控制线程提供服务
3)对I/O操作设置一个超时
参考:
Unix下五种I/O模型:http://blog.youkuaiyun.com/sunyubo458/article/details/6096723
使用异步 I/O 大大提高应用程序的性能:http://www.ibm.com/developerworks/cn/linux/l-async/