线程使用libcurl的巨坑踩了一天!!!

用libcurl一段时间遇到莫名其妙的程序崩溃的情况,开始觉得是线程栈溢出导致的段错误,专门增加了线程栈的大小,还是有问题。线程也是分离的。用valgrind定位到问题可能出现在curl的调用上。


排查的时候也发现了libcurl一些额外的坑,现做个总结笔记。

1.、

线程使用libcurl访问时,设置了超时时间,而libcurl库不会为这个超时信号做任何处理,信号产生而没有信号句柄处理,可能导致程序退出。用以下选项禁止访问超时的时候抛出超时信号。

curl_setopt(curl, CURLOPT_NOSIGNAL,1L);

2、

正常使用流程是先调用curl_global_init初始化资源,而这个函数不是线程安全的。curl_easy_init发现没有做初始化时会自动初始化,多个线程同时进行curl_easy_init时会导致异常。curl官方建议在主线程中先做初始化。

curl_global_init(CURL_GLOBAL_ALL); 

3、

用valgrind调试,做压力测试多线程运行时,valgrind弹出类似访问冲突的错误,把错误定位到libcurl的互斥访问的处理上。


大致是进行访问设计到ssl的东西的时候,需要做相应的加锁处理,libcurl源码没有加这块处理,不过给了个针对这种情况加锁的例子,总的来说curl的示例代码很重要很有用。

https://curl.haxx.se/libcurl/c/example.html


  1. #define USE_OPENSSL    
  2.    
  3. #include <stdio.h>  
  4. #include <pthread.h>  
  5. #include <curl/curl.h>  
  6.    
  7. #define NUMT 4  
  8.    
  9. /* we have this global to let the callback get easy access to it */   
  10. static pthread_mutex_t *lockarray;  
  11.    
  12. #ifdef USE_OPENSSL  
  13. #include <openssl/crypto.h>  
  14. static void lock_callback(int mode, int type, char *file, int line)  
  15. {  
  16.   (void)file;  
  17.   (void)line;  
  18.   if(mode & CRYPTO_LOCK) {  
  19.     pthread_mutex_lock(&(lockarray[type]));  
  20.   }  
  21.   else {  
  22.     pthread_mutex_unlock(&(lockarray[type]));  
  23.   }  
  24. }  
  25.    
  26. static unsigned long thread_id(void)  
  27. {  
  28.   unsigned long ret;  
  29.    
  30.   ret=(unsigned long)pthread_self();  
  31.   return ret;  
  32. }  
  33.    
  34. static void init_locks(void)  
  35. {  
  36.   int i;  
  37.    
  38.   lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() *  
  39.                                             sizeof(pthread_mutex_t));  
  40.   for(i=0; i<CRYPTO_num_locks(); i++) {  
  41.     pthread_mutex_init(&(lockarray[i]), NULL);  
  42.   }  
  43.    
  44.   CRYPTO_set_id_callback((unsigned long (*)())thread_id);  
  45.   CRYPTO_set_locking_callback((void (*)())lock_callback);  
  46. }  
  47.    
  48. static void kill_locks(void)  
  49. {  
  50.   int i;  
  51.    
  52.   CRYPTO_set_locking_callback(NULL);  
  53.   for(i=0; i<CRYPTO_num_locks(); i++)  
  54.     pthread_mutex_destroy(&(lockarray[i]));  
  55.    
  56.   OPENSSL_free(lockarray);  
  57. }  
  58. #endif  
  59.    
  60. #ifdef USE_GNUTLS  
  61. #include <gcrypt.h>  
  62. #include <errno.h>  
  63.    
  64. GCRY_THREAD_OPTION_PTHREAD_IMPL;  
  65.    
  66. void init_locks(void)  
  67. {  
  68.   gcry_control(GCRYCTL_SET_THREAD_CBS);  
  69. }  
  70.    
  71. #define kill_locks()  
  72. #endif  
  73.    
  74. /* List of URLs to fetch.*/   
  75. const char * const urls[]= {  
  76.   ”https://www.example.com/”,  
  77.   ”https://www2.example.com/”,  
  78.   ”https://www3.example.com/”,  
  79.   ”https://www4.example.com/”,  
  80. };  
  81.    
  82. static void *pull_one_url(void *url)  
  83. {  
  84.   CURL *curl;  
  85.    
  86.   curl = curl_easy_init();  
  87.   curl_easy_setopt(curl, CURLOPT_URL, url);  
  88.   /* this example doesn’t verify the server’s certificate, which means we 
  89.      might be downloading stuff from an impostor */   
  90.   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);  
  91.   curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);  
  92.   curl_easy_perform(curl); /* ignores error */   
  93.   curl_easy_cleanup(curl);  
  94.    
  95.   return NULL;  
  96. }  
  97.    
  98. int main(int argc, char **argv)  
  99. {  
  100.   pthread_t tid[NUMT];  
  101.   int i;  
  102.   int error;  
  103.   (void)argc; /* we don’t use any arguments in this example */   
  104.   (void)argv;  
  105.    
  106.   /* Must initialize libcurl before any threads are started */   
  107.   curl_global_init(CURL_GLOBAL_ALL);  
  108.    
  109.   init_locks();  
  110.    
  111.   for(i=0; i< NUMT; i++) {  
  112.     error = pthread_create(&tid[i],  
  113.                            NULL, /* default attributes please */   
  114.                            pull_one_url,  
  115.                            (void *)urls[i]);  
  116.     if(0 != error)  
  117.       fprintf(stderr, ”Couldn’t run thread number %d, errno %d\n”, i, error);  
  118.     else  
  119.       fprintf(stderr, ”Thread %d, gets %s\n”, i, urls[i]);  
  120.   }  
  121.    
  122.   /* now wait for all threads to terminate */   
  123.   for(i=0; i< NUMT; i++) {  
  124.     error = pthread_join(tid[i], NULL);  
  125.     fprintf(stderr, ”Thread %d terminated\n”, i);  
  126.   }  
  127.    
  128.   kill_locks();  
  129.    
  130.   return 0;  
  131. }  
