最近在Linux嵌入式平台上使用curl出现卡死的情况。
1.第一种情况
在发送的时候不加上链接超时和发送超时,这样子很容易造成在发送的时候出现卡死的现象,导致线程阻塞
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
2.第二种情况是加上链接超时和发送超时
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10);
这种情况,在发送的时候不会出现卡死的状况,但是如果在发送的时候,设备的断网后,再链接网以后,会出现程序崩溃的现象
3.第三种情况加上
既然第二种情况会出现崩溃,我们加上
//Setting CURLOPT_NOSIGNAL to 1 makes libcurl NOT ask the system to ignore SIGPIPE signals
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)
为什么加上这个呢,先看下下面这段代码:
int Curl_resolv_timeout(struct connectdata *conn,
const char *hostname,
int port,
struct Curl_dns_entry **entry,
time_t timeoutms)
{
#ifdef USE_ALARM_TIMEOUT
#ifdef HAVE_SIGACTION
struct sigaction keep_sigact; /* store the old struct here */
volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */
struct sigaction sigact;
#else
#ifdef HAVE_SIGNAL
void (*keep_sigact)(int); /* store the old handler here */
#endif /* HAVE_SIGNAL */
#endif /* HAVE_SIGACTION */
volatile long timeout;
volatile unsigned int prev_alarm = 0;
struct Curl_easy *data = conn->data;
#endif /* USE_ALARM_TIMEOUT */
int rc;
*entry = NULL;
if(timeoutms < 0)
/* got an already expired timeout */
return CURLRESOLV_TIMEDOUT;
#ifdef USE_ALARM_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);
if(timeout < 1000) {
/* The alarm() function only provides integer second resolution, so if
we want to wait less than one second we must bail out already now. */
failf(data,
"remaining timeout of %ld too small to resolve via SIGALRM method",
timeout);
return CURLRESOLV_TIMEDOUT;
}
/* This allows us to time-out from the name resolver, as the timeout
will generate a signal and we will siglongjmp() from that here.
This technique has problems (see alarmfunc).
This should be the last thing we do before calling Curl_resolv(),
as otherwise we'd have to worry about variables that get modified
before we invoke Curl_resolv() (and thus use "volatile"). */
if(sigsetjmp(curl_jmpenv, 1)) {
/* this is coming from a siglongjmp() after an alarm signal */
failf(data, "name lookup timed out");
rc = CURLRESOLV_ERROR;
goto clean_up;
}
else {
/*************************************************************
* Set signal handler to catch SIGALRM
* Store the old value to be able to set it back later!
*************************************************************/
#ifdef HAVE_SIGACTION
sigaction(SIGALRM, NULL, &sigact);
keep_sigact = sigact;
keep_copysig = TRUE; /* yes, we have a copy */
sigact.sa_handler = alarmfunc;
#ifdef SA_RESTART
/* HPUX doesn't have SA_RESTART but defaults to that behaviour! */
sigact.sa_flags &= ~SA_RESTART;
#endif
/* now set the new struct */
sigaction(SIGALRM, &sigact, NULL);
#else /* HAVE_SIGACTION */
/* no sigaction(), revert to the much lamer signal() */
#ifdef HAVE_SIGNAL
keep_sigact = signal(SIGALRM, alarmfunc);
#endif
#endif /* HAVE_SIGACTION */
/* alarm() makes a signal get sent when the timeout fires off, and that
will abort system calls */
prev_alarm = alarm(curlx_sltoui(timeout/1000L));
}
#else
#ifndef CURLRES_ASYNCH
if(timeoutms)
infof(conn->data, "timeout on name lookup is not supported\n");
#else
(void)timeoutms; /* timeoutms not used with an async resolver */
#endif
#endif /* USE_ALARM_TIMEOUT */
/* Perform the actual name resolution. This might be interrupted by an
* alarm if it takes too long.
*/
rc = Curl_resolv(conn, hostname, port, entry);
#ifdef USE_ALARM_TIMEOUT
clean_up:
if(!prev_alarm)
/* deactivate a possibly active alarm before uninstalling the handler */
alarm(0);
#ifdef HAVE_SIGACTION
if(keep_copysig) {
/* we got a struct as it looked before, now put that one back nice
and clean */
sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */
}
#else
#ifdef HAVE_SIGNAL
/* restore the previous SIGALRM handler */
signal(SIGALRM, keep_sigact);
#endif
#endif /* HAVE_SIGACTION */
/* switch back the alarm() to either zero or to what it was before minus
the time we spent until now! */
if(prev_alarm) {
/* there was an alarm() set before us, now put it back */
unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);
/* the alarm period is counted in even number of seconds */
unsigned long alarm_set = prev_alarm - elapsed_ms/1000;
if(!alarm_set ||
((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {
/* if the alarm time-left reached zero or turned "negative" (counted
with unsigned values), we should fire off a SIGALRM here, but we
won't, and zero would be to switch it off so we never set it to
less than 1! */
alarm(1);
rc = CURLRESOLV_TIMEDOUT;
failf(data, "Previous alarm fired off!");
}
else
alarm((unsigned int)alarm_set);
}
#endif /* USE_ALARM_TIMEOUT */
return rc;
}
4.第四钟情况
第四种情况是,curl能在超时的时候正常返回,但是还是会出现curl卡死的情况
那是因为curl正常是只用系统的DNS进行解析,那就是DNS解析将不受超时限制了, 万一DNS服务器 卡住了话, 那就可能会造成curl卡死的情况。
那么使用了curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)还是有问题,无奈之下,只能libcurl使用c-ares(C library for asynchronous DNS requests)来做名字解析,编编译curl的时候加上编译选项./configure --enable-ares这样子就可以不使用curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1)了。