问题描述
在需要并发请求http时,需要通过多线程使用curl库。curl的handle对象只要保证在一个线程中创建使用和销毁,一般就不会有问题。
但是有一种类型的对象叫做curl_share,通过curl_share_init函数创建。用于给多个curl handle对象提供共享数据,如为了降低请求延时,减少dns查询时间,可以共享同一份dns缓存。
crash堆栈
#0 0x00007fe4339c9387 in raise () from /lib64/libc.so.6
#1 0x00007fe4339caa78 in abort () from /lib64/libc.so.6
#2 0x00007fe433a0bed7 in __libc_message () from /lib64/libc.so.6
#3 0x00007fe433a14299 in _int_free () from /lib64/libc.so.6
#4 0x00007fe434c82b6d in hash_element_dtor () from /lib64/libcurl.so.4
#5 0x00007fe434c8297f in Curl_llist_remove () from /lib64/libcurl.so.4
#6 0x00007fe434c8308a in Curl_hash_clean_with_criterium () from /lib64/libcurl.so.4
#7 0x00007fe434c5ebb7 in Curl_fetch_addr () from /lib64/libcurl.so.4
#8 0x00007fe434c5ed24 in Curl_resolv () from /lib64/libcurl.so.4
#9 0x00007fe434c763c5 in Curl_connect () from /lib64/libcurl.so.4
#10 0x00007fe434c857c3 in multi_runsingle () from /lib64/libcurl.so.4
#11 0x00007fe434c860f9 in curl_multi_perform () from /lib64/libcurl.so.4
#12 0x00007fe434c7e7db in curl_easy_perform () from /lib64/libcurl.so.4
解决方法:
设置加解锁函数才可以加锁保护
static void curl_dns_lock(CURL *h, curl_lock_data data, curl_lock_access access, void*userptr) {
dns_mutex.lock();
}
static void curl_dns_unlock(CURL *h, curl_lock_data data, curl_lock_access access, void*userptr) {
dns_mutex.unlock();
}
curl_share_setopt(dns_share_handle, CURLSHOPT_LOCKFUNC, curl_dns_lock);
curl_share_setopt(dns_share_handle, CURLSHOPT_UNLOCKFUNC, curl_dns_unlock);
分析过程分享
通过堆栈发现和dns有关,因为有函数Curl_resolv。
查看curl源码
git clone https://github.com/curl/curl.git
struct Curl_dns_entry *
Curl_fetch_addr(struct Curl_easy *data,
const char *hostname,
int port)
{
struct Curl_dns_entry *dns = NULL;
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
dns = fetch_addr(data, hostname, port);
if(dns)
dns->inuse++; /* we use it! */
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
return dns;
}
发现此处获取地址,需要加锁。但是如果加锁了应该不会出crash才对。
//初始化时,是这么写的
dns_share_handle = curl_share_init();
curl_share_setopt(curl_dns_share_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
查看share->lockfunc赋值代码,发现是在curl_share_setopt中设置的:
curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) {
case CURLSHOPT_LOCKFUNC:
lockfunc = va_arg(param, curl_lock_function);
share->lockfunc = lockfunc;
break;
case CURLSHOPT_UNLOCKFUNC:
unlockfunc = va_arg(param, curl_unlock_function);
share->unlockfunc = unlockfunc;
break;
}