3
gcc (GCC) 4.7.2

こんにちは、

私が開発する 2 つのモジュール (共有ライブラリ) を含む大規模なプロジェクトを開発しています。

モジュールは、私が C で作成した共有ライブラリであり、相互に同期してメッセージを交換する必要があります。

ここに画像の説明を入力

マネージャー モジュールは、これらの両方のモジュール (.so) を制御し、メモリにロードします。失敗した場合。マネージャーはそれを再ロードしようとする可能性があります。

このようなことをしたのはこれが初めてなので、私は疑問に思っています。従うことができる設計パターンはありますか?

これはすべて C で記述され、メモリ プール管理に APR (Apache Portable Runtime) を使用し、必要に応じていくつかのスレッド プールを使用します。

  1. 両方のモジュールをロードするマネージャーを開始します。
  2. Manager はその後、それらの両方でいくつかの関数を呼び出して、おそらくそれらを開始および停止し、場合によってはクリーンアップします。
  3. 両方のモジュールがロードされて開始されたら。彼らはお互いにいくつかのメッセージを交換できるはずです。

モジュールはすべて、Redhat を実行している同じマシン上で実行されます。

ご提案いただきありがとうございます。

4

7 に答える 7

11

マネージャー モジュールは、これらの両方のモジュール (.so) を制御し、メモリにロードします。失敗した場合。マネージャーはそれを再ロードしようとする可能性があります。

単一の C プロセス内にある場合、これは通常悪い考えです。これらのモジュールのいずれかが失敗した場合、安全にアンロードすることはできず、ましてや再度ロードすることはできません。モジュールの障害から回復できるようにする必要がある場合は、独立したプロセスを使用する必要があります。ただし、コードは引き続き .so ファイルに含めることができfork()ます。モジュールごとにマネージャーを 1 回だけロードします。これは、たとえば chrome プラグイン API で使用されるモデルです。

さらに、コンポーネントの障害に対処すること自体が非常に難しい場合があります。A が再起動したからといって、B が新たに再起動された A と通信する準備ができているわけではありません。障害のあるコンポーネントを再起動するためのスーパーバイザ モジュールの階層。モジュールが 2 つしかない場合、これは少しやり過ぎかもしれませんが、少なくとも考慮する必要があります。

コミュニケーションの方法については、いくつかのパラダイムがあります。これらのモジュールが同じプロセスにある場合は、vtable を渡すだけで済みます。たとえば、次のようになります。

// moduleA.h

struct vtable_A {
  void (*do_something)();
};

void set_vtable_B(struct vtable_B *);
struct vtable_A *get_vtable_A();
void start_A();

// moduleB.h
struct vtable_B {
  void (*do_something)();
};

void set_vtable_A(struct vtable_A *);
struct vtable_B *get_vtable_B();
void start_B();

マネージャーは両方をロードし、vtable を A から B に、またはその逆に渡し、その後開始ルーチンを呼び出します。注文には注意してください。B の準備が整う前に A を開始するか、その逆にする必要があります。

それらが独立したプロセスにある場合は、通常、メッセージ パッシングが適しています。これは基本的にその時点でのネットワーク プロトコルです。サブプロセスはシリアル化されたメッセージをマネージャーに送信し、マネージャーはメッセージを他のサブプロセスにルーティングします。会話は次のようになります。

MGR->A      START
MGR->B      START
A->MGR      REGISTER_ENDPOINT 'ProcessA'
A->MGR      WATCH_ENDPOINT 'ProcessB'
MGR->A      OK_REGISTER 'ProcessA'
MGR->A      OK_WATCH 'ProcessB'
B->MGR      REGISTER_ENDPOINT 'ProcessB'
B->MGR      WATCH_ENDPOINT 'ProcessA'
MGR->B      OK_REGISTER 'ProcessB'
MGR->A      NEW_ENDPOINT 'ProcessB'
A->MGR      APPLICATION_DATA TO:'ProcessB', PAYLOAD:"Hello, world!"
MGR->B      OK_WATCH 'ProcessA'
MGR->B      NEW_ENDPOINT 'ProcessA'
MGR->B      APPLICATION_DATA FROM:'ProcessA', PAYLOAD:"Hello, world!"

この種のプロトコルを構築するには、上記の例以外にも多くの方法があり、メッセージ パッシング プロトコルの上に RPC を構築する方法があることに注意してください。以前にこの種のことを行ったDBUS (直接使用できる可能性があります!) やDCOMなどに興味があるかもしれません。この種のプロトコルに加えて、マネージャーを使用して A と B の間にある種の直接チャネルを確立し、A または B を再起動する必要がある場合にのみ再び関与させるなどの最適化が行われます。

とはいえ、マネージャーが何をする必要があるかを理解する前に、マネージャーがどのように機能するかの詳細を深く掘り下げないでください。プラグイン<->マネージャーの高レベルインターフェイスとプラグイン<->プラグイン プロトコルを設計します。その後、プラグイン<->マネージャ インターフェイスの詳細を設計します。CORBASOAPのような非常に複雑なものに行き着いてしまうのは、あまりにも簡単です。

于 2012-10-04T06:54:53.217 に答える
5

以下は、リクエストに基づいたプロジェクトの簡単な例です。

ソース コードのアーキテクチャは次のようになります。

src
   |__handler1.c //containing the main function
   |__handler2.c //containing other functions
   |__lib1.c //containing lib1 source
   |__lib2_file1.c  //containing lib2 source
   |__lib2_file2.c  //containing lib2 source
   |__Makefile  // file which contains commands to build the project
   |__inc
         |__lib1.h
         |__lib2.h
         |__handler2.h

handler1.c

#include <stdio.h>
#include "lib1.h"
#include "lib2.h"
#include "handler2.h"

int main()
{
    char *s1, *s2;
    print_hello_from_handler2();
    s1 = get_message_from_lib1_method1();
    get_message_from_lib1_method2(&s2);

    printf("s1 = %s\n",s1);
    printf("s2 = %s\n",s2);
    printf("extern string_from_lib1 = %s\n",string_from_lib1);
    printf("extern string_from_lib2 = %s\n",string_from_lib2);
}

handler2.c

#include <stdio.h>

void print_hello_from_handler2()
{
    printf("hello world from handler2\n");
}

lib1.c

#include "lib2.h"
char *string_from_lib1="message from lib1 variable";

char *get_message_from_lib1_method1()
{
    return get_message_from_lib2_method1();
}

void get_message_from_lib1_method2(char **s)
{
    get_message_from_lib2_method2(s);
}

lib2_file1.c

char *string_from_lib2="message from lib2 variable";

char *str="message from lib2 method1";

char *get_message_from_lib2_method1()
{
    return str;
}

lib2_file2.c

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void get_message_from_lib2_method2(char **s)
{
    *s = malloc(30);
    strcpy(*s,"message from lib2 method2");
}

lib1.h

extern char *string_from_lib1;

char *get_message_from_lib1_method1();
void get_message_from_lib1_method2(char **s);

lib2.h

extern char *string_from_lib2;

char *get_message_from_lib2_method1();
void get_message_from_lib2_method2(char **s);

handler2.h

void print_hello_from_handler2();

メイクファイル

SHLIB_EXT=so
LINK=$(CC)
SHLIB1_FILE=libmodule1.$(SHLIB_EXT).1
SHLIB2_FILE=libmodule2.$(SHLIB_EXT).1
SHLIB1_FLAGS=-shared -Wl,-soname,$(SHLIB1_FILE)
SHLIB2_FLAGS=-shared -Wl,-soname,$(SHLIB2_FILE)
FPIC=-fPIC

all: libmodule2.$(SHLIB_EXT) libmodule1.$(SHLIB_EXT) handler


