在从动态库加载的函数中使用OpenMP时出错

Tricky error using OpenMP in function loaded from dynamic libraries

本文关键字:OpenMP 出错 函数 动态 加载      更新时间:2023-10-16

我的问题涉及在动态库中存储的C++函数中使用OpenMP。让我们考虑以下代码(在shared.cpp中):

#include "omp.h"
#include <iostream>
extern "C" {
int test() { 
  int N = omp_get_max_threads();
#pragma omp parallel num_threads(N)
  {
    std::cout << omp_get_thread_num() << std::endl;
  }
  return 0;
}
};

我使用g++编译此代码:g++-fopenmp-shared-fPIC-o shared.so shared.cpp。然后,要使用测试函数,我有以下程序(main.cpp):

#include <iostream>
#include <dlfcn.h>
int main() {
  void* handle = dlopen("./shared.so", RTLD_NOW);
  if (!handle) {
    std::cerr << "can not open shared.so" << std::endl;
    return 1;
  }
  int(*f)() =  (int(*)()) dlsym(handle,"test");
  if (!f) {
    std::cerr << "can not find 'test' symbol in shared.so" << std::endl;
    return 1;
  }
  (*f)();
  if (dlclose(handle)) {
    std::cerr << "can not close shared.so" << std::endl;
    return 1;
  }  
  return 0;
}

使用命令编译:g++-o main main.cpp-ldl问题是分段错误发生在程序执行的最后。根据valgrind的说法,一些线程在这一点上仍然是活动的,这似乎与OpenMP的行为一致。

这篇文章中的一个解决方案(针对C代码)是使用gcc-fopenmp标志编译程序,但g++似乎足够聪明,可以检测到该程序中从未使用过OpenMP,也从未加载过OpenMP环境(两个版本的汇编代码相等)。我找到的唯一解决方法是在程序中对OpenMP进行无用的调用,这将迫使g++加载OpenMP环境,然后执行就正确了。但对我来说,这种变通方法相当丑陋。我试过g++-4.8.2、g++-4.8.1、g++-4-7.3和g++-4.6.4。(对于icc-14,在程序中使用-openmp选项实际上可以解决问题)。

有人遇到过这个问题吗?有更清洁的解决方法吗?谢谢Thomas

编辑尝试使用G++-4.9.2:仍然失败

我认为您看到了GCC的OpenMP运行库libgomp的问题。尝试与它链接:g++-o main main.cpp-ldl-lcomp,您的segfault就会消失。

libgomp有一些在第一次OpenMP调用时初始化的内部状态。由于某些原因,如果动态加载OpenMP库,则不会发生取消初始化的情况。对我来说这听起来像个bug。

英特尔编译器有自己的OpenMP运行时(libomp5),它没有这个问题。

我遇到了一个非常类似的问题,这是由于使用了带有RTLD_LAZY | RTLD_GLOBAL标志的dlopen。将RTLD_LAZY替换为RTLD_NOW使其能够工作。

对于以上问题,我建议尝试RTLD_NOW | RTLD_GLOBAL