赞
踩
使用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]
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。