%.o: %.c
    $(CC) -Iinc -c -o $@ $^

handler: handler1.o handler2.o
    $(CC) -o $@ $^ -L. -lmodule2 -lmodule1

lib2_file1.o: lib2_file1.c
    $(CC) $(FPIC) -Iinc -c -o $@ $<

lib2_file2.o: lib2_file2.c
    $(CC) $(FPIC) -Iinc -c -o $@ $<

libmodule2.$(SHLIB_EXT): lib2_file1.o lib2_file2.o
    $(LINK) $(SHLIB2_FLAGS) -o $(SHLIB2_FILE) $^
    ln -sf $(SHLIB2_FILE) $@

libmodule1.o: lib1.c
    $(CC) $(FPIC) -Iinc -c -o $@ $<

libmodule1.$(SHLIB_EXT): libmodule1.o
    $(LINK) $(SHLIB1_FLAGS) -o $(SHLIB1_FILE) $< -L. -lmodule2
    ln -sf $(SHLIB1_FILE) $@


clean:
    rm -f *.o *.so* handler
    rm -f /usr/lib/$(SHLIB1_FILE)
    rm -f /usr/lib/$(SHLIB2_FILE)
    rm -f /usr/lib/libmodule1.$(SHLIB_EXT)
    rm -f /usr/lib/libmodule2.$(SHLIB_EXT)

install:
    cp $(SHLIB1_FILE) /usr/lib/
    cp $(SHLIB2_FILE) /usr/lib/
    cp handler /usr/bin/
    ln -sf /usr/lib/$(SHLIB1_FILE) /usr/lib/libmodule1.$(SHLIB_EXT)
    ln -sf /usr/lib/$(SHLIB2_FILE) /usr/lib/libmodule2.$(SHLIB_EXT)

プロジェクトをコンパイルするコマンド

linux$ cd src
linux$ make

次に、バイナリとライブラリをインストールします

linux$ sudo make install

インストールされたライブラリとバイナリをクリーンアップし、ビルド バイナリ ライブラリとオブジェクトをクリーンアップするには:

linux$ sudo make clean

アプリケーションを実行するには:

linux$ handler
hello world from handler2
s1 = message from lib2 method1
s2 = message from lib2 method2
extern string_from_lib1 = message from lib1 variable
extern string_from_lib2 = message from lib2 variable
linux$
于 2012-10-07T16:54:47.927 に答える
2

私は「パターントーク」に少しアレルギーがありますが、これは私がこれにアプローチする方法です:

  • スレッド モデルを決定します。

    • モジュールが制御するメモリを使用して情報を交換しますか、それともマネージャを使用しますか?
    • モジュールは、モジュール間で共有される条件変数またはマネージャーが所有する条件変数を待機する必要がありますか?
  • 必要なマネージャーの汎用性を決定します。

    • モジュールのディレクトリをポーリングするか、構成を読み取るか、またはその両方を実行できる必要がありますか?
    • マネージャがメッセージを管理する場合、モジュール間のシグナリングには何が必要ですか?

これがわかれば、残りはほとんどがモジュールに含まれるビジネス ロジックになります。

于 2012-10-01T09:29:30.247 に答える
1

私が理解しているように、ポイント1と2を切り離す必要があります。

  • そのためには、BootstrapManager と呼ばれる別のクラスが必要です。これは、モジュールのロードと、失敗した場合の再ロードを担当します。
  • 次に必要なのは、Module という抽象クラスです。このクラスには、
    start() - モジュールの開始、stop() - モジュールの停止、cleanUp() - アクティビティのクリーンアップ、communication() - 別のモジュールとの通信の 3 つのメソッドがあります。
  • これで、Module1 と Module 2 の両方がこのクラスを拡張し、それに応じて独自のビジネス ロジックを実装します。
于 2012-10-05T17:48:16.753 に答える
1

