RtORBにおけるORBとPOA
ORBの構造体
RtORB内のORBは、corba-defs.hの中で次のような構造体で定義されています。
typedef struct CORBA_ORB_struct{
unsigned char *_id; // 現在使っていません。本来は一意のオブジェクトIDを生成しておくのですが、、、
PtrArray *_threads; //これもMulti-Threadに対応していませんので、未使用です。
PtrArray *_adapters; //ORB内のオブジェクトアダプタのリスト(サーバントのリストになります)
hashtable *_object_table; // ORB内のオブジェクトテーブル
CORBA_PolicyList *_policies; // 未使用
char *hostname; // ORBを起動したホスト名(IPアドレス)
unsigned long request_id; // 未使用
struct PortableServer_POAManagerFactory_struct *poa_mgr_factory; // RootPOAManager
CORBA_Config cfg; // 初期設定のコマンド引数などの処理で使っています。
} CORBA_ORB_struct;
typedef CORBA_ORB_struct * CORBA_ORB;
この定義は、threadの対応や、policiesなどが定義できるようにしていますが、実際にはほとんど使っていません。また、PtrArrayやhashtableという型定義がありますが、これらは、私が以前実装した汎用配列構造体とハッシュ表構造体になります。OpenRTMでもそうですが、現在のCORBAでは、BOAはほとんど使われないので、ORBの構造体には、RootPOAとRootPOAManager、それからオブジェクトアダプタのリストくらいしか使っていません。
POAの構造体
RtORBにおけるPOAは、poa.hの中で下記のように定義されています。
typedef struct PortableServer_POA_struct{
struct CORBA_ORB_struct *orb; // ORB
char *id; // ORB内で一意のID。例えば RootPOA など。
struct GIOP_Connection *_server; // POAのサービスのためのGIOPポート
PortableServer_POAManagerFactory mgr_factory; // POAManagerFactory
PortableServer_POAManager manager; // POAManagr
PtrArray *children;
struct PortableServer_ServantManager_struct *servant_mgr;
hashtable *object_map; // POA内のオブジェクトアダプタの登録用テーブル。初期値は64で生成している。
PtrList *requests;
}PortableServer_POA_struct;
typedef PortableServer_POA_struct * PortableServer_POA;
こちらもCORBAの仕様に基づいて定義している変数がありますが、POAを使わないのであれば要らないのかもしれません。OpenRTM-aistのように一般ユーザがCORBAレベルのプログラミングまでに関与しなければ、BOAのみでも十分かと思います。
ORBの初期化(CORBA_ORB_init)
RtORBでは、init_ORB関数でORBの初期化と初期設定、POAManagerFactor,RootPOAManager,RootPOAの生成を行っています。もちろんORBの_object_table内には、RootPOAのみが登録されている状態になります。上記にも書いていますが、ORBが保持するのは、RootPOAとRootPOAManagerとあれば、NameServerのオブジェクトくらいだったと思います。下記にRootPOAManagerとRootPOAの生成での動作を書いておきます。
POAManagerFactoryの生成(PortableServer_POAManager_Factory_new)
POAManagerFactoryは、PortableServer_POAManegr_Factory_new関数で生成します。これは、PortableServer_POAManagerFactory構造体の生成と初期化を行っているのみです。
RootPOAManaerの生成(PortableServer_POAManagerFactory_create_POAManager
RootPOAManagerの生成は、PortableServer_POAManagerFactory_create_POAManager関数を呼び出すことで生成されます。この関数では、先ず、POAManagerFactory内のManagerリストに生成するManagerと同じIDのものが有るかどうかを確認します。新規のものであれば、PortableServer_POAManager構造体を生成し、初期化します。そして、自分自身をPOAManagerFactoryに登録します。
RootPOAの生成(PortableServer_POA_new)
RootPOAは、ORBの初期化時に生成され、PortableServer_POA_new関数を呼び出すことで生成されます。CORBAでは、ORB内で必ずただ一つのRootPOAとRootPOAManegerが存在するように規定されています。PortabelServer_POA_new関数では、下記のような処理を行います。
- PortableServer_POA_struct の生成
- 生成したPortableServer_POA_struct のobject_mapを初期化。初期値は、64アイテムが保存できるハッシュ表です。
- POAに対するGIOPポート用の構造体の生成。これは、GIOP_Connection__create 関数を呼び出すことで生成します。
このときは、単に GIOP_Connection 構造体を生成し、ホストのIPアドレスのみを設定します。 - GIOPのためのソケットポートの生成。
これは、GIOP_Connection__open関数を呼ぶことでGIOPの通信に必要なsocketポートの生成を行います。
この関数では、ポート番号を引数で指定することができますが、0を指定するとシステム側で適当なポート番号を割り当てます。 - requests スロットを0に設定し、idを設定する。
- そして、set_SocketProfile_arg関数で、socketポートに対応したSocketProfile構造体のargスロットに生成したPOAを代入。
このSockProfile構造体は、低レベルの通信を行うときに使用され、各ポートに接続時、切断時に呼び出す関数を設定したり、ポートにデータが来たときに呼び出す関数を設定することができます。このargスロットはポート接続、切断時に呼び出される関数の引数になります。現在に実装では、使っていません。
上記からわかること<
上記のプロセスは、あくまでCOBRAの仕様を満たすように、実装したもので多くのものは、実際の通信には必要ありません。CORBAは、IIOPで通信する場合には、自分のプロセス内のオブジェクトアダプタの一覧とサービスの通信ポート位を管理していれば、十分に動作するものと思います。現在のRtORBの実装では、各通信ポートごとにバッファを持っておらず、すべての通信ポートでバッファを共有しているので、Threadへの対応は難しいと思います。
別の実装では、通信ポートごとに動的に拡張可能なリングバッファをもっているものがありますので、そちらに移行すればThreadへの対応は可能にると思います。
CORBA_ORB_resolve_initial_referenceによるCOBRAオブジェクトの獲得
この関数は、ORBの起動時に-ORBInitRefオブションで設定したオブジェクトリファレンスとORBのobject_tableに格納されているCORBA_Objectの中から、object_keyが同じものを検索して返します。関数の定義は、orb.cの中に
CORBA_Object CORBA_ORB_resolve_initial_reference(CORBA_ORB orb, char * obj_key, CORBA_Environment *env);と書かれています。検索する順番は、-ORBInitRedオプションで指定したものを最優先にしています。もし一致するオブジェクトが存在しない場合にが、CORBA_Environmentに"InvalidName"のシステムエラーを設定します。なお、'-ORBInitRef’の方に登録しているオブジェクトリファレンスは、CORBA_ORB_string_to_object関数でCORBA_Objectに変換されます。
CORBA_ORB_string_to_object関数<
この関数は、CORBAのURL表現(corbaloc://...)からCORBA_Objectを生成します。CORBAのURLのパージングは、parseCorbaURL関数で実施され、object->_urlに格納されるようになっています。このパージング後のURLは、動作速度を少し上げる以上の効果はありませんので(CORBA_Connectionやobject_keyがあれば再構成できますので)、COBRA_Objectに持たせる必要はないかもしれません。この辺りは、実装が予告なく変更される可能性があります。
CORBA_Objectの生成
CORBA_Objectの生成には、new_CORBA_Object関数で行いますが、この関数では、単に一意のobject_keyを設定しているのみです。実際には、XXX.idlからidlコンパイラによって生成された XXX-skelimpl.c に具体的なオブジェクトの実装を記述する必要があります。C++でRtORBを使用する場合には、omniORBなどを使ってプログラミングした場合と同じコードが使えるようになっています。
NameServerへの登録とNameServerからオブジェクトリファレンスの獲得
COBRAを利用する場合に、一般に、分散オブジェクトの名前からオブジェクトリファレンスを得る必要があります。すなわち、IORで表されたオブジェクトリファレンスは、NameServerに登録し、呼び出し側は、名前をキーにしてNameServerからIORを得るのが一般的です。
RtORBでは、NameServerにCORBA_Objectを登録には、bindObjectToName関数を使い、NameServerからオブジェクトリファレンスを得るには、CosNaming_NamingContext__resolve関数を使います。
POAManagerの獲得と活性化
CORBAでは、CORBA Servant(実装側)では、POAManagerを活性化する必要があります。POAManagerは、POAに対して1つのPOAManagerが割り当てられますが、システム全体で1つのPOAManagerを共有して使ってもよいことになっています。
RtORBでは、ほとんどRootPOAしか使いませんので、POAManagerも一つ存在します。POAManagerは、ORBの初期化時に作成されていますので、ORB構造体へ直接アクセスしてもよいのですが、通常は、PortableServer_POA__get_the_POAManager関数を使います。また、活性化に関しては、PortableServer_POAManager_activate関数を使いますが、この関数では、登録されているすべてのPOAオブジェクトを活性化します。RtORBでは、通信のためのイベントループにPOAのサービス用Socketポートに対して、データ受信時に呼び出される関数として、PortableServer_enqueue_request関数を設定します。この関数は、poa.cに
int PortableServer_enqueue_request(GIOP_ConnectionHandler *h){
PortableServer_POA poa = (PortableServer_POA) GIOP_ConnectionHandler_get_arg(h);
if((poa->requests = (PtrList *)GIOP_enqueue_request(h, poa->requests)) == NULL ){
return -1;
}
return 1;
}
と実装されています。GIOP_ConnectionHandler_get_arg関数は、ORB_initのときに登録しまいしたが、予めset_SockeProfile_arg関数で登録したPOAオブジェクトを返すだけです。また、GIOP_enqueue_request関数は、呼び出し時にGIOPメッセージ受信用バッファを確保し、データを受信します。そして、POAオブジェクトのrequestsに、リクエストを追加するために、GIOP_Request構造体 request を新たに生成します。POAオブジェクトのrequestsは、PtrList構造体であり、 汎用のリスト構造を提供したものです。
GIOPのメッセージ本体を受信するために、request->buf に受信データ保存用のバッファを確保し、GIOPメッセージをコピーします。その後に、requestを poa->requestsの末尾に追加し、最初に確保したデータ受信用バッファを削除します。ここで注意して欲しいのは、この関数は、リモートからの呼び出しメッセージを受信し、Queueに入れることしかしない。実際の関数呼び出しは、リクエストのあったすべてのSocketポートからメッセージを受信し終わった後に、逐次的に関数呼び出しが行われる。このことは、関数呼び出しの実効時に、別のリモートオブジェクトに関数を呼び出し、待ち状態に入ってしまうと、他からのリクエスト処理でできないことを意味する。このことは、Aが実装側、Bが呼び出し側の場合に、BがAのメッソドを呼び出している処理内で、AがBにリモート呼出をしてしまうとデッドロック状態に陥ってしまう。そのため、関数呼び出しは、直ちに返る必要がある。
CORBAイベントループの実行
CORBAで通信を行うには、通信用のイベントループを実行させる必要があります。これは、クライアントーサーバープログラムでのサーバーの通信ループとほぼ同じです。通信ループの実行は、CORBA_ORB_run関数を呼び出すことで開始されます。RtORBでは、実装側のSocketポートをselectで入力イベント待ちをするPOA_main_loop関数を呼び出し、無限ループが実行されることになります。このイベントループの詳細は、こちらで。




