Ошибка сегментации в libcurl, многопоточная - PullRequest
7 голосов
/ 19 июля 2010

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

 #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 "\177", 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 "\177", 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 "\177", 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 ]

13 голосов
/ 19 июля 2010

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

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

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 не является потокобезопасный.

0 голосов
/ 02 марта 2016

Как упоминалось в ошибка: 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, а не мои). "-

...