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

最近在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)

为什么加上这个呢,先看下下面这段代码:

 
  1. int Curl_resolv_timeout(struct connectdata *conn,

  2. const char *hostname,

  3. int port,

  4. struct Curl_dns_entry **entry,

  5. time_t timeoutms)

  6. {

  7. #ifdef USE_ALARM_TIMEOUT

  8. #ifdef HAVE_SIGACTION

  9. struct sigaction keep_sigact; /* store the old struct here */

  10. volatile bool keep_copysig = FALSE; /* wether old sigact has been saved */

  11. struct sigaction sigact;

  12. #else

  13. #ifdef HAVE_SIGNAL

  14. void (*keep_sigact)(int); /* store the old handler here */

  15. #endif /* HAVE_SIGNAL */

  16. #endif /* HAVE_SIGACTION */

  17. volatile long timeout;

  18. volatile unsigned int prev_alarm = 0;

  19. struct Curl_easy *data = conn->data;

  20. #endif /* USE_ALARM_TIMEOUT */

  21. int rc;

  22.  
  23. *entry = NULL;

  24.  
  25. if(timeoutms < 0)

  26. /* got an already expired timeout */

  27. return CURLRESOLV_TIMEDOUT;

  28.  
  29. #ifdef USE_ALARM_TIMEOUT

  30. if(data->set.no_signal)// 注意

  31. /* Ignore the timeout when signals are disabled */

  32. timeout = 0;

  33. else

  34. timeout = timeoutms;

  35.  
  36. if(!timeout)

  37. /* USE_ALARM_TIMEOUT defined, but no timeout actually requested */

  38. return Curl_resolv(conn, hostname, port, entry);

  39.  
  40. if(timeout < 1000) {

  41. /* The alarm() function only provides integer second resolution, so if

  42. we want to wait less than one second we must bail out already now. */

  43. failf(data,

  44. "remaining timeout of %ld too small to resolve via SIGALRM method",

  45. timeout);

  46. return CURLRESOLV_TIMEDOUT;

  47. }

  48. /* This allows us to time-out from the name resolver, as the timeout

  49. will generate a signal and we will siglongjmp() from that here.

  50. This technique has problems (see alarmfunc).

  51. This should be the last thing we do before calling Curl_resolv(),

  52. as otherwise we'd have to worry about variables that get modified

  53. before we invoke Curl_resolv() (and thus use "volatile"). */

  54. if(sigsetjmp(curl_jmpenv, 1)) {

  55. /* this is coming from a siglongjmp() after an alarm signal */

  56. failf(data, "name lookup timed out");

  57. rc = CURLRESOLV_ERROR;

  58. goto clean_up;

  59. }

  60. else {

  61. /*************************************************************

  62. * Set signal handler to catch SIGALRM

  63. * Store the old value to be able to set it back later!

  64. *************************************************************/

  65. #ifdef HAVE_SIGACTION

  66. sigaction(SIGALRM, NULL, &sigact);

  67. keep_sigact = sigact;

  68. keep_copysig = TRUE; /* yes, we have a copy */

  69. sigact.sa_handler = alarmfunc;

  70. #ifdef SA_RESTART

  71. /* HPUX doesn't have SA_RESTART but defaults to that behaviour! */

  72. sigact.sa_flags &= ~SA_RESTART;

  73. #endif

  74. /* now set the new struct */

  75. sigaction(SIGALRM, &sigact, NULL);

  76. #else /* HAVE_SIGACTION */

  77. /* no sigaction(), revert to the much lamer signal() */

  78. #ifdef HAVE_SIGNAL

  79. keep_sigact = signal(SIGALRM, alarmfunc);

  80. #endif

  81. #endif /* HAVE_SIGACTION */

  82.  
  83. /* alarm() makes a signal get sent when the timeout fires off, and that

  84. will abort system calls */

  85. prev_alarm = alarm(curlx_sltoui(timeout/1000L));

  86. }

  87.  
  88. #else

  89. #ifndef CURLRES_ASYNCH

  90. if(timeoutms)

  91. infof(conn->data, "timeout on name lookup is not supported\n");

  92. #else

  93. (void)timeoutms; /* timeoutms not used with an async resolver */

  94. #endif

  95. #endif /* USE_ALARM_TIMEOUT */

  96.  
  97. /* Perform the actual name resolution. This might be interrupted by an

  98. * alarm if it takes too long.

  99. */

  100. rc = Curl_resolv(conn, hostname, port, entry);

  101.  
  102. #ifdef USE_ALARM_TIMEOUT

  103. clean_up:

  104.  
  105. if(!prev_alarm)

  106. /* deactivate a possibly active alarm before uninstalling the handler */

  107. alarm(0);

  108.  
  109. #ifdef HAVE_SIGACTION

  110. if(keep_copysig) {

  111. /* we got a struct as it looked before, now put that one back nice

  112. and clean */

  113. sigaction(SIGALRM, &keep_sigact, NULL); /* put it back */

  114. }

  115. #else

  116. #ifdef HAVE_SIGNAL

  117. /* restore the previous SIGALRM handler */

  118. signal(SIGALRM, keep_sigact);

  119. #endif

  120. #endif /* HAVE_SIGACTION */

  121.  
  122. /* switch back the alarm() to either zero or to what it was before minus

  123. the time we spent until now! */

  124. if(prev_alarm) {

  125. /* there was an alarm() set before us, now put it back */

  126. unsigned long elapsed_ms = Curl_tvdiff(Curl_tvnow(), conn->created);

  127.  
  128. /* the alarm period is counted in even number of seconds */

  129. unsigned long alarm_set = prev_alarm - elapsed_ms/1000;

  130.  
  131. if(!alarm_set ||

  132. ((alarm_set >= 0x80000000) && (prev_alarm < 0x80000000)) ) {

  133. /* if the alarm time-left reached zero or turned "negative" (counted

  134. with unsigned values), we should fire off a SIGALRM here, but we

  135. won't, and zero would be to switch it off so we never set it to

  136. less than 1! */

  137. alarm(1);

  138. rc = CURLRESOLV_TIMEDOUT;

  139. failf(data, "Previous alarm fired off!");

  140. }

  141. else

  142. alarm((unsigned int)alarm_set);

  143. }

  144. #endif /* USE_ALARM_TIMEOUT */

  145.  
  146. return rc;

  147. }

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

猜你喜欢

转载自blog.csdn.net/qq_19004627/article/details/81944864