1 つの重要な質問: なぜこの方法でこれを実装したいのですか? 基本的に「疎」に結合されたコンポーネントを「密」に結合しています (共有ライブラリにはあらゆる種類のクラッシュ関連の問題があるため、マネージャーが停止します)。必要に応じて 2 つ以上の子プロセスを起動および再起動する (できる) マネージャー プログラムを用意してみませんか。

ある種のプロトコルを使用して、子プロセスが Manager と通信するか、相互に通信するようにします。ZeroMQをお勧めするのは、それが素晴らしいことと、プロセス間通信を完全に隠しているためです。つまり、(異なるマシン間の) ソケット、スレッド間のプロセス間通信、または単一のマシン上の名前付きパイプであり、非常に高速です。これは、クライアントを実装した後、クライアントをどのようにデプロイするかを決定できることを意味します。マネージャーにロードされた共有ライブラリとして、同じボックスで実行される個別のプロセスとして、または個別のマシンで実行される分散プロセスとして、ほとんど必要ありません。何かを変えるために。つまり、非常にスケーラブルです。

ただし、共有ライブラリのアプローチに専念している場合は、実装が少し難しいかもしれませんが、これに絶対にお勧めする「設計パターン」が 1 つあります。しかし、それはその重さの価値があります。

マネージャーは、モジュール間でメッセージを渡す前にタイムスタンプをチェックし、何か変更があった場合は再コンパイルしてリロードする必要があります。これは、コードの変更が「ホット」であることを意味します。変更を確認するためにマネージャーを停止し、再コンパイルし、マネージャーを再起動する必要はありません。したがって、js で開発するように C でプログラミングできます。何時間も節約できます。

少し前に、C++ と APR を使用して同様のことを行いました (ライブラリ間通信ではありません)。コードは少し「ハッキー」ですが、とにかくここにあります;-)

Makefile独自のディレクトリに for each サブモジュールがあることに依存していることに注意してください。依存関係のために、タイムスタンプをチェックせず、リクエストごとに再コンパイルするだけです。これはあなたにとって理想的ではないかもしれないので、その部分を再考する必要があるかもしれません.

それを機能させる上で最も困難な部分は、ディレクトリに対する適切なアクセス許可を取得することでしたが、考えてみると、これはこれを fcgi プロセスとして実行していたため、実際に実行すると Web サーバーとして実行されたためです。ほとんどの場合、これらの問題は発生しません。

#ifndef _CMJ_RUN_HPP
#define _CMJ_RUN_HPP

#include <fcgio.h>
#include <stdlib.h>

#include <iostream>
#include <string>
#include <sstream>
#include <vector>

#include <apr.h>
#include <apr_dso.h>
#include <apr_pools.h>
#include <apr_thread_proc.h>

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/case_conv.hpp>

#include <cgicc/Cgicc.h>
#include <cgicc/HTTPHTMLHeader.h>
#include <cgicc/HTMLClasses.h>
#include <stdexcept>

#include <cstdarg>

class Line {
protected:
    std::stringstream line_;
    bool isError_;
public:
    Line(const char* line, bool isError) : line_(line), isError_(isError) {}
    Line(const Line& rhs) : line_(rhs.line()), isError_(rhs.error()) {}
    bool error() const { return isError_; }
    const char* line() const { return line_.str().c_str(); }
    const Line& operator = (const Line& rhs) {
        line_.str() = rhs.line();
        isError_ = rhs.error();
        return rhs;
    }
};

class Run {
protected:
    int exitCode_;
    std::vector<Line> out_;
    bool errors_;
protected:
    void run(const char* dir, const char* cmd, std::vector<const char*> &args, apr_pool_t* parentPool) ;
public:
    Run(const char* dir, const char* cmd, std::vector<const char*> &args, apr_pool_t* parentPool);
    Run(const char* dir, const char* cmd, apr_pool_t* parentPool);
    int exitCode() { return exitCode_; }
    bool errors() { return errors_; }
    bool errors(std::ostream& out);
    int size() { return out_.size(); }
    Line& line(int i) { return out_[i]; }
};

