最近在做服务器的稳定性的相关测试,服务器的网络底层使用的是boost asio,然后自己做的二次封装以更好的满足需求。

  服务器昨天晚上发现crash了一次,之前测试了将近半个多月,有一次是莫名的退出了,不过由于是新的测试服,忘记将ulimit -c进行修改了,所以没有coredump,这次又发生了。

coredump如下:

#0  0x0000000000000091 in ?? ()
#1 0x0000000000459729 in ClientHandler::HandleConnect(cpnet::IConnection*) ()
#2 0x00000000004a0bbc in boost::asio::detail::completion_handler<boost::_bi::bind_t<void, boost::_mfi::mf1<void, cpnet::IMsgHandler, cpnet::IConnection*>, boost::_bi::list2<boost::_bi::value<cpnet::IMsgHandler*>, boost::_bi::value<cpnet::Connection*> > > >::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long) ()
#3 0x0000000000492e25 in boost::asio::detail::strand_service::do_complete(boost::asio::detail::task_io_service*, boost::asio::detail::task_io_service_operation*, boost::system::error_code const&, unsigned long) ()
#4 0x0000000000493f20 in boost::asio::detail::task_io_service::run(boost::system::error_code&) ()
#5 0x0000000000495bb5 in boost::asio::io_service::run() ()
#6 0x00007ff3798153cf in thread_proxy () from /home/slither/slither/depends/libboost_thread.so.1.58.0
#7 0x00007ff3788d7df5 in start_thread () from /lib64/libpthread.so.0
#8 0x00007ff3786051ad in clone () from /lib64/libc.so.6

  整个网络库是针对boost asio做的二次封装,程序由于是release版本的,之前也没有特别生成这个版本对应的symbols,我只能看出异常时候的堆栈信息和线程信息,其他的东西我也没有什么好的办法去查看。

  从堆栈异常中可以看出来,最后出错的地方是非法的地址方法,不过调用它的frame 1是我们自己的函数,一个当有连接成功时候的回调函数。不过一开始看这个回调函数的堆栈感觉很奇怪,因为是我们自己内部通过strand dispatch的,按理说调用这个函数的上层函数也应该是我们自己写的代码,然而情况不是这样,那么不可能是堆栈显示错了,而应该是自己之前对于dispatch的立即错了。

  这个HandleConnect我是使用boost strand做dispatch分发的,这里就涉及到我之前对strand dispatch接口的一个误读了,之前看文档不够细致,以为strand dispath是立即执行的,其实这是不对的,dispath的接口说明文档是这样的:

/**
* This function is used to ask the strand to execute the given handler.
*
* The strand object guarantees that handlers posted or dispatched through
* the strand will not be executed concurrently. The handler may be executed
* inside this function if the guarantee can be met. If this function is
* called from within a handler that was posted or dispatched through the same
* strand, then the new handler will be executed immediately
.
*
* The strand's guarantee is in addition to the guarantee provided by the
* underlying io_service. The io_service guarantees that the handler will only
* be called in a thread in which the io_service's run member function is
* currently being invoked.
*
* @param handler The handler to be called. The strand will make a copy of the
* handler object as required. The function signature of the handler must be:
* @code void handler(); @endcode
*/

  注意红色标记的那段,如果调用strand dispatch的时候,是持有相同strand调用的,那么当前dispatch的handler会立即执行。也就是说在多线程的时候,如果我们的线程调用strand dispatch的时候,其他线程已经在调用了,那么其实它是不会立即执行的,会放到等待队列里面去的。

  asio中的dispatch代码是这样的:

template <typename Handler>
void strand_service::dispatch(strand_service::implementation_type& impl,
Handler& handler)
{
// If we are already in the strand then the handler can run immediately.
 // 如果我们已经在这个strand中了,那么这个handler立即执行
if (call_stack<strand_impl>::contains(impl))
{
fenced_block b(fenced_block::full);
boost_asio_handler_invoke_helpers::invoke(handler, handler);
return;
} // Allocate and construct an operation to wrap the handler.
typedef completion_handler<Handler> op;
typename op::ptr p = { boost::asio::detail::addressof(handler),
boost_asio_handler_alloc_helpers::allocate(
sizeof(op), handler), };
p.p = new (p.v) op(handler); BOOST_ASIO_HANDLER_CREATION((p.p, "strand", impl, "dispatch"));
  // do_dispatch判断是否能够立即执行
bool dispatch_immediately = do_dispatch(impl, p.p);
operation* o = p.p;
p.v = p.p = ; if (dispatch_immediately)
{
// Indicate that this strand is executing on the current thread.
call_stack<strand_impl>::context ctx(impl); // Ensure the next handler, if any, is scheduled on block exit.
on_dispatch_exit on_exit = { &io_service_, impl };
(void)on_exit; completion_handler<Handler>::do_complete(
&io_service_, o, boost::system::error_code(), );
}
}

  再来看下do_dispatch的代码:

