Linux下关于curl卡死的情况分析

来源:互联网 发布:腾讯软件管家 编辑:程序博客网 时间:2024/05/01 08:13

最近在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_TIMEOUTclean_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;}
if(data->set.no_signal)如果设置了这个,那么在DNS超时的时候会立即返回。
另外:curl如果不加curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1),是不支持小于1000ms的超时的,
原因
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;
}
超时时间小于1000ms的时候, name解析会直接返回CURLRESOLV_TIMEOUT, 最后会导致CURLE_OPERATION_TIMEDOUT

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)了。



0 1