在编写服务端软件的时候,如何处理各种I/O事件是其中很重要的一部分。在Unix Network Programming中介绍了5种Unix/Linux下可用的I/O编程模型:
1 阻塞式I/O
2 非阻塞式I/O
3 I/O复用
4 信号驱动式I/O
5异步I/O
这几种都是基本的I/O编程模型,可以单独使用其中一种,也可以组合使用。为了应对高并发量的情形,在C10K Problem中另外总结了5种高性能的I/O编程模型:
1 单线程非阻塞式水平触发I/O(Serve many clients with each thread, and use nonblocking I/O and level-triggered readiness notification);
2 单线程非阻塞式边沿触发I/O(Serve many clients with each thread, and use nonblocking I/O and readiness change notification);
3 多线程异步I/O模型(Serve many clients with each server thread, and use asynchronous I/O);
4 单个服务线程对应单个客户(Serve one client with each server thread);
5 将服务线程放到内核(Build the server code into the kernel)。本文介绍的两种模型都是基于I/O多路复用的单线程处理模型。
Reactor模式是处理并发I/O比较常见的一种模式,中心思想就是,将所有要处理的I/O事件注册到一个中心I/O多路复用器上,同时主线程阻塞在多路复用器上;一旦有I/O事件到来或是准备就绪(区别在于多路复用器是边沿触发还是水平触发),多路复用器返回并将相应I/O事件分发到对应的处理器中。
这里有三个重要的组件:
因为这种模型经常使用,所有不少人对简单的系统调用做了一层封装,形成跨平台的事件处理库,比较典型的有:libevent,libev,boost asio等。
与Reactor模型相对应,Proactor最大的特点是使用异步I/O。所有的I/O操作都交由系统提供的异步I/O接口去执行。Proactor多路复用器等待异步I/O完成,并调用相应的用户处理函数。为了对比Reactor模型,以一个read操作为例:
在Reactor中:
在Proactor中: