前言:    
  最近有个项目, 需要访问第三方服务. 该服务是通过http的形式访问的, 为了安全和加密, 对方提供了一个加密用的C/C++库, 用于对参数进行处理.  鉴于此, 选用了C/C++语言, 以libcurl作为http类库来编写该服务模块. 这为后续的坑埋下了伏笔.
  

状况简述:
  程序采用Reactor模型, IO线程专做IO事件读写, 以及请求的dispatch分发, 后端线程池用于业务的同步操作. 对libcurl的使用, 也穿插在多线程中.
  当程序功能完成后, 对其进行压力测试. 过了大致20分钟, 程序crash并出core.

  

  gdb调core后, 发现其在epoll_wait之上的调用栈出问题了.
  根据经验, 本着谁肇事, 谁擦屁股的原则, 对该线程以及epoll调用方做了艰苦卓绝, 却步履维艰的排查. 后面的事实证明, 这是条不归路, duang duang duang.....

逆向推测:
  起初的排查没有效果, 于是乎, 采用逆向思维. 从SIGABRT 6信号出发, 看看什么情况下能触发该信号.
  指针的重复删除会引发该错误, 如下面例子:
  
  其他的内存操作, 如野指针的使用, 越界, 往往对应的是SIGSEGV 11.
  于是乎, 寻找疑似double delete的情况, 结果还是一无所获.

信号认识:
  真相只有一个, 可惜在那里呢? 是那个环节出错呢?
  无意中, 再次扫了下堆栈, 徒然发现 <signal handler called>
  
  看来epoll_wait的线程, 是被信号中断, 并执行了该信号回调函数(其来源标明了libcurl.so.4), 并在该信号处理函数中出SIGABRT 6.
  让我们回忆下, 信号的处理.
  raise函数, 会把信号发送给本线程. 而其他函数(如kill, alarm), 向进程发送信号, 但具体执行该信号处理函数的线程是不确定的.
  现在离真相已经很接近了: 某线程触发信号, epoll_wait线程则执行了该信号处理函数, 可惜在这个处理函数中, 出了异常. 和epoll_wait无关, 与其调用方也没关系.
  那某函数(黑衣人)是谁呢? 和libcurl有关系不?

拨开云雾:
  通过搜索引擎, 发现"libcurl 多线程使用注意事项", "Libcurl多线程crash问题" 这两篇文章.
  真相终于浮现出来了, 原来libcurl的超时机制默认是通过信号sigalrm, setjmp/longjmp函数来实现的, 在多线程情况下, 会导致程序crash.
  这官方的说明, 也解释了core栈上的异常信息.
  最终的解决方案就是, CURLOPT_NOSIGNAL设置为1.

curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

总结:
  甲方虐我千百遍, 我待甲方如初恋. 虽然被libcurl坑, 但对信号的理解更深刻了, 终是好事. 但C/C++和Java实现同样的业务功能时, C/C++ Coder需要了解更多的细节, 抬高门槛的同时, 也增加开发难度和开发时间, 未免有点得不偿失. 这篇文章不是语言之争, 就此打住.

写在最后:
  
如果你觉得这篇文章对你有帮助, 请小小打赏下. 其实我想试试, 看看写博客能否给自己带来一点小小的收益. 无论多少, 都是对楼主一种由衷的肯定.

  

 