class dso_error: public std::runtime_error {
public:
    dso_error(const char* c) : std::runtime_error(c) {};
    dso_error(std::string err) : std::runtime_error(err) {};
    static dso_error instance(const char* format, ...) {
        char errbuf[8192];
        va_list va;
        va_start(va, format);
        vsnprintf(errbuf, 8192, format, va);
        va_end(va);
        return dso_error(errbuf);
    }
};

/**
 * Provides a building and loading framework for Dynamic libraries, with the full power
 * of make behind it.
 * Usage:
 * <code>
 * DsoLib so("/var/www/frontier/echo","/var/www/frontier/echo/libecho.so",pool);
 * if (!so.errors(outStream)) {
 *  void (*pFn)(void) = sym("initialize");
 *  (*pFn)();
 * }
 * </code>
 */
class DsoLib : public Run {
protected:
    apr_pool_t* pool_;
    apr_dso_handle_t* dso_;
    std::string dirname_;
    std::string libname_;
public:
    /** dir is the directory where make should be executed, libname is full path to the library
     * from current working directory.
     */
    DsoLib(const char* dir, const char* libname, apr_pool_t* parentPool) throw(dso_error);
    ~DsoLib();
    void* sym(const char* symbol) throw (dso_error);
    void* sym(std::string symbol) throw (dso_error) { return sym(symbol.c_str()); }
};

#endif

そしてRun.cpp

#include "Run.hpp"

#include <string>
#include <sstream>
#include <boost/filesystem.hpp>
#include <cassert>

#define DBGENDL " (" << __FILE__ << ":" << __LINE__ << ")" << endl


using namespace std;

Run::Run(const char* dir, const char* cmd, apr_pool_t* pool) : errors_(false) {
    vector<const char *> args;
    run(dir, cmd, args, pool);
}

Run::Run(const char* dir, const char* cmd, vector<const char*> &args, apr_pool_t* pool) : errors_(false) {
    run(dir, cmd, args, pool);
}

