网络库crash以及boost asio strand dispath分析
最近在做服务器的稳定性的相关测试,服务器的网络底层使用的是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分析的更多相关文章
- 开源网络库ACE、Boost的ASIO、libevent、libev、ZeroMQ
开源C/C++网络库:ACE C++语言 跨平台Boost的ASIO C++语言 跨平台libevent C语言 主要支持linux,新版增加了对windows的IOC ...
- Boost.Asio 网络编程([译]Boost.Asio基本原理)
转自:https://m.w3cschool.cn/nlzbw/nlzbw-3vs825ya.html Boost.Asio基本原理 这一章涵盖了使用Boost.Asio时必须知道的一些事情.我们也将 ...
- Boost::asio io_service 实现分析
io_service的作用 io_servie 实现了一个任务队列,这里的任务就是void(void)的函数.Io_servie最常用的两个接口是post和run,post向任务队列中投递任务,run ...
- boost.ASIO-可能是下一代C++标准的网络库
曾几何时,Boost中有一个Socket库,但后来没有了下文,C++社区一直在翘首盼望一个标准网络库的出现,网络上开源的网络库也有不少,例如Apache Portable Runtime就是比较著名的 ...
- boost asio 学习(九) boost::asio 网络封装
http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting- started-with-boostasio?pg=10 9. A ...
- boost Asio网络编程简介
:first-child { margin-top: 0px; } .markdown-preview:not([data-use-github-style]) h1, .markdown-previ ...
- 网络库Asio交叉编译(Linux生成ARM)
1. Asio是一个跨平台的C++库,用于网络和底层I/O编程.Asio使用先进的C++方式提供了一系列的异步模型 2. 官方网址:http://think-async.com 3. 由于Asio库 ...
- boost::asio译文
Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENS ...
- Boost.Asio技术文档
Christopher Kohlhoff Copyright © 2003-2012 Christopher M. Kohlhoff 以Boost1.0的软件授权进行发布(见附带的LICENSE_1_ ...
随机推荐
- (满满的是硬货)Spring深入研究一IOC实现
IOC基于Java底层的反射机制实现 反射机制: 核心: Class cls = Class.forName(类名); Class ptypes[] = new Class[2]; ptypes[0] ...
- 学习solr
1.下载地址 http://apache.opencas.org/lucene/solr/
- 深入replace
replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串. stringObject.replace(reg/str,str/replacement); 这里主 ...
- SEO学习笔记-误区和经验总结
原文链接:http://www.cnblogs.com/monxue/p/seo_note.html 常见误区和错误: 1.忽视404错误页面的优化,没有及时处理死链导致权重降低 2.做外链优化只链到 ...
- sh back mongo
!/bin/shBACK_DB=ALLOUT_DIR=/home/jianyeruan/app/mongo #临时备份目录TAR_DIR=/home/jianyeruan/app/mongotar # ...
- Codeforces Round #211 (Div. 2) D题(二分,贪心)解题报告
---恢复内容开始--- 题目地址 简要题意: n个小伙子一起去买自行车,他们有每个人都带了一些钱,并且有公有的一笔梦想启动资金,可以分配给任何小伙子任何数值,当然分配权在我们的手中.现在给出m辆自行 ...
- keep the bar green to keep the code clean——Junit详解(二)
测试用例&测试套件 举个栗子: 编写MyStack类模拟栈,并对其进行测试用例编写测试: 编写文件删除方法,并对其删除测试. 不再做演示,戳此获取代码 MyStack类: public cla ...
- [转]ZendFramework数据库操作总结
Zend_Db数据库知识 例子: Model文件: $this->fetchAll("is_jian=1","id DESC",0,2)->toAr ...
- php 数据库insert函数
<?php function into($constr) { $con = mysql_connect("localhost","root"," ...
- 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 ...