本文目录,首先总结问题,然后案例还原。

总结:

问题的根本在于boost.asio作为header-only库,运行程序与动态库之间容易因为版本错配而产生运行期莫名其妙的问题。

cpprestsdk使用boost-1.53编译了动态库,运行程序通过cmake找库boost-1.53,但同时cmake_module_path包含了boost-1.7x的cmake帮助模块,致使程序实际却使用了boost-1.7x包含目录的配置,boost-1.53的库目录配置。

由于boost.asio是header-only库,cpprestsdk.so按boost-1.53方式访问io_service,并编译进库;运行程序却按boost-1.7x方式访问io_service。

由于程序整体(包括cpprestsdk.so)使用到的boost.asio的函数接口,两个版本的boost.asio并没有不一致,所以编译没有发生问题。但是io_service的结构却不同,编译没有发生问题,却不会被人发现。因此在运行期间,程序按boost-1.7x的结构定义创建io_service,cpprestsdk却按boost-1.53的结构定义访问。如果是发生了SIGSEGV一类内存访问越界,还容易发现,但却没有触发,只是变量在内存布局位置不同,而问题转移到其它函数,这些函数因为依赖io_service的变量而因为访问不到正确的值,触发参数错误。

正是因为上述原因,cpprestsdk不能正确读出io_service的epoll_fd_,从而编译在cpprestsdk.so的部分boost.asio代码在操作epoll时失败,也就是将22号errno抛出异常std::invalid_argument。

案例:

不要去企图调试pplx。

场境,cpprestsdk中的pplx只是ppl的一个修剪版,功力只有几成,而且没有timer,timer是另一个名为Asynchronous Agents Lib的类。所以只能使用boost.asio提供的timer。但是一旦代码加入了boost.asio的timer,http_listen.open().wait()就永远地抛“open: Invalid argument”。本例的平台环境是 10cores centos7。

调试开始:

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

线程抛异常。cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

一共有42个线程。

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

由http_listener.open().wait()抛出异常的backtrace。

不要去调试pplx,企图单步进去分析。open()异步到thread_pool,wait()阻塞等待task<>结果,然后将错误抛出异常。

也不要去调试cpprestsdk,你会接受一系列task的洗礼,无功而返。

根据异常信息分析,类型是boost::system::system_error,也就是系统调用,open函数报参数错误?socket是用socket()而不是open()。

断socket()分析。

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

上两图,主线程,正在wait;线程41正执行open的任务操作。线程41是什么?

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

线程41是由boost.asio库的threadpool创建的线程,运行io_service::run()的。为什么有40个线程,每个core创建了4个线程。

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

最后出现了我们的关键字“listener"。

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

可惜的是,socket()并没有失败,成功创建了fd:6的socket。

然后一波跟踪后,发现eventpoll的文件描述符有问题。

cpprestsdk同时使用boost.asio,acceptor就一直报Invalid argument。-LMLPHP

fd: 1是我们熟知的stdout,故一定出错。是谁手欠去将 epoll_fd_改掉了。只好在下次启动时,断epoll_create,找到epoll_fd_地址进行监视。但是让人崩溃的是,监视这个epoll_fd_,程序却直奔抛异常。

在几乎断线索的情况下,反过来一想epoll_fd_为什么就没有被访问呢,也就是说,我断的epoll_fd_,不是执行代码看到epoll_fd_,原因只有一个可能,我俩眼里的 io_service西施压根不是同一时空的。一查CMakeFiles里的CXX_includes发现,我的程序原来在使用boost-1.7x,我编译的cpprestsdk在使用boost-1.53,尽管我强调了find_package(Boost 1.53 REQUIRED),无奈CMAKE_MODULE_PATH包含了boost-1.7的帮助cmake却不知道。

事实上,这个错误浪费了不止上面贴出的过程的时间。因为在开始没有头绪的时候,还要先排除boost::asio timer对cpprestsdk.so的影响,毕竟是加了它才出问题。还有痛苦的pplx异步任务跟踪,徒劳无功。上面的案例是归纳修正思路后,案例重演调试,也就是几步,因为我以经知道问题所在,方向还怎么走到清楚。实际上就一yuan大头,浪费了多少时间。

05-11 23:03