Ошибка сегментации в libcurl, многопоточная

Итак, у меня есть куча рабочих потоков, выполняющих простой класс curl,каждый рабочий поток имеет свою собственную ручку curl easy. Они делают только головные поиски на случайных веб-сайтах. Также функции блокировки присутствуют для включения многопоточного SSL, как задокументировано здесь. Все работает, кроме 2 веб-страниц ilsole24ore.com (см. В примере вниз), и ninemsn.com.au/, они иногда производят ошибку seg, как показано в выводе трассировки, показанном здесь

 #0  *__GI___libc_res_nquery (statp=0xb4d12df4, name=0x849e9bd "ilsole24ore.com", class=1, type=1, answer=0xb4d0ca10 "", anslen=1024, answerp=0xb4d0d234,
        answerp2=0x0, nanswerp2=0x0, resplen2=0x0) at res_query.c:182
    #1  0x00434e8b in __libc_res_nquerydomain (statp=0xb4d12df4, name=0xb4d0ca10 "", domain=0x0, class=1, type=1, answer=0xb4d0ca10 "", anslen=1024,
        answerp=0xb4d0d234, answerp2=0x0, nanswerp2=0x0, resplen2=0x0) at res_query.c:576
    #2  0x004352b5 in *__GI___libc_res_nsearch (statp=0xb4d12df4, name=0x849e9bd "ilsole24ore.com", class=1, type=1, answer=0xb4d0ca10 "", anslen=1024,
        answerp=0xb4d0d234, answerp2=0x0, nanswerp2=0x0, resplen2=0x0) at res_query.c:377
    #3  0x009c0bd6 in *__GI__nss_dns_gethostbyname3_r (name=0x849e9bd "ilsole24ore.com", af=2, result=0xb4d0d5fc, buffer=0xb4d0d300 "7", buflen=512,
        errnop=0xb4d12b30, h_errnop=0xb4d0d614, ttlp=0x0, canonp=0x0) at nss_dns/dns-host.c:197
    #4  0x009c0f2b in _nss_dns_gethostbyname2_r (name=0x849e9bd "ilsole24ore.com", af=2, result=0xb4d0d5fc, buffer=0xb4d0d300 "7", buflen=512,
        errnop=0xb4d12b30, h_errnop=0xb4d0d614) at nss_dns/dns-host.c:251
    #5  0x0079eacd in __gethostbyname2_r (name=0x849e9bd "ilsole24ore.com", af=2, resbuf=0xb4d0d5fc, buffer=0xb4d0d300 "7", buflen=512, result=0xb4d0d618,
        h_errnop=0xb4d0d614) at ../nss/getXXbyYY_r.c:253
    #6  0x00760010 in gaih_inet (name=<value optimized out>, service=<value optimized out>, req=0xb4d0f83c, pai=0xb4d0d764, naddrs=0xb4d0d754)
        at ../sysdeps/posix/getaddrinfo.c:531
    #7  0x00761a65 in *__GI_getaddrinfo (name=0x849e9bd "ilsole24ore.com", service=0x0, hints=0xb4d0f83c, pai=0xb4d0f860) at ../sysdeps/posix/getaddrinfo.c:2160
    #8  0x00917f9a in ?? () from /usr/lib/libkrb5support.so.0
    #9  0x003b2f45 in krb5_sname_to_principal () from /usr/lib/libkrb5.so.3
    #10 0x0028a278 in ?? () from /usr/lib/libgssapi_krb5.so.2
    #11 0x0027eff2 in ?? () from /usr/lib/libgssapi_krb5.so.2
    #12 0x0027fb00 in gss_init_sec_context () from /usr/lib/libgssapi_krb5.so.2
    #13 0x00d8770e in ?? () from /usr/lib/libcurl.so.4
    #14 0x00d62c27 in ?? () from /usr/lib/libcurl.so.4
    #15 0x00d7e25b in ?? () from /usr/lib/libcurl.so.4
    #16 0x00d7e597 in ?? () from /usr/lib/libcurl.so.4
    #17 0x00d7f133 in curl_easy_perform () from /usr/lib/libcurl.so.4

моя функция выглядит примерно так

