zoukankan      html  css  js  c++  java
  • 【转载】linux信号处理及libcurl的坑

    转载自http://www.cnblogs.com/mumuxinfei/p/4363466.html

    前言:    
      最近有个项目, 需要访问第三方服务. 该服务是通过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.

    1
    curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L);

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

  • 相关阅读:
    CSS3媒体查询总结
    关于前端学习规划
    开发技术版本
    Visual Studio 问题汇总
    asp.net web core 开发问题汇总(1)
    asp.net web core 部署问题汇总
    报表平台需求文档(V0.0.0.1)
    Git使用说明
    派生类
    cin、cout的重载
  • 原文地址:https://www.cnblogs.com/vanishfan/p/4463575.html
Copyright © 2011-2022 走看看