C++ 将OpenMP与-fopenmp和-lgomp链接之间的区别

C++ 将OpenMP与-fopenmp和-lgomp链接之间的区别,c++,openmp,static-linking,dlopen,C++,Openmp,Static Linking,Dlopen,这几天我一直在努力解决一个奇怪的问题。我们使用GCC4.8创建了一些库,这些库静态地链接了它们的一些依赖项,例如log4cplus或boost。对于这些库,我们使用boostpython创建了Python绑定 每次这样的库使用TLS时(就像log4cplus在静态初始化中所做的那样,或者stdlibc++在抛出异常时所做的那样——不仅仅是在初始化阶段),整个过程都会在segfault中崩溃——并且每次线程局部变量的地址都是0 我尝试了一切,比如重新编译,确保使用了-fPIC,确保使用了-tls

这几天我一直在努力解决一个奇怪的问题。我们使用GCC4.8创建了一些库,这些库静态地链接了它们的一些依赖项,例如log4cplus或boost。对于这些库,我们使用boostpython创建了Python绑定

每次这样的库使用TLS时(就像log4cplus在静态初始化中所做的那样,或者stdlibc++在抛出异常时所做的那样——不仅仅是在初始化阶段),整个过程都会在segfault中崩溃——并且每次线程局部变量的地址都是0

我尝试了一切,比如重新编译,确保使用了-fPIC,确保使用了-tls model=global dynamic,等等。没有成功。然后今天我发现这些崩溃的原因是我们将OpenMP链接到的方式。我们使用“-lgomp”而不是仅仅使用“-fopenmp”来实现这一点。自从我改变了这一切都很好-没有崩溃,没有什么。很好

但我真的很想知道问题的原因是什么。那么,在OpenMP中链接这两种可能性之间有什么区别呢

我们这里有一台CentOS 5机器,我们在/opt/local/gcc48中安装了一个GCC-4.8,并且我们还确定使用了来自/opt/local/gcc48的libgomp以及libstdc++(使用了DL_调试)


有什么想法吗?在Google上找不到任何东西-或者我使用了错误的关键字:)

OpenMP是代码和执行之间的中介。每个
#pragma omp
语句都转换为对相应OpenMP库函数的调用,这就是它的全部内容。多线程执行(启动线程、连接和同步线程等)始终由操作系统(OS)处理。OpenMP所做的只是在一个简短的界面中为我们处理这些低级别的依赖操作系统的线程调用

-fopenmp
标志是一个高级标志,它不仅仅包括GCC的OpenMP实现(gomp)。这个gomp库需要更多的库来访问操作系统的线程功能。在兼容POSIX的操作系统上,OpenMP通常基于pthread,需要链接pthread。它可能还需要实时扩展库(librt)在某些操作系统上工作,而在其他操作系统上则不需要。当使用动态链接时,应该自动发现所有内容,但是当您指定
-static
时,我认为您陷入了Jakub Jelinek描述的情况。但是现在,当使用
-static
时,应该自动链接pthread(以及rt,如果需要的话)

除了链接依赖项外,
-fopenmp
标志还激活一些pragma语句处理。在整个GCC代码(如和)中,您可以看到,如果没有
-fopenmp
标志(不通过仅链接gomp库触发),多个pragma将不会转换为相应的OpenMP函数调用。我刚刚尝试了一些示例代码,并且
-lgomp
-fopenmp
都生成了一个链接到相同库的可执行文件。我的简单示例中唯一的区别是,
-fopenmp
有一个
-lgomp
没有的符号:
GOMP_parallel@@GOMP_4.0+
(code),该函数初始化并行部分,执行示例代码中的
\pragma omp parallel
请求的fork。因此,
-lgomp
版本没有将pragma转换为对GCC OpenMP实现的调用。两者都产生了一个工作的可执行文件,但在本例中,只有
-fopenmp
标志产生了一个并行可执行文件


总而言之,GCC需要使用
-fopenmp
来处理所有OpenMP杂注。如果没有它,您的并行部分将不会分叉任何线程,这可能会造成严重破坏,具体取决于内部代码所基于的假设。

-pthread或-lpthread已使用
-v
进行编译,并比较输出…添加-v作为链接器选项显示-fopenmp在末尾隐式添加了-lgomp。其他一切都保持不变。没有-fopenp,我就有了“-lstdc++-lm-lgcc_s-lpthread-lc-lgcc_s”,而有了-fopenmp,它就变成了“-lstdc++-lm-lgomp-lgcc_s-lpthread-lc-lgcc_s”。我仍然不明白崩溃的原因,因为所有这些库都是动态链接的:(那么,-l标志的顺序可能很重要。可能-lgomp在-lpthread或其他一些排列之前是很重要的。您可以尝试使用LD_PRELOAD来查看是否以不同的顺序加载依赖项会产生不同的效果。LD_PRELOAD libgomp.so确实有效-因此在加载Ope时似乎发生了一些有趣的事情关于TLS的nMP…让我们看看我们是否能找出那里到底发生了什么…用你的例子,你构建了(编译)使用
-fopenmp
的源代码,然后在链接
-fopenmp
/
-lgomp
时使用您的
.o
?当您使用
gcc-fopenmp example.c
时,它将在编译时启用omp pragma,并在链接时添加库;但以
gcc-lgomp example.c的形式使用的单个命令compile+link将不会通过openmp启用编译选项和pragma omp将被忽略。我可能错了,但我相信我们说的是同一件事?我写了“只有
-fopenmp
将生成并行可执行文件”,而你写了类似“
-lgomp
将不会生成并行可执行文件”的东西,或者我遗漏了什么?(正如我所写,没有
-fopenmp
,pragmas不会转换为函数调用)无论如何,我同意你所说的,我相信答案也是这样。也许我的答案的措辞会更好,不过。。。