curl在线程调用中崩溃 [英] curl crashes in threaded calls

查看:218
本文介绍了curl在线程调用中崩溃的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如果curl是线程安全的,则我阅读了SO文章.这个非常简单的代码会崩溃-并非总是如此,但是当我连续[非并行]多次调用该程序时,它会因分段错误或以下错误而崩溃.

I read the SO articles if curl is thread-safe. This very simple code crashes - not always but when I call the program several times in a row [not parallel] then it crashes either with a segmentation fault or with the below error.

到目前为止,我确信我已遵循文档中所述的有关curl和线程的规则.

That far I am convinced I follow the rules regarding curl and threads as stated in the documentation.

在测试中,我发现它在curl_easy_perform()中崩溃.

In tests I could find out that it crashes in curl_easy_perform().

#include <curl/curl.h>
#include <stdio.h>
#include <thread>

class curlClass
{
private:
    CURL * curl {};
    CURLcode res;
    const char * sUrl;

public:
    auto loadDataFromUrl()     -> void;
    static auto initCurl()     -> void;
    static auto releaseCurl()  -> void;
    static auto callbackSaveData( void * content, size_t size, size_t nmemb, curlClass * classInstance ) -> size_t;
    curlClass( const char * );
    ~curlClass();
};

auto curlClass::initCurl() -> void
{
    curl_global_init(CURL_GLOBAL_SSL);
}

auto curlClass::releaseCurl() -> void
{
    curl_global_cleanup();
}

curlClass::curlClass( const char * sUrl ) : sUrl( sUrl )
{
    curl = curl_easy_init();
}

curlClass::~curlClass()
{
    curl_easy_cleanup( curl );
}

auto curlClass::callbackSaveData( __attribute__ ((unused))  void *contents, 
    size_t size, 
    size_t nmemb, 
    __attribute__ ((unused))    curlClass * classInstance
) -> size_t
{
    return size * nmemb;
}

auto curlClass::loadDataFromUrl() -> void
{
    if ( curl )
    {
        curl_easy_setopt(curl, (CURLoption) CURLOPT_SSL_VERIFYPEER, nullptr);
        curl_easy_setopt(curl, (CURLoption) CURLOPT_URL, sUrl);
        curl_easy_setopt(curl, (CURLoption) CURLOPT_WRITEFUNCTION, callbackSaveData);
        res = curl_easy_perform(curl);
        printf( "Return: %d\n", res );
    }
}

auto worker( const char * sUrl ) -> void
{
    curlClass myInstance( sUrl );
    myInstance.loadDataFromUrl();
}

int main(void)
{
    curl_version_info_data * curl_version = curl_version_info(CURLVERSION_NOW);

    printf( "Curl version=%s\n", curl_version->version );
    curlClass::initCurl();

    std::thread thread1( worker, "https://www.google.com");
    std::thread thread2( worker, "https://www.google.com" );
    std::thread thread3( worker, "https://www.google.com" );
    std::thread thread4( worker, "https://www.google.com" );
    std::thread thread5( worker, "https://www.google.com" );
    std::thread thread6( worker, "https://www.google.com" );
    std::thread thread7( worker, "https://www.google.com" );

    thread1.join();
    thread2.join();
    thread3.join();
    thread4.join();
    thread5.join();
    thread6.join();
    thread7.join();

    curlClass::releaseCurl();
}

更新:

我编译了新的curl版本7.46.0.这是可能的错误转储之一:

I compiled the new curl version 7.46.0. This is one of the possible error dumps:

Curl version=7.46.0
*** glibc detected *** ./curl_crash: double free or corruption (out): 0x00007fcd200056d0 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x76618)[0x7fcd2ff4a618]
/lib64/libc.so.6(cfree+0x6c)[0x7fcd2ff4f65c]
/usr/lib64/libcrypto.so.0.9.8(CRYPTO_free+0x19)[0x7fcd2f9f13f9]
/usr/lib64/libcrypto.so.0.9.8(+0xf407d)[0x7fcd2f9d307d]
/usr/lib64/libcrypto.so.0.9.8(ERR_clear_error+0xd)[0x7fcd2f9efd1d]
/usr/local/lib/libcurl.so.4(+0x4849c)[0x7fcd30a2d49c]
/usr/local/lib/libcurl.so.4(+0x4b970)[0x7fcd30a30970]
/usr/local/lib/libcurl.so.4(+0x1073d)[0x7fcd309f573d]
/usr/local/lib/libcurl.so.4(+0x20451)[0x7fcd30a05451]
/usr/local/lib/libcurl.so.4(+0x31e0e)[0x7fcd30a16e0e]
/usr/local/lib/libcurl.so.4(curl_multi_perform+0xdd)[0x7fcd30a1780d]
/usr/local/lib/libcurl.so.4(curl_easy_perform+0x10b)[0x7fcd30a1001b]
./curl_crash(_ZN9curlClass15loadDataFromUrlEv+0x88)[0x408002]
./curl_crash(_Z6workerPKc+0x2c)[0x408051]
./curl_crash(_ZNSt12_Bind_simpleIFPFvPKcES1_EE9_M_invokeIILm0EEEEvSt12_Index_tupleIIXspT_EEE+0x40)[0x409818]
./curl_crash(_ZNSt12_Bind_simpleIFPFvPKcES1_EEclEv+0x1d)[0x409711]
./curl_crash(_ZNSt6thread5_ImplISt12_Bind_simpleIFPFvPKcES3_EEE6_M_runEv+0x1c)[0x40968e]
/usr/local/lib64/libstdc++.so.6(+0xb5c10)[0x7fcd30790c10]
/lib64/libpthread.so.0(+0x77f6)[0x7fcd2e81c7f6]
/lib64/libc.so.6(clone+0x6d)[0x7fcd2ffaf09d]
======= Memory map: ========

我真的不知道出什么问题了.当我启动该程序5次后,至少会崩溃一次.

I do not have really a clue what is going wrong. When I start the program 5 times then at least once it crashes.

我在做什么错?根据转储,我正在使用旧的SSL库吗?

What am I doing wrong or is it possible - according to the dump - that I am using an old SSL library?

要编译的命令行:

g++ --std=c++11 -Wall -Werror -pedantic -Wextra curl_crash.cpp -o curl_crash -lcurl -rdynamic && ./curl_crash

推荐答案

我停止阅读SSL库文档为时过早.感谢Petesh,他在访问openssl时指出了该问题,因此我可以快速解决该问题.

I stopped too early reading the SSL library documentation. Thanks to Petesh, who pointed out the issue while accessing openssl, I could then fix fast the problem.

curl文档中所述线程安全-> TLS-> OpenSSL我必须使用这些功能.我将其调整为C ++ 11标准:

As stated in the curl documentation Thread-safe --> TLS --> OpenSSL I had to use the functions. I adapted it to C++11 standard:

#include <mutex>
#include <openssl/err.h>
#include <vector>

class SslCurlWrapper
{
private:
    static std::vector<std::mutex> vectorOfSslMutex;
    static auto id_function() -> unsigned long { return ( pthread_self() ); }
    static auto locking_function(int, int, const char *, int) -> void;

public:
    SslCurlWrapper();
    ~SslCurlWrapper();
};

std::vector<std::mutex> SslCurlWrapper::vectorOfSslMutex( CRYPTO_num_locks() );

//----------------------------------------
auto SslCurlWrapper::locking_function( int mode,
                                       int n,
                                       __attribute__ ((unused)) const char * file,
                                       __attribute__ ((unused)) int line
                                     ) -> void
//----------------------------------------
{
    if ( mode & CRYPTO_LOCK )   vectorOfSslMutex [n].lock();
    else                        vectorOfSslMutex [n].unlock();
}

//------------------------------
SslCurlWrapper::SslCurlWrapper()
//------------------------------
{
    CRYPTO_set_id_callback( id_function );
    CRYPTO_set_locking_callback( locking_function );
}

//-------------------------------
SslCurlWrapper::~SslCurlWrapper()
//-------------------------------
{
    CRYPTO_set_id_callback( nullptr );
    CRYPTO_set_locking_callback( nullptr );
}

然后可以像这样使用它:

It can be then used like this:

int main(void)
{
        SslCurlWrapper sslObject;    // hook is set up
        // here it is safe to use the curl library in a multi-thread-environment
}       // hook is released/uninstalled

编译命令:

g++ -std=c++11 -Wall -Werror -Wextra -pedantic -c SslCurlWrapper.cpp

-lcrypto库必须添加到编译器命令行中,以使链接程序不会抱怨.

The -lcrypto library has to be added to the compiler command line in order the linker does not complain.

我个人的评论是,openssl库中的默认模式未实现上述两种功能,这使每个程序员不必从头开始,这让我感到有些惊讶.这可能是最低实现...

My personal remark is that I am a bit surprised that the default mode in the openssl library does not implement the both mentioned function that way to avoid each programmer has to start from scratch. This could be the minimum-implementation...

测试shell脚本调用了该程序200次,没有发生任何崩溃.在此之前它立即崩溃了.对我来说,它是固定的.

The test shell script called the program 200 times without any crash. Before it crashed immediately. For me it is fixed.

我不明白SergeyA的无用评论.

I do not understand the very useless comment from SergeyA.

这篇关于curl在线程调用中崩溃的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