bool strand_service::do_dispatch(implementation_type& impl, operation* op)
{
// If we are running inside the io_service, and no other handler already
// holds the strand lock, then the handler can run immediately.
 // 如果没有其他handler已经持有strand lock锁,那么这个handler就可以立即执行
bool can_dispatch = io_service_.can_dispatch();
impl->mutex_.lock();
if (can_dispatch && !impl->locked_)
{
// Immediate invocation is allowed.
impl->locked_ = true;
impl->mutex_.unlock();
return true;
} if (impl->locked_)
{
// Some other handler already holds the strand lock. Enqueue for later.
  // 如果其他handler已经持有strand锁了,那么放到队列中
impl->waiting_queue_.push(op);
impl->mutex_.unlock();
}
else
{
// The handler is acquiring the strand lock and so is responsible for
// scheduling the strand.
impl->locked_ = true;
impl->mutex_.unlock();
impl->ready_queue_.push(op);
io_service_.post_immediate_completion(impl, false);
} return false;
}

  通过asio strand的dispatch源代码,我们可以看出来,我们dispatch的handler是有可能不会被立即执行的。由于我们自己之前对于dispatch逻辑的认知错误,在dispatch handler之前,我们就开始准备读网络数据,在比较特殊的情况下,也就是客户端刚连上,立即端口,那么我们读网络数据的函数就立即返回错误,由于我自己封装的Connection是使用shared_ptr做的封装,如果没有任何引用,就会析构掉,那么等我们之前dispatch的handler从队列中被执行的时候,之前传递的Connection指针已经是野指针了,就导致程序crash掉了。

  这种偶现的bug,是比较难被测试出来的,通常只有我们自己进行多样的压力测试的时候,才比较容易发现。同时也是告诫自己在使用其他第三方库的时候,还是要更加仔细的弄懂api。

网络库crash以及boost asio strand dispath分析的更多相关文章

  1. 开源网络库ACE、Boost的ASIO、libevent、libev、ZeroMQ

    开源C/C++网络库:ACE          C++语言 跨平台Boost的ASIO  C++语言 跨平台libevent     C语言   主要支持linux,新版增加了对windows的IOC ...

  2. Boost.Asio 网络编程([译]Boost.Asio基本原理)

    转自:https://m.w3cschool.cn/nlzbw/nlzbw-3vs825ya.html Boost.Asio基本原理 这一章涵盖了使用Boost.Asio时必须知道的一些事情.我们也将 ...

  3. Boost::asio io_service 实现分析

    io_service的作用 io_servie 实现了一个任务队列,这里的任务就是void(void)的函数.Io_servie最常用的两个接口是post和run,post向任务队列中投递任务,run ...

  4. boost.ASIO-可能是下一代C++标准的网络库

    曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的 ...

  5. boost asio 学习(九) boost::asio 网络封装

    http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio?pg=10 9. A ...

  6. boost Asio网络编程简介

    :first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...

  7. 网络库Asio交叉编译(Linux生成ARM)

    1.  Asio是一个跨平台的C++库,用于网络和底层I/O编程.Asio使用先进的C++方式提供了一系列的异步模型 2. 官方网址:http://think-async.com 3. 由于Asio库 ...

  8. boost::asio译文

        Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENS ...

  9. Boost.Asio技术文档

    Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE_1_ ...

随机推荐

  1. (满满的是硬货)Spring深入研究一IOC实现

    IOC基于Java底层的反射机制实现 反射机制: 核心: Class cls = Class.forName(类名); Class ptypes[] = new Class[2]; ptypes[0] ...

  2. 学习solr

    1.下载地址 http://apache.opencas.org/lucene/solr/

  3. 深入replace

    replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串. stringObject.replace(reg/str,str/replacement); 这里主 ...

  4. SEO学习笔记-误区和经验总结

    原文链接:http://www.cnblogs.com/monxue/p/seo_note.html 常见误区和错误: 1.忽视404错误页面的优化,没有及时处理死链导致权重降低 2.做外链优化只链到 ...

  5. sh back mongo

    !/bin/shBACK_DB=ALLOUT_DIR=/home/jianyeruan/app/mongo #临时备份目录TAR_DIR=/home/jianyeruan/app/mongotar # ...

  6. Codeforces Round #211 (Div. 2) D题(二分,贪心)解题报告

    ---恢复内容开始--- 题目地址 简要题意: n个小伙子一起去买自行车,他们有每个人都带了一些钱,并且有公有的一笔梦想启动资金,可以分配给任何小伙子任何数值,当然分配权在我们的手中.现在给出m辆自行 ...

  7. keep the bar green to keep the code clean——Junit详解(二)

    测试用例&测试套件 举个栗子: 编写MyStack类模拟栈,并对其进行测试用例编写测试: 编写文件删除方法,并对其删除测试. 不再做演示,戳此获取代码 MyStack类: public cla ...

  8. [转]ZendFramework数据库操作总结

    Zend_Db数据库知识 例子: Model文件: $this->fetchAll("is_jian=1","id DESC",0,2)->toAr ...

  9. php 数据库insert函数

    <?php function into($constr) { $con = mysql_connect("localhost","root"," ...

  10. Windows Server 2003 IIS6.0+PHP5(FastCGI)+MySQL5环境搭建教程

    准备篇 一.环境说明: 操作系统:Windows Server 2003 SP2 32位 PHP版本:php 5.3.14(我用的php 5.3.10安装版) MySQL版本:MySQL5.5.25 ...