c++ - 在没有线程支持的程序加载的共享库中使用 C++11 多线程

标签 c++ multithreading c++11 shared-libraries dynamic-linking

我目前正在尝试在加载到 Linux 上的主程序(用 C 编写)的共享库中使用 C++11 多线程。这是一个大型模拟程序的一部分,我无法更改有关库加载的任何内容或一般更改主程序。

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

共享库使用 gcc 4.8.2 编译,以便使用 C++11 多线程。我正在传递编译器命令

-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)编译独立测试程序可以在我的系统上正常工作。但是当我启动加载共享库的程序时,出现异常:

Caught std::exception!
Exception Message: Enable multithreading to use std::thread: Operation not permitted

Terminating...

使用 -pthread-lpthread ( 编辑: 并且只有 -pthread 没有 -lpthread )编译参数不起作用。
编译器参数是(我使用的是 Cook 构建系统):

-pthread -std=c++11 -fmessage-length=0 -fPIC -Wchar-subscripts ...(lots of -W* here)
... -Wunused-variable -m64 -D__64BIT__ -pthread -lpthread

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

-pthread -lpthread -std=c++11 -pthread -lpthread -std=c++11 -shared -fPIC -Wl,-Bsymbolic -Wl,--allow-shlib-undefined -pthread -lpthread

在我的库上调用 ldd 给出以下输出

$ 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)

并在主程序上

$ 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指出您必须将 pthreads 链接到主程序,但对此答案的第二条评论(@R..)表示没有必要(这听起来合乎逻辑)。

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

请注意,其他 C++11 功能确实有效(并且 libstdc++.so 在我的库的依赖项中)但 C++11 多线程不是(尽管 libpthread.so 也在我的库的依赖项中)。

使用程序本身包含的库中的线程类是有效的(这个线程类似乎也使用 pthreads)。

我也试过用 -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 使用 pthreads似乎工作(虽然程序当前终止于 pthread_join 没有错误消息,但我仍在那里测试......)

编辑 2:

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

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

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 ))

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

最佳答案

thread.cc 你可以读到如果 __gthread_active_p 会产生这个异常返回假。该调用仅检查给定符号是否可用。有问题的符号是一个弱符号:它不必存在,但会检查它的存在以确定是否支持线程。

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

一种适用于 my reproducing example 的解决方案正在设置 LD_PRELOAD=/lib64/libpthread.so.0在用于调用二进制文件的环境中。加载 libpthread前面,所以它的符号可用于满足弱符号链接(symbolic link)。这不适用于 setuid/setgid 二进制文件,并且在其他情况下也可能被认为是丑陋的黑客,所以我对更清洁的解决方案感兴趣。尽管如此,这在大多数情况下都能完成工作。

关于c++ - 在没有线程支持的程序加载的共享库中使用 C++11 多线程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20568235/

相关文章:

c++ - 检测方 block opencv和c++

c++ - 我应该存储整个对象还是指向容器中对象的指针?

c++ - 线程通信理论

c++ - 指针和数组与指针和整数 C++

c++ - 资源密集型多线程杀死其他进程

c++ - 为什么std::lock_guard在使用std::adopt_lock之后释放锁?

c++ - 用于同步的条件变量与原子类型

c++ - 使用 shared_ptr 时出现 SEGFAULT

c++ - Qt 版本检查和 QOverload 未在范围内定义

c++ - C++ unordered_map 的 rehash() 和 reserve() 方法有什么区别?