代码改变世界

boost.asio的一点备忘

2011-04-08 14:22  leo_hello  阅读(2579)  评论(0编辑  收藏  举报

这两天事情不多,简单看了下boost.asio的源码,因为asio采用proactor模式,而windows下的IOCP本身就是这个模式的体现,所以将精力集中在了asio在linux下的实现(asio在windows下采用了IOCP,linux下用epoll,还有其它的一些实现如kqueue,select等).

在高性能服务器并发模型设计中,Reactor和Proactor是两个经常用到的设计模式,前者用于同步IO,后者用于异步IO,前者在IO操作就绪的情况下通知用户,用户再采取实际的IO操作,后者是在IO操作完成后通知用户,举个简单的例子,比如说你有一封信到了邮局,Reactor模式就是邮局的人到你家来告诉你说你有信件,然后你到邮局去拿,而Proactor模式则是邮局的把信件送到了你家.在我看来IOCP的设计就是Proactor模式的完美体现,而epoll则很容易实现Reactor模式,asio设计为跨平台,并且在linux下采用epoll,我的印象中linux对异步IO的支持没有windows那么完善(看看IOCP和epoll模型的区别就知道),那么asio是怎么用epoll机制实现proactor模式的呢,刚开始想的是应该在应用层做了一层封装,就是asio内部应该有某个循环调用epoll_wait,当有IO事件就绪时帮用户做一些操作(比如把数据拷贝到我们提交的缓冲区),然后在操作完成的时候调用我们的handler(后来看代码基本是这样).OK,不说废话了.

几个重要的类:

io_service: 这是asio中最重要的一个类,任何其它类基本都需要这个类作为成员类构造,这个类主要实现了proactor模式,它有一个成员io_service_impl,就是它的实现,这个实现根据平台的不同而不同,如下:

#if defined(BOOST_ASIO_HAS_IOCP)
namespace detail { typedef win_iocp_io_service io_service_impl; }
#else
namespace detail { typedef task_io_service io_service_impl; }
#endif

如果IOCP可用采用IOCP的实现,否则采用后者.

task_io_service:作为proactor模式在非win下的具体实现,主要功能有对IO是否就绪的不停扫描,以及事件到达后对线程池的统筹协作等,它的几个比较重要的成员如reactor(这是一个typedef定义的同义词,不同非win平台有不同的实现),op_queue<operation>(回调函数对象列表,这里面的每一个operation都会在调用了run函数的用户线程里面执行,per op per thread),其它还有一些标志和同步对象之类的.

epoll_reactor:上面reactor在linux下的实现,采用的是epoll机制,对IO是否就绪不停扫描,我们熟悉的epoll_wait就在这个类的run函数里面,具体的过程是1发现IO就绪,2取得相应回调压入task_io_service的op_queue成员里面,3循环直到一次epoll_wait返回的就绪事件全部处理.

basic_stream_socket<tcp>:

basic_socket_acceptor<tcp>:这两个东东都是对套接字的封装,都是基于TCP协议(协议可变),前者用于普通数据流套接字,后者用于监听套接字,它们的成员前者有同步或异步的发送接收操作,后者有同步或异步的accept等,当然,根据asio的习惯,这两个类肯定自己是不会实现这些功能的,都交给了另外一个干实事的类:reactive_socket_service.

reactive_socket_service:前面说过这个类真正实现了上面两个套接字的具体功能,具体干了些什么呢,我们不妨想一下,我们在调用一些异步IO操作的时候,提供了操作的具体类型和套接字,它如果要起作用还必须和epoll关联起来,这样我们的事件多路分发器才会关注我们提交的事件,而这个类正是做这项工作的,在我们的异步调用中,它会封装我们提交的回调函数,并注册我们我们感兴趣的事件到epoll中(它是有epoll_reactor引用的哦,有了它,一切事情都好办了....)

OK,再来看看一个asio工作的一个流程(以linux下epoll实现为例):

1,用户初始化一个io_service对象.相当于初始化了网络,包括reactor,也就是操作系统提供的同步事件多路分发器(linux下epoll的初始化),io_service在这个分发器的基础上封装实现proactor模式;

2,用户根据应用要求构造basic_stream_socket<tcp>,basic_socket_acceptor<tcp>,并调用相应的异步IO函数(用户负责提供回调,如果需要还要提供buff及数据等,这个操作会把IO事件源绑定到epoll,并设置相应的回调)

3.用户调用io_service的run函数(如上面说到的,这个函数会调用具体实现类的run函数,linux下会调用task_io_service的run函数,这个函数首先会检查op_queue是不是有事情做,有几个的话会唤醒调用了io_service.run的线程,如果只有一个则自己去做,同时解锁,让另外的线程接替自己来轮询,如果op_queue为空的话会调用epoll_wait,看看是否有新到达的事件,有一点需要注意的是,如果需要使服务器一直轮询,就必须保持至少有一个未完成的异步IO请求,否则run函数就要返回了)

这仅仅是一个大体的框架,还有许多实现的细节以后慢慢去体会,Keep Learnning!!