1

私が抱えている状況は、共有オブジェクトコンストラクターでファイルスコープ変数 std::string を初期化しようとしているということです。おそらくコードでより明確になるでしょう:

#include <string>
#include <dlfcn.h>
#include <cstring>
static std::string pathToDaemon; // daemon should always be in the same dir as my *.so
__attribute__((constructor))
static void SetPath()
{
    int lastSlash(0):
    Dl_info dl_info;
    memset(&dl_info, 0, sizeof(dl_info));

    if((dladdr((void*)SetPath, &dl_info)) == 0)
        throw up;

    pathToDaemon = dl_info.dli_fname; // **whoops, segfault here**
    lastSlash = pathToDaemon.find_last_of('/');
    if(std::string::npos == lastSlash)
    {
        // no slash, but in this dir
        pathToDaemon = "progd";
    }
    else
    {
        pathToDaemon.erase(pathToDaemon.begin() + (lastSlash+1), pathToDaemon.end());
        pathToDaemon.append("progd");
    }

    std::cout << "DEBUG: path to daemon is: " << pathToDaemon << std::endl;
}

これと同じことを行う非常に単純なプログラムがあります。コンセプトのテスト ドライバー プログラムです。その中のコードは次のようになります: ファイルがロードされたときに dladdr() を使用して *.so ファイルのパスを格納する「共有オブジェクト ctor」。

私が試した変更:

namespace {
    std::string pathToDaemon;
    __attribute__((constructor))
    void SetPath() {
        // function def
    }
}

また

static std::string pathToDaemon;
__attribute__((constructor))
void SetPath() { // this function not static
    // function def
}

std::string pathToDaemon; // variable not static
__attribute__((constructor))
void SetPath() { // this function not static
    // function def
}

上記の例は、静的オブジェクト ライブラリと DLL の両方にコンパイルされたファイルにあります。コンパイルプロセス:

  • static.a のオプション: --std=C++0x -c -Os。
  • shared.so のオプション: -Wl,--whole-archive /path/to/static.a -Wl,--no-whole-archive -lz -lrt -ldl -Wl,-Bstatic -lboost_python -lboost_thread -lboost_regex - lboost_system -Wl,-Bdynamic -fPIC -shared -o mymodule.so [Python に静的なものをラップする多数のオブジェクト]

大規模なプロジェクトでジャンプしなければならないフープにより、小さなテスト ドライバー プログラムが必要とするよりもはるかに複雑なビルド プロセスが作成されます。これは、問題はそこにあると私に思わせます。誰かが私が欠けているものに光を当てることができますか?

ありがとう、アンディ

4

3 に答える 3

0

上記の自己投稿ソリューションでは、»interface« (pathToDaemon / DaemonPath() を読み取るコード用) を »Accessing a file scoped variable« から »calling a function in anonymous namespace« に変更しました。

しかし、DaemonPath() の実装は、スレッドセーフな方法では行われません。あなたの質問には »-lboost_thread« と書かれているので、スレッドセーフは重要だと思います。そのため、実装をスレッドセーフに変更することを考えるかもしれません。利用可能なシングルトン パターンとスレッド セーフについては、多くの議論と解決策があります。

実際には、ライブラリのロードが完了した後、 DaemonPath() が呼び出されます (おそらく遠い)。マルチスレッド環境では、シングルトン パターンへの最初の呼び出しのみが重要であることに注意してください。

別の方法として、次のように DaemonPath() 関数に単純な »early« 呼び出しを追加することもできます。

namespace {
    std::string PathToProgram() {
        ... your code from above ...
    }

    std::string DaemonPath() {
        ... your code from above ...
    }

    __attribute__((constructor)) void MyPathInit() {
        DaemonPath();
    }
}

または、次のようなより移植性の高い方法で:

namespace {
    std::string PathToProgram() {
        ... your code from above ...
    }

    std::string DaemonPath() {
        ... your code from above ...
    }

    class MyPathInit {
    public:
        MyPathInit() {
            DaemonPath();
        }
    } myPathInit;
}

もちろん、このアプローチでは、シングルトン パターンをスレッド セーフにすることはできません。しかし、場合によっては、同時スレッド アクセスがないことを確認できる場合があります (たとえば、共有ライブラリがロードされている初期化時)。この条件が当てはまる場合、このアプローチは、スレッド ロック (mutex...) を使用せずにスレッド セーフの問題を回避する方法になる可能性があります。

于 2013-10-27T18:04:49.337 に答える