int do_http_check(taskinfo *info,standardResult *data)
{
    standardResultInit(data);

    char errorBuffer[CURL_ERROR_SIZE];

    CURL *curl;
    CURLcode result;

    curl = curl_easy_init();

    if(curl)
    {
        //required options first
        curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errorBuffer);
        curl_easy_setopt(curl, CURLOPT_URL, info->address.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writer);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data->body);
        curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, writer);
        curl_easy_setopt(curl, CURLOPT_WRITEHEADER, &data->head);
        curl_easy_setopt(curl, CURLOPT_DNS_USE_GLOBAL_CACHE,0);
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30 );
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL,1);
        curl_easy_setopt(curl, CURLOPT_NOBODY,1);
        curl_easy_setopt(curl, CURLOPT_TIMEOUT ,240);

        //optional options
        if(info->options.follow)
        {
            curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
            curl_easy_setopt(curl, CURLOPT_MAXREDIRS, info->options.redirects);
        }

        result = curl_easy_perform(curl);

        if (result == CURLE_OK)
        {
            data->success = true;
            curl_easy_getinfo(curl,CURLINFO_RESPONSE_CODE,&data->httpMsg);
            curl_easy_getinfo(curl,CURLINFO_REDIRECT_COUNT,&data->numRedirects);
            data->msg = "OK";
        }
        else
        {
            ... handle error
        }


    return 1;
}

теперь, когда я вызываю функцию без каких-либо потоков, просто вызывая ее из main, она никогда не ломается, поэтому я думал, что она связана с потоками или, может быть, как возвращается структура возврата данных, но из того, что я видел в трассировке, похоже, что ошибка генерируется в вызове easy_perform (), и это меня смущает. Поэтому, если кто-то знает, где мне искать дальше, это было бы очень полезно, спасибо.

2 ответов


есть целый раздел, посвященный в libcurl для многопоточности.

первое основное правило заключается в том, что вы должны никогда не делитесь дескриптором libcurl (будь то легкий или multi или что угодно) между различные потоки. Используйте только одну ручку по одной нити за раз.

libcurl полностью потокобезопасен, за исключением двух вопросов: сигналы и Обработчики SSL/TLS. Сигналы используются для тайм-аут имя разрешается (во время DNS lookup) - при построении без c-ares поддержку, а не на Windows.

Если вы получаете доступ к HTTPS или FTPS URL-адреса многопоточным способом, вы тогда, конечно, используя базовая многопоточная библиотека SSL и эти библиотеки могут быть свои требования по данному вопросу. В основном, вам нужно предоставить один или два функции, позволяющие ему функционировать правильно. Для всех деталей, см. Это:

OpenSSL

http://www.openssl.org/docs/crypto/threads.html#DESCRIPTION

GnuTLS

http://www.gnu.org/software/gnutls/manual/html_node/Multi_002dthreaded-applications.html

NSS

считается потокобезопасным уже ничего не требуется.

yassl

требуемые действия неизвестны.

при использовании нескольких потоков, вы должны установить Опция CURLOPT_NOSIGNAL для 1 для всех ручек. Все будет или может работать нормально, за исключением тайм-аутов не соблюдаются во время поиска DNS - который вы можете обойти, построив libcurl с поддержкой c-ares. c-ares-это библиотека, которая предоставляет асинхронное имя разрешается. На некоторых платформ, libcurl просто не будет функция правильно многопоточная если этот параметр не установлен.

кроме того, обратите внимание, что CURLOPT_DNS_USE_GLOBAL_CACHE не поточно-ориентированный.


Как говорится в ошибка: longjmp вызывает неинициализированный кадр стека, последние версии libcurl (>=7.32.0) в репозиториях Debian/Ubuntu содержат новый многопоточный решатель для решения этих проблем. Поддержка C-ares не является хорошим решением:

https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=570436#74

"реальная проблема заключается в том, что c-ares еще не является полной заменой функций gethostby* (например, он не поддерживает многоадресный DNS) и включение его в пакеты libcurl на складе может быть не очень хорошим шагом (обратите внимание, что это слова вышестоящего автора как curl, так и c-ares, а не мои)."-