linux信号处理及libcurl的坑的更多相关文章

  1. 【转载】linux信号处理及libcurl的坑

    转载自http://www.cnblogs.com/mumuxinfei/p/4363466.html 前言:     最近有个项目, 需要访问第三方服务. 该服务是通过http的形式访问的, 为了安 ...

  2. linux 信号处理 五 (示例)

    [摘要]本文分析了Linux内核对于信号的实现机制和应用层的相关处理.首先介绍了软中断信号的本质及信号的两种不同分类方法尤其是不可靠信号的原理.接着分析了内核对于信号的处理流程包括信号的触发/注册/执 ...

  3. linux信号处理相关知识

      因为要处理最近项目中碰上的多个子进程退出信号同时到达,导致程序不当产生core的情况,今天我花了时间看了一些关于linux信号处理的博客. 总结一下:(知识未经实践) linux信号分两种,一种实 ...

  4. 第一次使用Linux服务器所栽之坑

    一直以来想拥有一个服务器,但是一直舍不得.9.21日终于心血来潮,买了一个腾讯云服务器.对比了一下腾讯云和阿里云,发现腾讯云比较良心,不仅有学生价1元/月,而且整体价格也比阿里云便宜.之前用过一次阿里 ...

  5. Linux下编译LibCURL

    编译环境 操作系统: Red Hat Enterprise Linux Server release 5.4 64-bit 编译工具: gcc (GCC) 4.1.2 20080704 (Red Ha ...

  6. linux下安装libcurl及开源库的一般安装步骤

    前言 总有人说:要多看源代码!那么源代码去哪找呢?找到了又该怎么安装呢?本票博客不介绍如何使用和学习,只要讲获取和安装,以后会将curl和libevent的使用和学习. 一.开源库常用安装步骤 1.开 ...

  7. Linux Mint安装Docker踩坑指南

    我家的服务器选用的Linux Mint系统,最近安装Docker的时候踩了一些小坑,但是总体还算顺利. 我们都知道Linux Mint系统是基于Ubuntu的,说实话用起来感觉还是很不错的,安装Doc ...

  8. LIBCURL踩坑记

    这里一个java程序员进行C++开发使用libcurl踩过的坑: 1.  发送指定请求类型body,比较通用方法如下,可以直接填写数据并手动指定content type,如果是form表单等形式,则需 ...

  9. linux 信号处理 四

    一.信号生命周期 从信号发送到信号处理函数的执行完毕 对于一个完整的信号生命周期(从信号发送到相应的处理函数执行完毕)来说,可以分为三个重要的阶段,这三个阶段由四个重要事件来刻画:信号诞生:信号在进程 ...

随机推荐

  1. "数学口袋精灵"bug的发现

    团队成员的博客园地址: 曾治业:http://www.cnblogs.com/zzy999/ 陈焕恳:http://www.cnblogs.com/4249ken/ 蓝叶:http://www.cnb ...

  2. 一个app中保持程序全屏的方法。

    public void toggleFullscreen(boolean fullScreen) { //fullScreen为true时全屏 WindowManager.LayoutParams a ...

  3. PHP后台

    一.ajax提交表单 先引入ajax.js function ajax(url, fnSucc, fnFaild) { //1.创建Ajax对象 var oAjax=null; if(window.X ...

  4. POJ 2240 && ZOJ 1082 Arbitrage 最短路,c++ stl pass g++ tle 难度:0

    http://poj.org/problem?id=2240 用log化乘法为加法找正圈 c++ 110ms,g++tle #include <string> #include <m ...

  5. IBatis.net 输出SQL语句(七)

    一.IBatis.net输出SQL语句到控制台 输出IBatis.net生成的SQL语句到控制台,能够方便调试. 如果要想输出IBatis.net的SQL语句到控制台,那么只需要做如下配置即可: &l ...

  6. 使用ASP.Net WebAPI构建REST服务(四)——参数绑定

    默认绑定方式 WebAPI把参数分成了简单类型和复杂类型: 简单类型主要包括CLR的primitive types,(int.double.bool等),系统内置的几个strcut类型(TimeSpa ...

  7. ASP.NET MVC学习之控制器篇(二)

    原文链接:http://www.asp.net/learn/mvc/ 这篇教程探索了ASP.NET MVC控制器(controller).控制器动作(controller action)和动作结果(a ...

  8. 算法导论-钢条切割 C# 递归实现

    下班前看到有位兄弟写 钢条切割问题,尝试实现C#版, 还没有实现最优版,分享一下. int[] parr; private void button1_Click(object sender, Even ...

  9. JS小问题总结

    1. 超链接中href=#与href=javascript:void(0) 的区别  #包含了一个位置信息.默认的锚是#top 也就是网页的上端:而javascript:void(0)   仅仅表示一 ...

  10. hive内部表、外部表

    hive内部表.外部表区别自不用说,可实际用的时候还是要小心. Hive的数据分为表数据和元数据,表数据是Hive中表格(table)具有的数据:而元数据是用来存储表的名字,表的列和分区及其属性,表的 ...