网络库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_ ...
随机推荐
- webfrrm基础
一.B/S和C/S 1.C/S C/S 架构是一种典型的两层架构,其全程是Client/Server,即客户端服务器端架构,其客户端包含一个或多个在用户的电脑上运行的程序,而服务器端有两种,一种是数据 ...
- 多项目开发下的dll文件管理
阅读目录: DS01:为什么要对生成的dll文件进行管理? DS02:首先介绍以下两个DOS命令 DS03:第一种实现方法(xcopy) DS04:第二种实现方法(attrib) DS05:分享一个有 ...
- JavaWeb chapter10 JavaWeb开发模式
1. 开发模式 (1)开发模式1:JSP+JavaBean (2)开发模式2:Servlet+JSP+JavaBean (MVC) 2.JavaBean 本质上是一个普通的Java类:需要遵循一定的 ...
- daydayup2 codeforces143D
这题很考察分析 题意:让你构造n*m的矩阵,使得不存在两个点的距离的平方为5 1若n=1 ,答案为m 2若m=1,答案为n 3若n=2 则这样 110011001100.. 110011001100 ...
- .NET 3.5 安装错误的四个原因及解决方法
.net framework 3.5 安装错误的四个常见原因及解决方法,飓风软件站整理,转载请注明. 1.清除所有版本 .NET Framework 安装错误后在系统中遗留的文件: 如果您以往安装过 ...
- PHP文件的读取
1.PHP部分文件操作函数 ( fopen ,fread ,filesize,fwrite,fclose ) 2.unlink() rmdir() 删除函数 unlink(路径和文件名): rmdi ...
- Coding源码学习第四部分(Masonry介绍与使用(三))
接上篇继续进行Masonry 的学习. (12)tableViewCell 布局 #import "TableViewController.h" #import "Tes ...
- Python3学习(一)-基础、数据类型、变量、字符串和编码、list&tuple、if、for、while、dict、set、函数与参数
##廖雪峰py3笔记 ## '//'这是获得相除后的整数部分 ##a = 10//3 ##print (a) ## '/'获得相除后的结果,为浮点数,结果能整除也也是浮点数 ##b = 10/3 ## ...
- hihoCoder#1094
刚开始学习C语言,准备在做hiho的题目的过程中来学习,在此进行记录,如果代码中有错误或者不当的地方还请指正. 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 Littl ...
- java多线程系列之 synchronized
一.synchronized基本原理 java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁.线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁. ...