boost-io_service的工作方式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/twtydgo/article/details/50856937

io_service在ASIO中有着举足轻重的地位置如果对io_service的实现不感兴趣可以选择性无视这篇文章。在开始说io_service之前,先介绍一下ASIO给我们提供的可用于TCP服务器开发的组件。

boost::asio::ip::tcp::acceptor  用于监听与接受连接的类。
boost::asio::ip::tcp::socket  套接字,提供同步/异步IO方法,一个socket对象面向一个客户端连接。
boost::asio::deadline_timer  计时器,提供同步/异步计时功能并在计时结束之后回调到一个函数。

io_service的工作方式

把io_service想象为一个Worker Pool,也可以把它当作一个IO队列,他是一个联系用户和ASIO核心实现的类。如果你看过ASIO的例子或者用过ASIO,在构造上面表格中的对象时,往往需要给它们传递一个io_service对象,因为io_service担任的是一个Worker Pool的作用,这些组件的任何IO操作都会通过io_service.post()方法去投递一个任务。

为了更直观地看到io_service的作用,来一段异步定时器(deadline_timer)工作的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <boost/bind.hpp>  //for std::bind()
#include <boost/asio.hpp>
 
int main()
{
    void on_expired();
 
    boost::asio::io_service io_service;
    boost::asio::deadline_timer timer(io_service);
    timer.expires_from_now(boost::posix_time::milliseconds(3000));
    timer.async_wait(boost::bind(on_expired, boost::asio::placeholders::error));
    io_service.run();
 
    return 0;
}
 
void on_expired(const boost::system::error_code& error)
{
    if (!error)
    {
        std::cout << "timer expired." << std::endl;
    }
}

上面代码演示了一个异步定时器的工作流程:
1.定义一个io_service对象。
2.定义一个deadline_timer对象,并把io_service对象作为唯一参数传递给它。
3.使用expires_from_now()方法设置一个以当前时间为起始点的超时时间。
4.调用deadline_timer的async_wait()方法使定时器进入等待。
5.调用io_service的run()方法。

第4步调用async_wait()方法之后,deadline_timer的内部就做了一些和io_service打交道的事,它实则是向io_service提交了一个异步任务,io_service会把它放到队列里。第5步我们调用run()方法时,io_service就开始了它的工作,并把队列中的任务进行处理。

io_service用例图:

uml_io_service

这个用例图展示了io_service的工作方式:
1.用户的应用程序提交一个操作。
2.操作转发给io_service,告诉它我有一个异步操作可能要执行,此时io_service便会把该操作放入一个队列里。
3.调用run()方法启动io_service。
4.io_service启动后,会把队列中的操作提交给操作系统,相当于一个联系了用户和操作系统的媒介。
5.操作系统把执行结果反馈给io_service,io_service会根据结果构造一个boost::system::error_code对象并回调给用户,通过该对象可获知所投递操作的执行结果。

流程很简单,从我们的应用程序逐层向下,完成之后再逐层向上反馈。但是,我们看到的其实只是冰山一角。ASIO是一个网络库,除了给我们提供了定时器这类开发组件之外,更重要的是它的网络通信功能。ASIO提供的网络通信功能,不仅封装了操作系统提供的socket api,同时也用自己的思想把不同平台的网络通信模型封装了起来。下面我们来深入io_service,揭开其面纱。

io_service的跨平台策略

考虑到在多平台下的实现细节,io_service可谓是做了不少功夫。要根据不同平台提供的api去设计一个统一的框架提供给用户,而且用户不必关心其实现细节,除了工程庞大之外,也必须要有一个良好的框架。我们从io_service的源码入手,探讨它如何去适应不同平台的实现。(推荐用Source Insight, Sublime Text 2或VC编辑器阅读源码)。

由io_service.run()开始,先转到run()的定义:

1
std::size_t s = impl_.run(ec);

该方法实则上是调用了impl_对象的run()方法,看起来是io_service在impl_的基础上封装了一层,impl_是io_service的成员,不难看出,impl_担任了一个实现功能细节的角色。在io_service类的声明中,找到impl_的声明:

1
2
// The implementation.
impl_type& impl_;

然后是impl_type类型的定义:

1
typedef detail::io_service_impl impl_type;

原来impl_type的原本类型是detail::io_service_impl,实质上它是由宏来决定其最终类型。转到定义可以发现:

1
2
3
4
5
#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

上面代码很清晰可以看到,如果定义了BOOST_ASIO_HAS_IOCP这个宏,那么io_service_impl的类型就是win_iocp_io_service,否则就是task_io_service. 如此说来,io_service_impl这个类型完全是由BOOST_ASIO_HAS_IOCP宏来决定的。跟踪一下这个宏,看它如何定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// Windows: IO Completion Ports.
#if defined(BOOST_WINDOWS) || defined(__CYGWIN__)
# if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
#  if !defined(UNDER_CE)
#   if !defined(BOOST_ASIO_DISABLE_IOCP)
#    define BOOST_ASIO_HAS_IOCP 1
#   endif // !defined(BOOST_ASIO_DISABLE_IOCP)
#  endif // !defined(UNDER_CE)
# endif // defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0400)
#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__)
 
// Linux: epoll, eventfd and timerfd.
#if defined(__linux__)
# include <linux/version.h>
# if !defined(BOOST_ASIO_DISABLE_EPOLL)
#  if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
#   define BOOST_ASIO_HAS_EPOLL 1
#  endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,45)
# endif // !defined(BOOST_ASIO_DISABLE_EVENTFD)
# if !defined(BOOST_ASIO_DISABLE_EVENTFD)
#  if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
#   define BOOST_ASIO_HAS_EVENTFD 1
#  endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,22)
# endif // !defined(BOOST_ASIO_DISABLE_EVENTFD)
# if defined(BOOST_ASIO_HAS_EPOLL)
#  if (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
#   define BOOST_ASIO_HAS_TIMERFD 1
#  endif // (__GLIBC__ > 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
# endif // defined(BOOST_ASIO_HAS_EPOLL)
#endif // defined(__linux__)

如果在Windows或者CYGWIN环境并且IOCP未被禁用的情况下,那么ASIO的网络模型就会被定义为IOCP;而在linux下,则有epoll, eventfd, timerfd三种情况,但结合io_service_impl的定义,可以发现除了BOOST_ASIO_HAS_IOCP在打开时io_service_impl被定义为win_iocp_io_service之外,其它情况都是task_io_service. 因此可以把ASIO归纳为只有两种实现,分别是win_iocp_io_service和task_io_service. 而在io_service中impl_成员的最终类型,也只有这两种。无论采用了哪种模型,io_service暴露的方法都一样,他们的具体实现都必须转给impl_去做。

service_level_table

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页