SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

japan.internet.com翻訳記事

Win32で動作するMySQL 5.1用ストレージエンジンプラグイン

遅延読み込みを活用する

  • X ポスト
  • このエントリーをはてなブックマークに追加

プラグインのセットアップ

3. プラグインに遅延ローダーを追加しリンカーに動的リンクを指定する

 次に、ストレージエンジンプラグインの遅延ローダーのコードで、存在しないmysqld.dllを遅延読み込みできるようにします。そのためには、mysqld.dllを使うようリンカーに指示する必要があります。MySQLではcmakeビルドシステムを使用するので、CMakeLists.txtファイルを適切に編集します。以下の行を追加し、適宜編集を加えます。

SET(DELAY_LDR_DIR ../../win/delayload)
SET(DELAY_LOADER ${DELAY_LDR_DIR}/mysqld ${DELAY_LDR_DIR}/mapdelayldr)
SET_TARGET_PROPERTIES _
   (your_storage_engine PROPERTIES LINK_FLAGS "/DELAYLOAD:mysqld.dll")
TARGET_LINK_LIBRARIES(your_storage_engine ${DELAY_LOADER})

 遅延ローダーについて若干補足しておきます。

 遅延ローダーは、ホストプロセス(この例ではmysqld.exe)のディレクトリの.mapファイルをハッシュテーブルに読み込みます。そして、このテーブルを使用してアドレスを解決します。しかし、遅延ローダーのモジュールには、ちょっとした問題点があります。

 実は、厄介なことに、VC6のリンカーとVC7以降のリンカーでは、遅延ローダーに違いがあるのです。というのも、64ビットアーキテクチャが登場したときに、遅延ローダーのデザイナが、ImgDelayDescrでポインタではなくRVAを使うべきだったことに気付いたからです。したがって、適切なリンクのコードを使用して、両方のバージョンのリンカーに対応する必要があります。Visual Studioのバージョンによって、ヘッダーファイルは異なります。遅延ローダーのコードは、この点に対応して、正しいバージョンでコンパイルされるようになっています。この遅延ローダーはVC6のリンカーでのみテストしました。新しいバージョンの遅延ローダーで動作する保証はありませんが、理屈上は正しく動くはずです。

4. リンクする

 次に、cmakeでmakefileを作成し、リンクしてみましょう。私はその作業にnmakeを使用しているので、MySQLのメインディレクトリでcmake . -G "NMake Makefiles"を実行し、その後で、nmakeコマンドでプラグインをビルドします。でも、cmakeのターゲットが異なる場合でも、うまくいくはずです。

 プラグインによっては、未解決の外部シンボルが原因でリンカーエラーが生じることがあります。これは通常、プラグインで使用しているグローバルデータ変数を示しています。たとえば、私が最初にプラグインをリンクしたときには、次のような未解決シンボルのエラーが表示されました。

ha_storage.obj : error LNK2001: Nichtaufgeloestes externes Symbol _
   "class Bitmap<64> const key_map_empty" _
   (?key_map_empty@@3V?$Bitmap@CODE_REPLACEMENT 4EA@@@B)
ha_storage.obj : error LNK2001: Nichtaufgeloestes externes Symbol _
   _my_charset_bin
ha_storage.obj : error LNK2001: Nichtaufgeloestes externes Symbol _
   "struct charset_
info_st * system_charset_info" _
   (?system_charset_info@@3PAUcharset_info_st@@A)
storage.dll : fatal error LNK1120: 3 unaufgeloeste externe Verweise

5. 外部データシンボルを手動で解決する

 残念ながら、このリンカーが遅延読み込みできるのは関数だけで、データシンボルは読み込めません。そこで、ちょっとした裏技が必要になります。

 どうにかして、遅延ローダーが解決できるようなポインタを用意し、これらのポインタがプラグインで逆参照されるようにしなければなりません。基本的には、シンボルをポインタに変換することが必要で、そのポインタを使用するときには必ずポインタを逆参照して正しいメモリ位置を参照するようにします。このようなシンボルはMySQLのヘッダーファイルで定義されることもあるので、一連の処理は透過的な方法で行わなければなりません。幸い、このからくりは、プリプロセッサを使用して実現できます。方法は次のとおりです。

 まずは、オブジェクトへのすべての参照をポインタに変換し、逆参照する必要があります。そこで、そのための#defineをソースの冒頭部分に記述します(これはMySQLのヘッダーをインクルードする前に行います)。私の例の場合、次のような記述になります。

