libcurl多线程环境下执行curl_seay_perform后发生coredump的原因及解决

libcurl多线程环境下执行curl_seay_perform后发生coredump的原因及解决

问题背景

  • 设备上的某模块已上线稳定运行了两年了,最近突然发现了前方问题.初步定位后发现用户在配置集控服务器地址时,未切换大小写,导致地址配置成了x.x.x.x:8080,此处":"为中文符号.
  • 按常理推断,此时正常现象应为连接服务器失败,一分钟后尝试重连.
  • 但此时现场的现象为设备上集控进程crash并产生了coredump文件.
  • 若正常配置服务器地址,无任何问题.

问题排查

  • 使用gdb查看core文件打印函数调用栈发现已被踩坏,无法获取到有用信息.只能粗略的看出为段错误.
    在这里插入图片描述

  • 在连接服务器的线程中打调试发现,curl句柄初始化无误,且curl_easy_perform返回值为CURLE_COULDNT_RESOLVE_HOST(6),系统日志发送无误,线程正常退出.无法具体确定产生crash的代码

  • 尝试将主要流程注掉发现,当注掉curl_easy_perform并直接返回失败时,进程不再挂了.

  • 查阅libcurl相关资料后发现此问题为已知问题:
    产生此问题的函数调用栈大致如下:
    curl_easy_perform->easy_perform->easy_transfer->curl_multi_perform->multi_runsingle->Curl_connect->create_conn->resolve_server->Curl_resolv_timeout.
    在curl_resolve_timeout中实现了对服务器域名解析和超时后的错误处理,而在linux系统中,其调用了linux的本地域名解析流程,具体使用了sigalarm+sigsetjmp/siglongjmp 来实现逻辑.

  • 问题就出在此处,sigalarm+sigsetjmp/siglongjmp 并不是线程安全的,当我在A线程中域名解析失败,产生了sigalarm信号,可能被其他线程捕获到,它是使用alarm + siglongjmp 实现的。用sigalarm在多线程下做超时,本身就几乎不可能。如果只是使用sigalarm,并不会导致程序崩溃,但是,再加上siglongjmp,就要命了 (程序崩溃的很可怕,core中几乎看不出有用信息),因为其需要一个sigjmp_buf型的全局变量,多线程修改它。(通常情况下,可以每个线程一个 sigjmp_buf 型的变量,这种情况下,多线程中使用 siglongjmp 是没有问题的,但是libcurl只有一个全局变量,所有的线程都会用)。
    具体是类似 curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L) 的超时设置,导致alarm的使用(估计发生在域名解析阶段),如前所述,这在多线程中是不行的。解决方式是禁用掉alarm这种超时, curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L)。

解决方案

设置curl_easy_setopt(curl, CURLOPT_NOSIGNAL,1L);屏蔽当前线程产生的所有信号和处理信号的所有函数.带来的副作用即为此线程域名解析的超时设置失效,若无其他超时设置,将会阻塞在域名解析中.
官方说明中有提到可以用libcurl with the c-ares 中提供的新的异步的域名解析方案来处理此问题.
详情参加:
[link][https://curl.se/libcurl/c/threadsafe.html]
[link][https://curl.se/libcurl/c/CURLOPT_NOSIGNAL.html]

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值