N1 Grid Engine 6.1 に付属する DRMAA C 言語バインド実装を使用するには、重要なファイルがある場所を知っておく必要があります。もっとも重要なファイルは、C アプリケーションから DRMAA の関数を利用できるようにするために、アプリケーションからインクルードする DRMAA ヘッダーファイルです。DRMAA ヘッダーファイルは、 sge-root/include/drmaa.h にあります。ここで sge-root は、デフォルトで /usr/SGE です。DRMAA 関数の詳細な参照情報については、sge-root/man ディレクトリにある、N1 Grid Engine のマニュアルページの 5 節を参照してください。アプリケーションのコンパイルとリンクでは、 sge-root/lib/arch/libdrmaa.so にある DRMAA 共有ライブラリを使用してください。
アプリケーションで DRMAA の関数を使用するには、DRMAA 関数を使用するすべてのソースファイルに DRMAA ヘッダーファイルをインクルードします。ソースファイルに DRMAA ヘッダーファイルをインクルードするには、ソースコードの通常は先頭近くに次の行を追加します。
#include "drmaa.h"
DRMAA アプリケーションをコンパイルする場合、コンパイラとリンカーに DRMAA を使用するよう指示するための追加のコンパイラ指令を含める必要があります。次の指示は Sun Studio Compiler Collection と gcc に該当しますが、ほかのコンパイラおよびリンカーには該当しません。ご使用のコンパイラおよびリンカー製品のマニュアルを参照してください。
2 つの指令を含めます。
コンパイラコマンド行に次の文を追加して、DRMAA ヘッダーファイルをインクルードするようコンパイラに指示します。
-I<sge-root>/include
コンパイラかリンカーコマンド行、またはその両方に次の文を追加して、DRMAA ライブラリをインクルードするようリンカーに指示します。
-ldrmaa
また、sge-root/lib/arch ディレクトリがライブラリ検索パス (Solaris オペレーティング環境および Linux の場合は LD_LIBRARY_PATH) に含まれていることを確認する必要があります。settings.sh または settings.csh ファイルを使用して環境設定した場合、sge-root/lib/arch ディレクトリは自動的には追加されません。
Sun Studio Compiler を使用して DRMAA アプリケーションをコンパイルする例を示します。この例は、次のことを前提にしています。
Solaris ホストで csh シェルを使用。
N1 Grid Engine のインストール場所は /sge。
DRMAA アプリケーションの保存場所は app.c。
コマンド例は、次のようになります。
% source /sge/default/common/settings.csh % cc -I/sge/include -ldrmaa app.c
コンパイルした DRMAA アプリケーションを実行するには、次のことを確認します。
sge-root/lib/arch ディレクトリがライブラリ検索パス (Solaris オペレーティング環境および Linux の場合は LD_LIBRARY_PATH) に含まれている。settings.sh または settings.csh ファイルを使用して環境設定した場合、sge-root/lib/arch ディレクトリは自動的には追加されません。
N1 Grid Engine 発行ホストであるマシンにログインしている。マシンが N1 Grid Engine 発行ホストでない場合、DRMAA 関数呼び出しはすべて失敗し、「DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE 」が返されます。
デフォルトで有効な DRMAA 共有ライブラリは、version 1.0 の DRMAA C Language Binding Specification をサポートしています。ただし、下位互換性のため、Grid Engine には、version 0.95 の DRMAA C Language Binding Specification の実装も含まれています。すべての新しいアプリケーションは、1.0 の共有ライブラリを使用して開発することを推奨しますが、0.95 の実装を必要とするアプリケーションが見つかることもあります。
version 0.95 の共有ライブラリを有効にする手順は、次のとおりです。
インストールした Grid Engine に対する変更権限を持つユーザーでログインします。
% su - |
sge-root/lib/arch ディレクトリに移動します。
% cd /sge/lib/sol-sparc64 |
libdrmaa.so シンボリックリンクを削除します。
% rm libdrmaa.so |
0.95 ライブラリへの新しいシンボリックリンクを作成します。
% ln -s libdrmaa.so.0.95 libdrmaa.so |
Solaris または Linux プラットフォームの場合、共有ライブラリにはバージョン番号のタグが付いています。version 1.0 向けにコンパイルおよびリンクしたアプリケーションで、version 0.95 の共有ライブラリが有効にされている場合、および version 0.95 向けにコンパイルおよびリンクしたアプリケーションで、version 1.0 の共有ライブラリが有効にされている場合は、どちらもライブラリが見つからないというエラーになります。そのほかのプラットフォームの場合、1.0 アプリケーションは 0.95 共有ライブラリを正しく読み込みますが、未知のシンボルが原因でエラーになることがあります。0.95 アプリケーションは 1.0 共有ライブラリを正しく読み込みますが、DRMAA 関数によって予期しないエラーコードが返されて、エラーになる可能性があります。
ここでは、C 言語バインドを使用したアプリケーション対話例をいくつか紹介します。Grid Engine Community サイトの「Howtos」セクションでも、そのほかの例を紹介しています。
次のコードセグメントは、もっとも基本的な DRMAA C バインドプログラムの例です。
DRMAA 関数を呼び出すたびにエラーコードが返されます。何も問題がなければ、コードは DRMAA_ERRNO_SUCCESS になります。エラーが発生すると、そのエラーに応じたエラーコードが返されます。各 DRMAA 関数は少なくとも 2 つのパラメータを取ります。2 つのパラメータは、エラーの場合にエラーメッセージに埋め込む文字列およびエラー文字列の最大の長さを表す整数です。
例の行 8 では、drmaa_init() を呼び出しています。この関数は DRMAA セッションを設定するもので、ほかの大部分の DRMAA 関数の前に呼び出します。drmaa_get_contact () のように、drmaa_init() の前に呼び出せる関数もありますが、これらの関数は一般的な情報を提供するだけです。drmaa_run_job() や drmaa_wait() のようにアクションを実行する関数は、drmaa_init() の復帰後に呼び出します。drmaa_init() が復帰する前にこのような関数を呼び出すと、「DRMAA_ERRNO_NO_ACTIVE_SESSION」というエラーコードが返されます。
dmraa_init() 関数はセッションを作成し、イベントクライアントリスナースレッドを開始します。このセッションは DRMAA を使用して発行されたジョブを整理するためのもので、スレッドはジョブの状態およびシステム全般に関する最新情報をキューマスターから受け取ります。drmaa_init() が正しく呼び出されたあと、呼び出し元のアプリケーションは、終了する前に drmaa_exit () の呼び出しも行います。終了する前にアプリケーションが drmaa_exit() を呼び出さないと、キューマスターに無効なイベントクライアントハンドルが残り、キューマスターのパフォーマンスが低下することがあります。
プログラムが終了する行 17 では、drmaa_exit() はセッションをクリーンアップして、イベントクライアントリスナースレッドを停止します。ほかの大部分の DRMAA 関数は、drmaa_exit() の前に呼び出します。drmaa_get_contact() のように、drmaa_exit() のあとで呼び出せる関数もありますが、これらの関数は一般的な情報を提供するだけです。drmaa_run_job() あるいは drmaa_wait() のようにアクションを実行する関数は、drmaa_exit() を呼び出す前にに呼び出します。drmaa_exit() を呼び出したあとでこのような関数を呼び出すと、「DRMAA_ERRNO_NO_ACTIVE_SESSION」というエラーコードが返されます。
01: #include 02: #include "drmaa.h" 03: 04: int main(int argc, char **argv) { 05: char error[DRMAA_ERROR_STRING_BUFFER]; 06: int errnum = 0; 07: 08: errnum = drmaa_init(NULL, error, DRMAA_ERROR_STRING_BUFFER); 09: 10: if (errnum != DRMAA_ERRNO_SUCCESS) { 11: fprintf(stderr, "Could not initialize the DRMAA library: %s\n", error); 12: return 1; 13: } 14: 15: printf("DRMAA library was started successfully\n"); 16: 17: errnum = drmaa_exit(error, DRMAA_ERROR_STRING_BUFFER); 18: 19: if (errnum != DRMAA_ERRNO_SUCCESS) { 20: fprintf(stderr, "Could not shut down the DRMAA library: %s\n", error); 21: return 1; 22: } 23: 24: return 0; 25: }
次は、DRMAA C バインドを使用して N1 Grid Engine にジョブを発行するコードセグメントの例です。このプログラムの最初と最後は、例 6–2 と同じです。違いは行 16 〜 59 にあります。行 16 では、DRMAA がジョブテンプレートを割り当てています。ジョブテンプレートは、発行するジョブの情報を格納するための構造です。drmaa_run_job() または drmaa_run_bulk_job() の複数の呼び出しでは、同じテンプレートを再利用できます。
行 22 では、DRMAA_REMOTE_COMMAND 属性を設定しています。この属性は、実行するプログラムがある場所を DRMAA に伝えます。この属性の値は、実行可能ファイルへのパスです。パスは相対または絶対のどちらでもかまいません。相対の場合は、DRMAA_WD 属性を基準にしたパスで、デフォルトではユーザーのホームディレクトリです。DRMAA の属性については、drmaa_attributes のマニュアルページを参照してください。このプログラムが動作するには、sleeper.sh スクリプトがデフォルトパスになければなりません。
行 32 では、DRMAA_V_ARGV 属性を設定しています。この属性は、実行可能ファイルに渡す引数を DRMAA に伝えます。DRMAA の属性については、drmaa_attributes のマニュアルページを参照してください。
行 43 の drmaa_run_job() はジョブを発行します。DRMAA は、ジョブに割り当てられた ID を文字配列に書き込み、この配列が drmaa_run_job() に渡されます。これで、ジョブは qsub によって発行されたかのように動作します。この時点で drmaa_exit() を呼び出すか、プログラムを終了しても、ジョブは何の影響も受けません。
クリーンアップするために、行 54 でジョブテンプレートが削除されます。これによって、ジョブテンプレート用に DRMAA が確保していたメモリーが解放されますが、発行されたジョブは何の影響も受けません。
最後に、行 61 で drmaa_exit() が呼び出されています。drmaa_exit() の呼び出しは、行 18 から始まっていた if 構造の外側にあります。これは、 drmaa_init() が呼び出されたあと、ほかのコマンドが成功したかどうかに関係なく、終了する前に drmaa_exit() を呼び出さなければならないためです。
01: #include 02: #include "drmaa.h" 03: 04: int main(int argc, char **argv) { 05: char error[DRMAA_ERROR_STRING_BUFFER]; 06: int errnum = 0; 07: drmaa_job_template_t *jt = NULL; 08: 09: errnum = drmaa_init(NULL, error, DRMAA_ERROR_STRING_BUFFER); 10: 11: if (errnum != DRMAA_ERRNO_SUCCESS) { 12: fprintf(stderr, "Could not initialize the DRMAA library: %s\n", error); 13: return 1; 14: } 15: 16: errnum = drmaa_allocate_job_template(&jt, error, DRMAA_ERROR_STRING_BUFFER); 17: 18: if (errnum != DRMAA_ERRNO_SUCCESS) { 19: fprintf(stderr, "Could not create job template: %s\n", error); 20: } 21: else { 22: errnum = drmaa_set_attribute(jt, DRMAA_REMOTE_COMMAND, "sleeper.sh", 23: error, DRMAA_ERROR_STRING_BUFFER); 24: 25: if (errnum != DRMAA_ERRNO_SUCCESS) { 26: fprintf(stderr, "Could not set attribute \"%s\": %s\n", 27: DRMAA_REMOTE_COMMAND, error); 28: } 29: else { 30: const char *args[2] = {"5", NULL}; 31: 32: errnum = drmaa_set_vector_attribute(jt, DRMAA_V_ARGV, args, error, 33: DRMAA_ERROR_STRING_BUFFER); 34: } 35: 36: if (errnum != DRMAA_ERRNO_SUCCESS) { 37: fprintf(stderr, "Could not set attribute \"%s\": %s\n", 38: DRMAA_REMOTE_COMMAND, error); 39: } 40: else { 41: char jobid[DRMAA_JOBNAME_BUFFER]; 42: 43: errnum = drmaa_run_job(jobid, DRMAA_JOBNAME_BUFFER, jt, error, 44: DRMAA_ERROR_STRING_BUFFER); 45: 46: if (errnum != DRMAA_ERRNO_SUCCESS) { 47: fprintf(stderr, "Could not submit job: %s\n", error); 48: } 49: else { 50: printf("Your job has been submitted with id %s\n", jobid); 51: } 52: } /* else */ 53: 54: errnum = drmaa_delete_job_template(jt, error, DRMAA_ERROR_STRING_BUFFER); 55: 56: if (errnum != DRMAA_ERRNO_SUCCESS) { 57: fprintf(stderr, "Could not delete job template: %s\n", error); 58: } 59: } /* else */ 60: 61: errnum = drmaa_exit(error, DRMAA_ERROR_STRING_BUFFER); 62: 63: if (errnum != DRMAA_ERRNO_SUCCESS) { 64: fprintf(stderr, "Could not shut down the DRMAA library: %s\n", error); 65: return 1; 66: } 67: 68: return 0; 69: }