#ifdef WIN32
#define system_charset_info *ppsystem_charset_info
#define my_charset_bin *pmy_charset_bin
#endif

 次に、必要なMySQLのヘッダーをインクルードします。

#include "mysql_priv.h"

 次に、不足しているシンボルの宣言をMySQLのソースコード内で探し、プラグインファイルに記述します(この時点でプリプロセッサの処理は完了します)。

#ifdef WIN32

const key_map key_map_empty(0);
CHARSET_INFO *system_charset_info;
CHARSET_INFO my_charset_bin;
#endif

 key_map_emptyは常に、ここで宣言したように宣言されるので、元のシンボルを参照する必要はありません。しかし、他のエクスポートについては、これを同様に行います。この時点で、関数ポインタの宣言は済んでおり、使用時に逆参照されます。もちろん、この段階ではポインタはまだ無効です。したがって、モジュールの初期化時にポインタを初期化する必要があります。そのためには、遅延ローダーの動作について知っている必要があります。ここでは、そのための関数を2つ宣言します。1つは_LoadMyMapfileで、もう1つはGetMapProcAddressです。

 _LoadMyMapfileが行うのは、mysqld.mapファイルを調査および解析し、すべての関数および対応するアドレスを、高速に検索できる内部ハッシュテーブルに追加するという処理です。この関数の戻り値は、モジュールハンドルを示すHMODULEです。GetMapProcAddressで個別の関数の正しいアドレスを取得するときには、そのハンドルを使います。_LoadMyMapfileの戻り値が0の場合は、エラーが発生した(.mapファイルが見つからなかった)という意味です。HMODULEは常にモジュールの読み込みアドレスで、取得するハンドルはmysqld.exeのハンドルなので、個別のインポート関数について、正しく再配置されたアドレスを判断できます。

 簡単にまとめると、まず必要なのは、遅延ローダーで2つの関数を次のように宣言することです。

#ifdef WIN32

extern "C" HMODULE _LoadMyMapfile(void);
extern "C" FARPROC GetMapProcAddress (HANDLE hModule, _
   const char *szImport);
#endif

 次に、プラグインのinit_funcで、リンカーでエラーとなったすべての外部シンボルを解決します。前述のエラーメッセージでは、妙な記号の付いた名前が示されていました。その名前をここで使います。今回の例では、プラグインの初期化関数に次のコードを追加します。

#ifdef WIN32

   HMODULE hMod = _LoadMyMapfile();
   if (!hMod) DBUG_RETURN(1);>
   pmy_charset_bin = (CHARSET_INFO*)GetMapProcAddress(hMod, _
      "my_charset_bin");
   ppsystem_charset_info = (CHARSET_INFO**)GetMapProcAddress(hMod, _
      "?system_charset_info@@3PAUcharset_info_st@@A");
#endif
 ここでは、前述の#defineで使用した関数名が必要です。プリプロセッサが書き込む逆参照バージョンは必要ないからです。また、シンボルの型のポインタにキャストする必要がある点にも注意してください(ポインタを追加)。

 以上の面倒な手順が完了したら、ストレージエンジンを再度リンクしてみてください。今度はきちんとリンクできるはずです。

 おつかれさまでした。これで、動的読み込みが可能なストレージエンジンプラグインの完成です。

補足

 この記事で紹介した手法は、MySQLがWin32用のプラグインAPIに修正を加えるまでの回避策にすぎません。MySQLは、MySQLdのすべての機能をDLLに組み込んで、mysqld.exeはその単なるローダーにしてくれればよいのになあと思います。そうすれば、プラグインはそのDLLを使えるようになって、この記事の回避策は無用になります。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
japan.internet.com翻訳記事連載記事一覧

もっと読む

この記事の著者

japan.internet.com(ジャパンインターネットコム)

japan.internet.com は、1999年9月にオープンした、日本初のネットビジネス専門ニュースサイト。月間2億以上のページビューを誇る米国 Jupitermedia Corporation (Nasdaq: JUPM) のニュースサイト internet.comEarthWeb.com からの最新記事を日本語に翻訳して掲載するとともに、日本独自のネットビジネス関連記事やレポートを配信。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

Ludwig Ertl(Ludwig Ertl)

8歳でプログラミングを始める。最初はCommodore 64のBASICで、後にIBM PCに移行。アセンブラとCを愛好し、システムレベルプログラミングへの造詣を深めた。現在は、オーストリアの医療ソフトウェア企業でUNIXのCプログラマとして勤務。システムプログラミング全般に深い興味を持ち、特にIn...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2009 2008/01/21 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング