首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在无线程支持的程序加载的共享库中使用C++11多线程

在无线程支持的程序加载的共享库中使用C++11多线程
EN

Stack Overflow用户
提问于 2013-12-13 21:54:48
回答 2查看 5.4K关注 0票数 18

我目前正在尝试在一个共享库中使用C++11多线程,该库被加载到Linux上的主程序(用C编写)中。这是一个大的模拟程序的一部分,我不能改变任何关于库加载的事情,也不能改变主程序。

主程序是用gcc 4.1.2编译的,我没有源代码(我不能用gcc 4.8.2重新编译它)。

共享库是用gcc 4.8.2编译的,目的是为了使用C++11多线程。我正在传递编译器命令

代码语言:javascript
复制
-pthread -lpthread -std=c++11

What is the correct link options to use std::thread in GCC under linux?中所述

使用这种配置("-pthread -std=c++11“和gcc 4.8)编译一个独立的测试程序,在我的系统上可以正常工作。但是当我启动加载共享库的程序时,我得到了一个异常:

代码语言:javascript
复制
Caught std::exception!
Exception Message: Enable multithreading to use std::thread: Operation not permitted

Terminating...

使用-pthread-lpthread (编辑:以及仅使用-pthread而不使用-lpthread)编译参数不起作用。编译器参数是(我使用的是库克构建系统):

代码语言:javascript
复制
-pthread -std=c++11 -fmessage-length=0 -fPIC -Wchar-subscripts ...(lots of -W* here)
... -Wunused-variable -m64 -D__64BIT__ -pthread -lpthread

和链接器参数(由于构建系统而导致重复参数):

代码语言:javascript
复制
-pthread -lpthread -std=c++11 -pthread -lpthread -std=c++11 -shared -fPIC -Wl,-Bsymbolic -Wl,--allow-shlib-undefined -pthread -lpthread

在我的库上调用ldd会产生以下输出

代码语言:javascript
复制
$ ldd calc3/build/amd64_linux26_RH5/library.so
    linux-vdso.so.1 =>  (0x00007fff4d1fd000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00002ae6ec124000)
    libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ae6ec340000)
    libm.so.6 => /lib64/libm.so.6 (0x00002ae6ec655000)
    libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ae6ec8d8000)
    libc.so.6 => /lib64/libc.so.6 (0x00002ae6ecaef000)
    /lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)

在主程序上

代码语言:javascript
复制
$ ldd .../bin-64/main_program
    linux-vdso.so.1 =>  (0x00007fff64595000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00000032cc000000)
    libz.so.1 => /usr/lib64/libz.so.1 (0x00000032cc800000)
    libc.so.6 => /lib64/libc.so.6 (0x00000032cb800000)
    /lib64/ld-linux-x86-64.so.2 (0x00000032cb400000)

pthread库链接到我的共享库,但没有链接到主程序。此answer声明您必须将pthread链接到主程序,但此答案的第二个注释(由@R..)说这是不必要的(这听起来合乎逻辑)。

不幸的是,我对整个系统的加载机制一无所知,除了我的库使用了另一个C++库作为API。

请注意,其他C++11特性可以工作( libstdc++.so在我的库的依赖项中),但C++11多线程不能(尽管libpthread.so也在我的库的依赖项中)。

使用程序本身包含的库中的线程类是可行的(而且这个线程类似乎也使用了pthread)。

我也尝试过使用-fabi-version=0-fabi-version=2,因为我的库中的主程序是用gcc 4.1.2编译的,但它没有改变任何东西。

有没有什么我忽略的地方,或者我可以用来使它工作的编译器选项?或者这似乎是我的程序环境的问题?欢迎任何想法。

编辑:

我尝试使用-Wl,-no-as-needed (正如评论中所建议的),但不幸的是,它并没有改变任何事情。

使用clang 3.5而不是gcc 4.8也不起作用。

只要我对主应用程序和共享库都使用gcc 4.8或clang 3.5,就可以创建一个加载共享库的小型测试应用程序(就像下面@chill回答的那样)(即使没有编译器标志)。然而,当使用主程序的gcc 4.1时,主程序甚至无法加载库(它在我的“真正”应用程序中工作)。我认为编译器的不同ABI可能有问题。

直接从pthread.h使用pthread似乎是可行的(虽然程序目前在pthread_join上终止,没有出现错误消息,但我仍在那里测试……)

编辑2:

使用LD_LIBRARY_PATH=$(pwd):$LD_LIBRARY_PATH运行‘测试程序’(因为gcc 4.8的库路径也需要在那里,谢谢@MvG)确实运行了程序,但再次崩溃,并出现Enable multithreading to use std::thread: Operation not permitted异常。

我检查了所有其他加载的库(使用strace ./main_program 2>&1 | grep '^open(".*\.so"'找到它们[参见here]),并使用ldd检查了所有这些库。它们都依赖于相同的库(具有相同的路径)。ldd输出(在所有这些输出上):

代码语言:javascript
复制
linux-vdso.so.1 =>  (0x00007fff4d3fd000)
libstdc++.so.6 => /afs/bb/data/d6833/util/gcc_482/lib64/libstdc++.so.6 (0x00002ade28774000)
libm.so.6 => /lib64/libm.so.6 (0x00002ade28ab0000)
libgcc_s.so.1 => /afs/bb/data/d6833/util/gcc_482/lib64/libgcc_s.so.1 (0x00002ade28d33000)
libc.so.6 => /lib64/libc.so.6 (0x00002ade28f49000)
/lib64/ld-linux-x86-64.so.2 (0x00000032ea200000)

(除了我的库和另一个库之外,它们都不依赖于libpthread.so.0 (但它是相同的/lib64/libpthread.so.0))

一些库确实有更多的依赖项(看起来与线程无关),但似乎没有任何“冲突”的依赖项(在这些库中没有对同一个库的不同版本/路径的依赖项)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-12-17 00:44:22

thread.cc中,您可以看到,如果__gthread_active_p返回false,则会生成此异常。该调用只是检查给定的符号是否可用。所讨论的符号是一个弱符号:它不一定要存在,但会检查它的存在,以确定是否支持线程。

但是符号的存在意味着什么呢?在本例中,这意味着符号在符号表的列表中,相关的库(在我的例子中是libgcc_s.so.1)将搜索符号定义。这包括由应用程序本身导出的符号,也包括由在其之前加载的所有库导出的符号。但是,它不包括后来加载的库。不幸的是,如果在libpthread之前加载libgcc,则符号在其搜索域中不可用。所以它报告线程不受支持。我猜您在加载多线程模块之前已经加载了一些其他C++模块,所以您会遇到这个问题。

my reproducing example中工作的一种解决方案是在用于调用二进制文件的环境中设置LD_PRELOAD=/lib64/libpthread.so.0。这会预先加载libpthread,因此它的符号可用于满足弱符号链接。这对setuid/setgid二进制文件不起作用,在其他情况下也可能被认为是丑陋的技巧,所以我对更干净的解决方案感兴趣。尽管如此,这在大多数情况下都能完成工作。

票数 12
EN

Stack Overflow用户

发布于 2013-12-16 18:18:31

我已经能够复制出与你的情况非常相似的东西。

首先是测试共享库的源代码:

代码语言:javascript
复制
#include <thread>

void f() {}

extern "C" int foo () {
        std::thread(f).join();
        return 0;
}

使用c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so编译/链接

然后是“主程序”:

代码语言:javascript
复制
#include <dlfcn.h>

int
main () {
    void *lib = dlopen ("libthrlib.so", RTLD_NOW);

    int (*foo)();
    foo = (int (*)()) dlsym (lib, "foo");
    foo ();
}

使用gcc main.c -ldl编译/链接

执行的结果是:

代码语言:javascript
复制
velco@sue:~/tmp$ LD_LIBRARY_PATH=`pwd` ./a.out 
terminate called after throwing an instance of 'std::system_error'
  what():  Enable multithreading to use std::thread: Operation not permitted
Aborted (core dumped)

现在,通过向命令行添加选项-Wl,-no-as-needed来构建共享库

代码语言:javascript
复制
c++ -fPIC -shared -std=c++11 -pthread thrlib.cxx -o libthrlib.so -Wl,-no-as-needed

并且主程序执行时没有错误。

我在Ubuntu 13.10和gcc 4.8.1上运行了这个,上面的所有这些实际上可能不适用于你的环境,但效果与你的非常相似,所以有一个很好的机会同样的解决方案将适用于你,无论如何值得一试:)

PS。另一件值得尝试的事情是,在你的库中添加一个对pthread_cancel的显式硬引用:

代码语言:javascript
复制
#include <pthread.h>
void *zzz = (void *) pthread_cancel;
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/20568235

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档