我目前正在尝试在加载到 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/