void
Run::run(const char* dir, const char* cmd, vector<const char*> &args, apr_pool_t* parentPool) {
    cout << "Run::run(dir=" << ", cmd=" << cmd << ", args...)" << endl;
    apr_status_t status;
    char aprError[1024];
    struct aprPool_s {
        apr_pool_t* pool_;
        aprPool_s(apr_pool_t* parent) {
            apr_pool_create(&pool_, parent);
        }
        ~aprPool_s() {
            apr_pool_destroy(pool_);
        }
        operator apr_pool_t*  () { return pool_; }
    } pool (parentPool);

    apr_procattr_t* attr;
    if (APR_SUCCESS != (status = apr_procattr_create(&attr, pool))) {
        cerr << "apr_procattr_create error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    if (APR_SUCCESS != (status = apr_procattr_dir_set(attr, dir))) {
        cerr << "apr_procattr_dir_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    if (APR_SUCCESS != (status = apr_procattr_cmdtype_set(attr, APR_PROGRAM_ENV))) {
        cerr << "apr_procattr_cmdtype_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    if (APR_SUCCESS != (status = apr_procattr_io_set(attr, APR_NO_PIPE, APR_FULL_NONBLOCK, APR_FULL_NONBLOCK))) {
        cerr << "apr_procattr_io_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    if (APR_SUCCESS != (status = apr_procattr_user_set(attr, "craig", "lateral"))) {
        cerr << "apr_procattr_user_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    if (APR_SUCCESS != (status = apr_procattr_group_set(attr, "craig"))) {
        cerr << "apr_procattr_group_set error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    apr_proc_t proc;

    const char **argv = (const char**) new char*[ 2 + args.size() ];
    argv[0] = cmd;
    size_t i=0;
    size_t argc=args.size();
    for (i=0; i<argc; i++) {
        argv[i+1] = args[i];
        cerr << "arg " << i << " = " << args[i];
    }
    argv[i+1] = NULL;
    argc++;
    cerr << "About to execute " << cmd << " in dir " << dir << endl;
    cerr << "ARGS:" << endl;
    for (i=0; i<argc; i++) {
        cerr << "[" << i << "]: " << argv[i] << endl;
    }

    if (APR_SUCCESS != (status = apr_proc_create(&proc, cmd, argv, NULL, attr, pool))) {
        cerr << "apr_proc_create error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }

    apr_exit_why_e exitWhy;
    cerr << "--- " << cmd << " ---" << endl;
    while (APR_CHILD_NOTDONE == (status = apr_proc_wait(&proc, &exitCode_, &exitWhy, APR_NOWAIT))) {
        char line[1024];
        status = apr_file_gets(line, sizeof(line), proc.out);
        if (APR_SUCCESS==status) {
            out_.push_back(Line(line, false));
            cerr << line << endl;
        }

        status = apr_file_gets(line, sizeof(line), proc.err);
        if (APR_SUCCESS==status) {
            out_.push_back(Line(line, true));
            errors_ = true;
            cerr << "E:" << line ;
        }
    }
    cerr << " -----" << endl;

    delete[] argv;

    if ( (APR_CHILD_DONE != status) && (APR_PROC_EXIT != status) ) {
        cerr << "apr_proc_wait error: " << apr_strerror(status, aprError, sizeof(aprError)) << endl;
    }
    cerr << cmd << " exited " << ((APR_PROC_EXIT==exitWhy) ? "PROC_EXIT" :
            ((APR_PROC_SIGNAL==exitWhy) ? "PROC_SIGNAL" :
            ((APR_PROC_SIGNAL_CORE==exitWhy) ? "PROC_SIGNAL_CORE" : "Unknown"))) << endl;
}

bool
Run::errors(std::ostream& os) {
    cerr << "Run::errors(ostream) : errors()=" << errors() << endl;
    if (errors()) {
        cerr << "Writing errors to ostream" << endl;
        os << "Content-type: text/html\r\n\r\n";
        os << "<html><head><title>Errors</title>"
            << "<link rel=\"stylesheet\" type=\"text/css\" href=\"css/frontier.css\"></link>"
            << "</head>"
            << "<body>";
        for (int i=0; i<size(); i++) {
            Line& errline = line(i);
            os << "<div class=\"" << ( (errline.error() ? "error" : "out" ) ) << "\">"
                    << errline.line()
                    << "</div>";
        }
        os
            << "</body>"
            << "</html>";
    }
    return errors();
}

DsoLib::DsoLib(const char* dir, const char* libname, apr_pool_t* parentPool) throw (dso_error) :
    Run(dir, "/usr/bin/make", parentPool), pool_(NULL), dso_(NULL), dirname_(dir), libname_(libname)
{
    if (errors()) {
        cerr << "Run encountered errors, quitting DsoLib::DsoLib()" << DBGENDL;
        //throw dso_error::instance("Build failed for dir %s, library %s", dir, libname);
        return;
    } else {
        cerr << "No errors encountered with Run in DsoLib::DsoLib" << DBGENDL;
    }

    apr_status_t status;
    if (APR_SUCCESS != apr_pool_create(&pool_, parentPool)) {
        cerr << "Failed to allocate pool" << DBGENDL;
        throw dso_error("Failed to allocate apr_pool");
    }

    cerr << "Created pool ok" << DBGENDL;   //(" << __FILE__ << ":" << __LINE__ << ")" << endl;

    if (APR_SUCCESS != (status = apr_dso_load(&dso_, libname, pool_))) {
        cerr << "apr_dso_load(" << libname << ") failed" << DBGENDL;
        char aprError[1024];
        throw dso_error::instance("dso_load failed, path=%s, error=%s",
                libname, apr_strerror(status, aprError, sizeof(aprError)));
    }
    cerr << "Loaded dso ok" << DBGENDL;
#if 0
    void (*initialize)(apr_pool_t*) = reinterpret_cast< void(*)(apr_pool_t*) > (sym("initialize"));
    if (initialize) {
        cerr << "found initialize sym: about to call initialize" << DBGENDL;
        initialize(pool_);
        cerr << "initialize(pool) returned ok" << DBGENDL;
    } else {
        cerr << "initialize sym not found" << DBGENDL;
    }
#endif
    cerr << "Exiting DsoLib::DsoLib(" << dir << ", " << libname << ") with success." << endl;
}

DsoLib::~DsoLib() {
    cerr << "Entering DsoLib::~DsoLib(dir=" << dirname_ <<", " << "lib=" << libname_ << ")" << endl;
    if (NULL!=dso_) {
        void (*terminate)(void) = reinterpret_cast<void(*)()>(sym("terminate"));
        if (terminate) terminate();
        apr_status_t status = apr_dso_unload(dso_);
        if (APR_SUCCESS != status) {
            char err[8192];
            cerr << "ERR apr_dso_unload failed: " << apr_dso_error(dso_, err, sizeof(err)) << endl;
        } else {
            cerr << "Unloaded " << libname_ << endl;
        }
    } else {
        cerr << "ERR dso_ handle is NULL" << endl;
    }
    if (NULL!=pool_) apr_pool_destroy(pool_);
}

void *
DsoLib::sym(const char* symbol) throw (dso_error) {
    cerr << "sym('" << symbol << "')" << DBGENDL;
    cerr << "dso_ == NULL ? " << ((NULL==dso_)?"true":"false") << DBGENDL;
    cerr << "dso_ = " << dso_ << DBGENDL;
    assert(NULL!=symbol);
    assert(NULL!=dso_);
    apr_status_t status;
    void* p = NULL;
    if (APR_SUCCESS != (status = apr_dso_sym((apr_dso_handle_sym_t*)&p, dso_, symbol))) {
        cerr << "apr_dso_sym() DID NOT RETURN APR_SUCCESS" << DBGENDL;
        char aprError[1024];
        stringstream err;
        err << "dso_sym failed, symbol=" << symbol << ": " << apr_strerror(status, aprError, sizeof(aprError));
        cerr << err.str() << DBGENDL;
    } else {
        cerr << "sym succeeded for " << symbol << " in " << libname_ << DBGENDL;
    }
    return p;
}
于 2012-10-11T01:21:02.123 に答える
1

APR を使用することを既に決定している場合は、おそらく APR が提供する動的ライブラリ ロードを使用する必要がありますここでチュートリアルを見つけることができます 。

于 2012-10-10T10:33:24.347 に答える
1

ここでのアーキテクチャは比較的単純であるため、複雑な設計パターンは必要ありません。

主な問題はデータの完全性です。システムが部分的にクラッシュした場合、両方に同じデータのコピーがあることをどのように確認しますか?

メッセージングを使用しているため、問題はすでに半分解決しています。次の 2 つのことだけを行う必要があります。

(1) 最近のメッセージのリストを保存し、チェックポイント バックアップとチェックポイント以降のメッセージのリストを指定してモジュールを復元するためのロールバック/更新メカニズムを作成する

(2) メッセージがアトミックであることを確認します。つまり、送信者がメッセージの送信中にクラッシュした場合、不完全な情報を受け入れることで受信者が破損する可能性があるため、部分的なメッセージまたはトランザクションが受け入れられることは決してありません。

問題 2 を解決するには、トランザクションの最後にチェックサムまたはハッシュを追加します。受信者は、ハッシュが受信され、データと一致しない限り、メッセージ セットの受け入れを確定しません。

于 2012-10-10T16:50:13.690 に答える