堆栈打印
[sigaction_func:42] signo 11
[sigaction_func:43] si_signo 11; si_pid 232145656; si_uid 1186409107!
[sigaction_func:52] SegmentFault!
[print_backtrace:569] =============================
[print_backtrace:570] backtrace() start
[print_backtrace:571] returned 9 addresses
[print_backtrace:585] 0:./main.exe(print_backtrace+0x20) [0x49882c]
[print_backtrace:585] 1:./main.exe() [0x4170fc]
[print_backtrace:585] 2:linux-vdso.so.1(__kernel_rt_sigreturn+0) [0x7fa182c63c]
[print_backtrace:585] 3:/usr/lib/libcurl.so.4(+0x20e48) [0x7fa17bae48]
[print_backtrace:585] 4:/usr/lib/libcurl.so.4(+0x21340) [0x7fa17bb340]
[print_backtrace:585] 5:/usr/lib/libcurl.so.4(curl_mvsnprintf+0x38) [0x7fa17bc448]
[print_backtrace:585] 6:/usr/lib/libcurl.so.4(+0x12d4c) [0x7fa17acd4c]
[print_backtrace:585] 7:/usr/lib/libcurl.so.4(+0x968c) [0x7fa17a368c]
[print_backtrace:585] 8:/lib64/libc.so.6(+0x176cb8) [0x7fa0ed5cb8]
[print_backtrace:587] backtrace end--------------
测试发现的现象:
- 在域名检测超时时,会出现段错误
- 在测试程序中,关闭轮询线程(单线程运行),域名检测超时了也没有段错误
根据现象,应该是线程安全问题;
根据堆栈打印,得知,段错误出现在curl_mvsnprintf函数中,在curl代码中查找到# define vsnprintf curl_mvsnprintf
[print_backtrace:585] 3:/usr/lib/libcurl.so.4(+0x20e48) [0x7fa17bae48]
[print_backtrace:585] 4:/usr/lib/libcurl.so.4(+0x21340) [0x7fa17bb340]
[print_backtrace:585] 5:/usr/lib/libcurl.so.4(curl_mvsnprintf+0x38) [0x7fa17bc448]
找到堆栈最后一个,
[print_backtrace:585] 7:/usr/lib/libcurl.so.4(+0x968c) [0x7fa17a368c]
在libcurl.so.4.4.0.S代码中对应的Curl_resolv_timeout
函数的位置
0000000000009610 <Curl_resolv_timeout>:
...
968c: b94057a0 ldr w0, [x29, #84]
9690: 340007e0 cbz w0, 978c <Curl_resolv_timeout+0x17c>
...
curl-7_50_0 : lib/hostip.c :: Curl_resolv_timeout()
/*
* Curl_resolv_timeout() is the same as Curl_resolv() but specifies a
* timeout. This function might return immediately if we're using asynch
* resolves. See the return codes.
*
* The cache entry we return will get its 'inuse' counter increased when this
* function is used. You MUST call Curl_resolv_unlock() later (when you're
* done using this struct) to decrease the counter again.
*
* If built with a synchronous resolver and use of signals is not
* disabled by the application, then a nonzero timeout will cause a
* timeout after the specified number of milliseconds. Otherwise, timeout
* is ignored.
*
* Return codes:
*
* CURLRESOLV_TIMEDOUT(-2) = warning, time too short or previous alarm expired
* CURLRESOLV_ERROR (-1) = error, no pointer
* CURLRESOLV_RESOLVED (0) = OK, pointer provided
* CURLRESOLV_PENDING (1) = waiting for response, no pointer
*/
int Curl_resolv_timeout(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry,
long timeoutms)
{
这个函数里使用了SIGALRM,且这个函数里,先将原有的信号处理设置保存,设置新的SIGALRM信号处理,处理完后,再恢复,使用了sigaction
接口,信号处理函数是
#ifdef HAVE_SIGSETJMP
/* Beware this is a global and unique instance. This is used to store the
return address that we can jump back to from inside a signal handler. This
is not thread-safe stuff. */
sigjmp_buf curl_jmpenv;
#endif
RETSIGTYPE alarmfunc(int sig)
{
/* this is for "-ansi -Wall -pedantic" to stop complaining! (rabe) */
(void)sig;
siglongjmp(curl_jmpenv, 1);
return;
}
curl_jmpenv
是全局的!
Curl_resolv_timeout中有个条件是:
if(data->set.no_signal)
/* Ignore the timeout when signals are disabled */
timeout = 0;
else
timeout = timeoutms;
if(!timeout)
/* USE_ALARM_TIMEOUT defined, but no timeout actually requested */
return Curl_resolv(conn, hostname, port, entry);
搜索了下set.no_signal
得到:
case CURLOPT_NOSIGNAL:
/*
* The application asks not to set any signal() or alarm() handlers,
* even when using a timeout.
*/
data->set.no_signal = (0 != va_arg(param, long)) ? TRUE : FALSE;
break;
在测试代码中,对接口设置
curl_easy_setopt(webProxy->hCurl, CURLOPT_NOSIGNAL, 1L);
进行测试,域名检测超时后,没有发现段错误
费劲半天,早知道百度一下就解决了