导航

阻塞与非阻塞、同步与异步 I/O模型

Posted on 2014-06-02 18:15  蝈蝈俊  阅读(2192)  评论(0编辑  收藏  举报

I/O模型

Linux 下的五种I/O模型

  • 阻塞I/O(blocking I/O)
  • 非阻塞I/O (nonblocking I/O)
  • I/O复用(select 和poll) (I/O multiplexing)
  • 信号驱动I/O (signal driven I/O (SIGIO))
  • 异步I/O (asynchronous I/O (the POSIX aio_functions))

前四种都是同步,只有最后一种才是异步IO。

 

Winsock的异步IO模型有下面六种

  • select选择模型
  • WSAAsyncSelect 异步选择模型
  • WSAEventSelect 事件选择模型
  • Overlapped I/O 事件通知模型
  • Overlapped I/O 完成例程模型
  • IOCP模型

 

Linux 的几种I/O模型介绍:

阻塞I/O模型:

简介:进程会一直阻塞,直到数据拷贝完成
应用程序调用一个IO函数,导致应用程序阻塞,等待数据准备好。 如果数据没有准备好,一直等待….数据准备好了,从内核拷贝到用户空间,IO函数返回成功指示。

1334216532_9745

 

可能阻塞套接字的Windows Sockets API调用分为以下四种:

1.输入操作: recv()、recvfrom()、WSARecv()和WSARecvfrom()函数。以阻塞套接字为参数调用该函数接收数据。如果此时套接字缓冲区内没有数据可读,则调用线程在数据到来前一直睡眠。

2.输出操作: send()、sendto()、WSASend()和WSASendto()函数。以阻塞套接字为参数调用该函数发送数据。如果套接字缓冲区没有可用空间,线程会一直睡眠,直到有空间。

3.接受连接:accept()和WSAAcept()函数。以阻塞套接字为参数调用该函数,等待接受对方的连接请求。如果此时没有连接请求,线程就会进入睡眠状态。

4.外出连接:connect()和WSAConnect()函数。对于TCP连接,客户端以阻塞套接字为参数,调用该函数向服务器发起连接。该函数在收到服务器的应答前,不会返回。这意味着TCP连接总会等待至少到服务器的一次往返时间。

非阻塞I/O模型

简介:非阻塞IO通过进程反复调用IO函数(多次系统调用,并马上返回);在数据拷贝的过程中,进程是阻塞的;

我们把一个SOCKET接口设置为非阻塞就是告诉内核,当所请求的I/O操作无法完成时,不要将进程睡眠,而是返回一个错误。这样我们的I/O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。在这个不断测试的过程中,会大量的占用CPU的时间。

 

1334216607_3004

非阻塞套接字在控制建立的多个连接,在数据的收发量不均,时间不定时,明显具有优势。这种套接字在使用上存在一定难度,但只要排除了这些困难,它在功能上还是非常强大的。

 

I/O复用模型

简介:主要是select和epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听;

I/O复用模型会用到select、poll、epoll函数,这几个函数也会使进程阻塞,但是和阻塞I/O所不同的的,这两个函数可以同时阻塞多个I/O操作。而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。

 

1334216620_6310

 

 

信号驱动I/O模型

 

简介:两次调用,两次返回;

首先我们允许套接口进行信号驱动I/O,并安装一个信号处理函数,进程继续运行并不阻塞。当数据准备好时,进程会收到一个SIGIO信号,可以在信号处理函数中调用I/O操作函数处理数据。

 

1334216632_6025

 

异步I/O模型

简介:数据拷贝的时候进程无需阻塞。
当一个异步过程调用发出后,调用者不能立刻得到结果。实际处理这个调用的部件在完成后,通过状态、通知和回调来通知调用者的输入输出操作

1334216641_7821

 

5种I/O模型的比较:

 

1334216724_2405

 

 

Windows I/O模型介绍

网上有个比喻很好的说明这些模型,在这里引用一下。
老陈有一个在外地工作的女儿,不能经常回来,老陈和她通过信件联系。他们的信会被邮递员投递到他们的信箱里。

select模型


老陈非常想看到女儿的信。以至于他每隔10分钟就下楼检查信箱,看是否有女儿的信~~~~~
在这种情况下,“下楼检查信箱”然后回到楼上耽误了老陈太多的时间,以至于老陈无法做其他工作。

WSAAsyncSelect模型


后来,老陈使用了微软公司的新式信箱。这种信箱非常先进,一旦信箱里有新的信件,盖茨就会给老陈打电话:喂,大爷,你有新的信件了!从此,老陈再也不必频繁上下楼检查信箱了,牙也不疼了,你瞅准了,蓝天……不是,微软~~~~~~~~

WSAEventSelect模型


后来,微软的信箱非常畅销,购买微软信箱的人以百万计数……以至于盖茨每天24小时给客户打电话,累得腰酸背痛,喝蚁力神都不好使~~~~~~
微软改进了他们的信箱:在客户的家中添加一个附加装置,这个装置会监视客户的信箱,每当新的信件来临,此装置会发出“新信件到达”声,提醒老陈去收信。盖茨终于可以睡觉了。

Overlapped I/O 事件通知模型


后来,微软通过调查发现,老陈不喜欢上下楼收发信件,因为上下楼其实很浪费时间。于是微软再次改进他们的信箱。新式的信箱采用了更为先进的技术,只要用户告诉微软自己的家在几楼几号,新式信箱会把信件直接传送到用户的家中,然后告诉用户,你的信件已经放到你的家中了!老陈很高兴,因为他不必再亲自收发信件了!

Overlapped I/O 完成例程模型


老陈接收到新的信件后,一般的程序是:打开信封—-掏出信纸—-阅读信件—-回复信件……为了进一步减轻用户负担,微软又开发了一种新的技术:用户只要告诉微软对信件的操作步骤,微软信箱将按照这些步骤去处理信件,不再需要用户亲自拆信/阅读/回复了!老陈终于过上了小资生活!

IOCP模型


微软信箱似乎很完美,老陈也很满意。但是在一些大公司情况却完全不同!这些大公司有数以万计的信箱,每秒钟都有数以百计的信件需要处理,以至于微软信箱经常因超负荷运转而崩溃!需要重新启动!微软不得不使出杀手锏……
微软给每个大公司派了一名名叫“Completion Port”的超级机器人,让这个机器人去处理那些信件!

其实,上面每种模型都有优点,要根据程序需求而适当选择合适的模型,前面三种模型效率已经比较高,实现起来难道不大,很多一般的网络程序都采用前三种模型,只有对网络要求特别高的一些服务器才会考虑用后面的那些模型。MFC中的CAsyncSocket类就是用的WSAAsyncSelect模型,电驴中也是用的这种,不过在寻找对应socket的时候进行了优化,查找更快,在GridCast中采用的是WSAEventSelect模型,等待。

 

Java的I/O模型

OIO

OIO 就是同步I/O,

NIO

在JDK 1.4版本之后才开始支持异步IO,其实NIO也是使用操作系统的IO模型,但在各操作系统上的实现方式也不太一样

在Windows系统使用的是Select模型,而不是性能更高的IOCP,原因就是并非所有Windows都支持IOCP,IOCP在windows NT 3.5中被引入,只支持WindowsNT和windows 2000。(参考:http://www.cnblogs.com/jobs/archive/2006/11/22/568023.html)

在Linux系统上使用的是多路复用IO,JDK 6之前用的是poll模型,JDK 7中使用epoll。(参考:http://www.cnblogs.com/jobs/archive/2006/11/22/568022.html

AIO

JDK 7中出现了AIO,其实AIO就是NIO的增强版,因为JDK 6之前用的是poll,JDK 7用的是epoll,而epoll就是poll的增强版。

 

 

参考资料:

http://blog.csdn.net/hguisu/article/details/7453390

Java NIO和操作系统I/O模型
http://wugc.iteye.com/blog/986997 

IO - 同步,异步,阻塞,非阻塞 (亡羊补牢篇)
http://blog.csdn.net/historyasamirror/article/details/5778378

Java NIO原理 图文分析及代码实现
http://weixiaolu.iteye.com/blog/1479656

Java IO模型
http://blog.csdn.net/shanpengfei77/article/details/8161077

Windows I/O模型、同步/异步、阻塞/非阻塞
http://www.linuxnote.org/windows-i-o-model-synchronous-asynchronous-blocking-non-blocking.html