#define USE_OPENSSL  





#include <stdio.h> #include <pthread.h> #include <curl/curl.h> #define NUMT 4 /* we have this global to let the callback get easy access to it */ static pthread_mutex_t *lockarray; #ifdef USE_OPENSSL #include <openssl/crypto.h> static void lock_callback(int mode, int type, char *file, int line) { (void)file; (void)line; if(mode & CRYPTO_LOCK) { pthread_mutex_lock(&(lockarray[type])); } else { pthread_mutex_unlock(&(lockarray[type])); } } static unsigned long thread_id(void) { unsigned long ret; ret=(unsigned long)pthread_self(); return ret; } static void init_locks(void) { int i; lockarray=(pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); for(i=0; i<CRYPTO_num_locks(); i++) { pthread_mutex_init(&(lockarray[i]), NULL); } CRYPTO_set_id_callback((unsigned long (*)())thread_id); CRYPTO_set_locking_callback((void (*)())lock_callback); } static void kill_locks(void) { int i; CRYPTO_set_locking_callback(NULL); for(i=0; i<CRYPTO_num_locks(); i++) pthread_mutex_destroy(&(lockarray[i])); OPENSSL_free(lockarray); } #endif #ifdef USE_GNUTLS #include <gcrypt.h> #include <errno.h> GCRY_THREAD_OPTION_PTHREAD_IMPL; void init_locks(void) { gcry_control(GCRYCTL_SET_THREAD_CBS); } #define kill_locks() #endif /* List of URLs to fetch.*/ const char * const urls[]= { "https://www.example.com/", "https://www2.example.com/", "https://www3.example.com/", "https://www4.example.com/", }; static void *pull_one_url(void *url) { CURL *curl; curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url); /* this example doesn't verify the server's certificate, which means we might be downloading stuff from an impostor */ curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); curl_easy_perform(curl); /* ignores error */ curl_easy_cleanup(curl); return NULL; } int main(int argc, char **argv) { pthread_t tid[NUMT]; int i; int error; (void)argc; /* we don't use any arguments in this example */ (void)argv; /* Must initialize libcurl before any threads are started */ curl_global_init(CURL_GLOBAL_ALL); init_locks(); for(i=0; i< NUMT; i++) { error = pthread_create(&tid[i], NULL, /* default attributes please */ pull_one_url, (void *)urls[i]); if(0 != error) fprintf(stderr, "Couldn't run thread number %d, errno %d\n", i, error); else fprintf(stderr, "Thread %d, gets %s\n", i, urls[i]); } /* now wait for all threads to terminate */ for(i=0; i< NUMT; i++) { error = pthread_join(tid[i], NULL); fprintf(stderr, "Thread %d terminated\n", i); } kill_locks(); return 0; }


未完待续。。。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值