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

 

这篇文章主要总结下这几个概念,前几天看到微博里在讨论,当时也有点搞不清楚,昨天在看到Reactor和Proactor模式的时候,又提到相关概念,索性搞搞清楚,写个总结。

 

《Unix网络编程卷1:套接字联网API》(下面称为卷1)第6章对Unix I/O模型有5种划分:阻塞式I/O模型,非阻塞式I/O模型,I/O复用模型,信号驱动式I/O,异步I/O模型。这里我们只关心跟我们主题相关的四类:

阻塞式I/O模型:应用进程调用I/O操作时阻塞,只有等待要操作的数据准备好,并复制到应用进程的缓冲区中才返回。

非阻塞式I/O模型:当应用进程要调用的I/O操作会导致该进程进入阻塞状态时,该I/O调用返回一个错误,一般情况下,应用进程需要利用轮询的方式来检测某个操作是否就绪。数据就绪后,实际的I/O操作会等待数据复制到应用进程的缓冲区中以后才返回。

I/O复用模型:阻塞发生在select/poll的系统调用上,而不是阻塞在实际的I/O系统调用上。select/poll发现有数据就绪后,通过实际的I/O操作将数据复制到应用进程的缓冲区中。

异步I/O模型:应用进程通知内核开始一个异步I/O操作,并让内核在整个操作(包含将数据从内核复制到应该进程的缓冲区)完成后通知应用进程。

 

对于上面的分类,卷1给出了一个很形象的图,如下:

 

从上面图中可以看出,卷1中把I/O操作分为两个阶段,第一阶段等待数据可用,第二阶段将数据从内核复制到用户空间。前三种模型的区别在于第一阶段(阻塞式I/O阻塞在I/O操作上,非阻塞式I/O轮询,I/O复用阻塞在select/poll或者epoll上),第二阶段都是一样的,即这里的阻塞不阻塞体现在第一阶段,从这方面来说I/O复用类型也可以归类到阻塞式I/O,它与阻塞式I/O的区别在于阻塞的系统调用不同。而异步I/O的两个阶段都不会阻塞进程。

 

我们再来看看同步I/O与异步I/O(AIO),根据卷1的说明,同步I/O与异步I/O是由POSIX定义的两个术语:

同步I/O操作:实际的I/O操作将导致请求进程阻塞,直到I/O操作完成。

异步I/O操作:实际的I/O操作不导致请求进程阻塞。

由此定义来看,前面分类中的前三种:阻塞式I/O,非阻塞式I/O,I/O复用都属于同步I/O,因为第二阶段的数据复制都是阻塞的。而只有前面定义的异步I/O模型与这里的异步I/O操作吻合。

由异步I/O的定义来看,操作系统必须提供一种方式,在应用进程发出I/O操作后,可以在后台(而不是当前应用进程)完成数据等待和数据复制的工作,并最终通知应用进程I/O操作已经完成。

 

在Linux下有两种称为AIO的的接口。一个是由glibc提供,是由多线程来模拟:数据等待和数据复制的工作,由glibc创建线程来完成。数据复制完成后,执行I/O操作的线程通过回调函数的方式通知应用线程(严格来讲,这种方式不能算真正的AIO,因为用来执行实际I/O操作的线程还是阻塞在I/O操作上,只不过从应用进程的角度来看是异步方式的)。另一种是由内核提供的Kernel AIO,可以做到真正的内核异步通知(这种方式对读写方式,写入大小及偏移都有严格的要求),并且不支持网络I/O[1][2],其实现原理本质上与下面要介绍的IOCP类似。

 

还有一种称为IOCP(Input/Output Completion Port)的AIO。从实现原理上讲,IOCP做完I/O操作后,将结果封装成完成包(completion packet)入队到完成端口的队列(FIFO)中去,应用线程从队列中读取到完成消息后,处理后续逻辑。从这方面来讲,IOCP类似生产者-消费者模型:生产者为内核,收到应用线程的I/O请求后,等待数据可用,并将结果数据复制到应用线程指定的缓冲区中后,然后入队一个完成消息;消费者为应用线程,一开始向内核提交I/O请求,并在队列上等待内核的完成消息(只不过,IOCP对同时可运行的消费者有限制),收到完成消息后,进行后续处理[3]。

 

从上面对Linux kernel AIO以及IOCP的介绍可以看出,这两种异步I/O操作的完成通知是通过入队消息到消息队列的方式来完成的,应用进程必须阻塞在消息队列上来等待完成消息(别被这里的阻塞混淆,AIO定义中的阻塞是指实际的I/O操作)。

 

Reactor与Proactor模式就分别对应同步I/O和异步I/O[4]:Reactor是在事件就绪时通知应用进程,应用进程需要完成实际的I/O操作;而Proactor是在I/O操作已经完成的时候(数据就绪,并且已经拷贝到应用进程的缓冲区中,实际的I/O操作由操作系统来完成)通知应用进程。

 

AIO在服务器设计方面很少被用到[5],更多的使用在本地I/O方面[6]。

 

总结:

其实要搞清楚这些概念,主要是搞清楚这些概念描述的主体是什么:

阻塞或者非阻塞I/O主要是指I/O操作第一阶段的完成方式,即数据还未准备好的时候,应用进程的表现,如果这里进程挂起,则为阻塞I/O,否则为非阻塞I/O。

 

同步或者异步I/O主要是指实际I/O操作的完成方式,同步意味着由应用进程发起并完成I/O操作,I/O操作未完成前,会导致应用进程挂起;异步意味着应用进程只发出I/O请求,并接收完成通知,实际I/O操作由系统完成,I/O操作进行时,应用进程可以继续工作。

 

参考资料:

1. http://cnodejs.org/topic/4f16442ccae1f4aa270010a7/

2. http://lse.sourceforge.net/io/aio.html

3. http://60.251.1.52/taiwan/technet/sysinternals/information/iocompletionports.mspx

4. http://www.artima.com/articles/io_design_patterns2.html

5. http://www.kegel.com/c10k.html#aio

6. http://blog.yufeng.info/archives/741

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值