この付録では、分散メーク (dmake) ユーティリティで構築プロセスを複数のホストに分散させ、複数のワークステーションやマルチプロセッサによってプログラムを並列的に開発する方法について説明します。この付録は、次の各節で構成されています。
分散メーク (dmake) を使用することにより、多数のプログラムで構成される大規模なプロジェクトの構築プロセスを複数のワークステーション (マルチプロセッサシステムの場合は複数の CPU) に分散させて、同時に作業を進行させることができます。dmake ユーティリティではメークファイルの構文解析の他に、次の作業を行うことができます。
同時構築の候補となるターゲットを特定する
ユーザー自身が設定した多数のホストに、ターゲットとなる各構築を分散する
dmake ユーティリティは、make ユーティリティの拡張版として提供されています。
dmake の概念を理解するには、次の事項に関する知識が必要になります。
構成ファイル (実行時構成ファイルと構築サーバー構成ファイル)
dmake ホストファイル
構築サーバー
dmake ユーティリティは、実行時構成ファイルと構築サーバー構成ファイルの 2 つを参照して、分散化の候補となる構築サーバーのジョブを特定し、各構築サーバーに分散させるジョブの数を決めます。
dmake ユーティリティは、dmake ホスト上で実行時構成ファイルを探して、各ジョブをどこに分散させるかを判断します。一般に、この構成ファイルは dmake ホストのホームディレクトリに、.dmakerc というファイル名で置かれています。このファイルには、一連の構築サーバーのリストと、各構築サーバーに分散させる各ジョブの数が記述されています。詳細については、「dmake ホスト」を参照してください。
各構築サーバーのファイルシステムには、/etc/opt/SPROdmake/dmake.conf という構築サーバー構成ファイルがあります。このファイルでは、dmake の全ユーザーが各構築サーバーに分散できる dmake ジョブの最大数を指定します。さらに、すべての dmake ジョブを実行する際の優先順位を指定することもできます。
詳細については、「構築サーバー」を参照してください。
dmake ユーティリティは、実行時構成ファイルを探して、各ジョブをどこに分散させるかを判断します。通常、このファイルは dmake ホストのホームディレクトリに、.dmakerc というファイル名で置かれています。dmake ユーティリティは、以下の順序に従って、実行時構成ファイルを探します。
-c オプションで、 コマンド行 (CLI) から指定されたパス名
DMAKE_RCFILE という名のメークファイルマクロにより、CLI から指定されたパス名
DMAKE_RCFILE 環境変数で指定されたパス名
$(HOME)/.dmakerc
実行時構成ファイルが見つからない場合、dmake ユーティリティはデフォルトとして dmake ホストに 2 つのジョブを分散させます。
実行時構成ファイルをユーザー自身が編集して、各構築サーバーのリストと各構築サーバーに分散させるジョブの数を記述することができます。次に、.dmakerc ファイルの例を示します。
# 自分のマシン。この行のため、dmake は自分のマシンへジョブを分散する。 falcon { jobs = 1 } hawk eagle { jobs = 3 } # 上司のマシン。たいてい、会議中。 heron { jobs = 4 } avocet
falcon、hawk、eagle、heron、avocet は構築サーバー
各構築サーバーに分散させるジョブの数を指定
デフォルトのジョブ数は 2 です。
"#" 文字で始まる行はコメントと解釈される
構築サーバー falcon は、同時に dmake ホストにもなっています。dmake ホストを構築サーバーとして指定することもできます。実行時構成ファイルで構築サーバーの指定がないと、dmake ジョブは分散されません。
実行時構成ファイルには、複数の構築サーバーを記述することができます。これにより、状況に応じて構築サーバーを柔軟に切り換えることができます。たとえば、異なるオペレーティングシステムでの構築処理ごとに、複数の異なる構築サーバーを定義することができます。また、専用のソフトウェアがインストールされた複数の異なる構築サーバーを定義することもできます。
次に、一連の構築サーバーを記述した実行時構成ファイルの例を示します。
earth { jobs = 2 } mars { jobs = 3 } group lab1 { host falcon { jobs = 3 } host hawk host eagle { jobs = 3 } } group lab2 { host heron host avocet { jobs = 3 } host stilt { jobs = 2 } } group labs { group lab1 group lab2 } group sunos5.x { group labs host jupiter host venus { jobs = 2 } host pluto { jobs = 3 } }
group 指令によって形式上のグループを指定する
グループの各メンバーは中括弧 ({ }) で区切ります。
グループのメンバーになっている構築サーバーは省略可能な host 指令で指定
あるグループが別のグループのメンバーになっていてもかまわない
個々の構築サーバーは、構築サーバーのグループをリストした実行時構成ファイルにも記述できる
この場合、dmake はこれらの構築サーバーを「無名の」グループのメンバーとして扱います。
dmake ユーティリティは次の順序に従って、各ジョブを分散させます。
-g オプションへの CLI 引数として指定された形式グループ
DMAKE_GROUP という名のメークファイルマクロで指定された形式グループ
DMAKE_GROUP 環境変数で指定された形式グループ
実行時構成ファイルで指定された最初のグループ
dmake ユーティリティを使用すると、構築サーバーごとに異なる実行パスを指定することができます。デフォルトでは、dmake は、論理サーバー内の dmake ホストと同じ論理パス上にある dmake サポートバイナリを検索します。構築サーバーの代替パスは、たとえば次のように、ホスト属性として .dmakerc ファイルに指定することができます。
group lab1 { host falcon{ jobs = 10 , path = "/set/dist/sparc-s2/bin" } host hawk{ path = "/opt/SUNWspro/bin" }
.dmakerc ファイル内のグループ名とホスト名は、二重引用符で囲むことができます。二重引用符で囲むことで、グループ名として使用できる文字列の柔軟性が増し、英字だけでなく、数字も使用できるようになります。たとえば、次に示すように、グループ名が数字で始まる場合は、二重引用符で囲んでください。
group "123_lab" { host "456_hawk"{path = "/opt/SUNWspro/bin" } }
/etc/opt/SPROdmake/dmake.conf 構成ファイルは、構築サーバーのファイルシステム中に置かれています。このファイルを使用すると、1 つの構築サーバー上で同時に実行できる dmake ジョブの最大数 (全ユーザーのジョブの合計数) を制限したり、すべての dmake ジョブを実行する際の優先順位を指定することができます。次に /etc/opt/SPROdmake/dmake.conf ファイルの例を示します。この例では、 (全 dmake ユーザーから) 1 つの構築サーバー上で実行できる dmake の最大数が 8 個に設定されます。
jobs: 8 nice_prio: 5
構築サーバーに /etc/opt/SPROdmake/dmake.conf ファイルがないと、そのサーバー上では dmake ジョブは実行できません。
分散メークを実行するには、標準的な make ユーティリティの代わりに、dmake という実行可能ファイルを使用します。したがって、dmake を使用する前に、Solaris の make ユーティリティについて理解しておく必要があります。make ユーティリティの詳細については、Solaris のマニュアル『プログラミングユーティリティ』を参照してください。これまでに make ユーティリティを使用したことがあるユーザーの場合は、ほとんど変更作業を伴わずに、dmake に移行することができます。
この項で紹介する方法と例では、dmake により解決できる問題を示しています。必ずしもこれらの方法が最善の解決策であるとは限りませんが、できるだけわかりやすく機能が説明されています。
手順が複雑になるに従って、これを実現するメークファイルも複雑になってきます。したがって、メークファイルを効果的に機能させる上で必要なアプローチもよく理解しておく必要があります。この項では一般的なコード開発の作業を進める上での問題点を取り上げ、dmake による簡単な解決方法について、例を紹介しながら説明します。
プロジェクトの最初の段階からメークファイルのテンプレートを使用しておけば、プロジェクトの進行に応じて、このテンプレートをもとにメークファイルをカスタマイズできます。この方法には、次の利点があります。
内容についてより深く精通できる
理解しやすくなる
統合しやすくなる
保守しやすくなる
再利用しやすくなる
メークファイルの編集作業が簡便化するにしたがって、プログラムやプロジェクト本来の開発作業に専念できるようになります。
大規模なソフトウェア開発プロジェクトは、それぞれ独立した多数のモジュールで構成されるのが一般的です。これらのモジュールは、それぞれ分散させて並列的に構築することができます。dmake ユーティリティでは、ネットワークを介して分散した複数のマシン上で、ターゲットの並列構築を行うことができます。この並列構築により、大規模な開発プロジェクトを構築するのに必要な時間が大幅に省けます。
dmake では、構築の対象となるターゲットを中心に、このターゲットに関連する依存関係を調べ、同期の取れていない依存部分の構築を行います。依存関係の構築とは、依存関係の中に含まれている部分を構築することでもあります。dmake はジョブを分散させた後、対象となるすべてのターゲットを起動します。構築の対象となるすべてのターゲットが終了すると、dmake は構築の対象以外のターゲットを起動します。デフォルトの場合、入れ子構造になった dmake の呼び出しは同時には実行されません。ただし、この設定は変更可能です (詳細については、「並列性を制限する 」を参照してください)。
dmake では複数のターゲットが同時並行で構築されるので、各構築プロセスの出力も同時に生成されます。各コマンドからの出力が混乱しないように、dmake では各構築の出力を個別に収集します。また dmake は、各コマンドを実行する前に、そのコマンドの内容を表示します。実行したコマンドが出力、警告、またはエラーを生成すると、dmake ユーティリティは該当するコマンドの出力全体を表示します。後から開始したコマンドが先行するコマンドよりも先に終了することもあるので、出力の順序が予想と異なる場合があります。
複数のターゲットを同時に構築する場合に、メークファイルには多少の制限が伴います。たとえば、暗黙の順序に依存しているメークファイルは、並列構築を行うと正しく動作しない可能性があります。また同じファイルの修正を行うターゲットがあり、これらファイルが 2 つの異なるターゲットで同時に変更されると、メークファイル中のターゲットが誤動作する場合があります。この項では、こうした問題を取り上げて説明します。
ターゲットの並列構築を行う場合、まず依存関係リストが正確であることが重要なポイントになります。たとえば、 2 つの実行可能ファイルが同じオブジェクトファイルを共有しているときに、一方のファイルでしか依存関係が指定されていないと、並列構築を行なった場合はエラーになる可能性があります。次のメークファイルの断片を考えてみます。
all: prog1 prog2 prog1: prog1.o aux.o $(LINK.c) prog1.o aux.o -o prog1 prog2: prog2.o $(LINK.c) prog2.o aux.o -o prog2
直列的に構築を実行する場合は、ターゲット aux.o は prog1 の依存対象として作成されるので、prog2 の構築時には更新されています。一方、並列的に構築を実行すると、prog2 のリンクが、aux.o の作成前に設定される可能性があります。この場合は、エラーになります。make の .KEEP_STATE 機能で検出できる依存関係も一部ありますが、上のような関係はその対象にはなりません。
次に、暗黙の順序にもとづく依存関係がある場合は、問題がさらに複雑になるという例を示します。たとえば、あるシステムのヘッダーをすべて作成してから構築を開始する場合は、すべての要素がこの構築に依存していなければなりません。これに伴いメークファイルがさらに複雑になり、新しいターゲットをメークファイルに追加した場合などにエラーが起きる可能性が高くなります。この場合は、メークファイルの中で特殊な .WAIT ターゲットを指定して、暗黙の依存関係があることを明示することができます。dmake により依存関係リストの中に .WAIT ターゲットが検出されると、これまでの依存関係の処理を一時停止してから、以降の依存関係の処理を実行するようになります。 1 つの依存関係リストに、複数の .WAIT ターゲットを入れることもできます。次に、.WAIT を使用して、まずヘッダーを作成してから、以降の処理を続行する例を示します。
all: hdrs .WAIT libs functions
.WAIT ターゲットに適用する空の規則をメークファイルに追加すれば、旧バージョンのメークファイルとの互換性も保持されます。
ターゲットの構築プロセスでは、同時に同じファイルに対して修正作業を行わないように注意する必要があります。この問題は、さまざまな形式で起きる可能性があります。一時ファイルを使用する新しい接尾辞の規則を定義する場合、一時ファイルの名前は、ターゲットごとに異なっていなければなりません。この場合は、$@ や $* の動的マクロを利用できます。たとえば、ソースファイルをコンパイルする前に .c ファイルに多少の修正を加える場合、.c.o の規則は次のように定義できます。
.c.o: awk -f modify.awk $*.c > $*.mod.c $(COMPILE.c) $*.mod.c -o $*.o $(RM) $*.mod.c
並列処理に伴うもう 1 つの問題は、ライブラリを作成するときのデフォルトの規則です。すなわち、ライブラリという固定ファイルが変更される場合があるということです。本来適用されるべきでない .c.a 規則によって、dmake がそれぞれのオブジェクトファイルを生成し、このオブジェクトファイルをアーカイブする可能性があります。dmake が 2 つのオブジェクトファイルを同時にアーカイブすると、並列的に更新が行われてアーカイブファイルが壊れることがあります。
.c.a: $(COMPILE.c) -o $% $< $(AR) $(ARFLAGS) $@ $% $(RM) $%
この場合は、各オブジェクトファイルを作成して、構築が完了した後、すべてのオブジェクトファイルをアーカイブするようにします。正しい接尾辞の規則と対応するライブラリの規則は次のとおりです。
.c.a: $(COMPILE.c) -o $% $< lib.a: lib.a($(OBJECTS)) $(AR) $(ARFLAGS) $(OBJECTS) $(RM) $(OBJECTS)
ファイルの同時更新の問題は、同じ規則を複数のターゲットに対して定義した場合にも起きます。たとえば、lex(1) を使用してプログラムとヘッダーの両方を作成する yacc(1) プログラムがあります。1 つの規則にもとづいて複数のターゲットファイルを作成する場合は、+ 記号を使用して各ファイルを 1 つのグループとして指定する必要があります。特に、並列構築の場合に、これが重要になります。
y.tab.c y.tab.h: parser.y $(YACC.y) parser.y
y.tab.c: parser.y $(YACC.y) parser.y y.tab.h: parser.y $(YACC.y) parser.y
直列の make ではまず、y.tab.c を生成する最初の規則を使用して構築を行い、次に y.tab.h がすでに最新版になっているため構築の必要がないと判断します。一方、並列構築の場合、dmake は yacc が y.tab.c の構築を完了する前に y.tab.h を検索し、y.tab.h が構築の必要があることを知り、最初の yacc を並行して別の yacc を開始します。いずれの yacc の呼び出しも同じファイル (y.tab.c と y.tab.h) に書き込まれるので、これらのファイルの内容が不当になる可能性があります。正しい方法は、+ 構文を使用して、両方のターゲットを同じ規則に従って同時に作成するように指示することです。たとえば次のとおりです。
y.tab.c + y.tab.h: parser.y $(YACC.y) parser.y
メークファイルでファイルの衝突が避けられない場合があります。たとえば、C プログラムから文字列を抽出して、共有文字列を生成する xstr(1) コマンドがあります。xstr コマンドでは変更された C プログラムは x.c という固定したファイルに書き込まれ、抽出した文字列は strings という固定ファイルに追加されます。xstr は個々の C ファイルを対象として実行するので、次の .c.o 規則がよく使用されます。
.c.o: $(CC) $(CPPFLAGS) -E $*.c | xstr -c - $(CC) $(CFLAGS) $(TARGET_ARCH) -c x.c mv x.o $*.o
この場合、各ターゲットを作成するごとに、x.c と strings という同じファイルに書き込みを行うので、dmake ユーティリティは上のルールにもとづいてターゲットを同時に作成することはできません。また使用するファイルを変更することもできません。この場合は、.NO_PARALLEL: という特殊なターゲットを使用して、dmake に各ターゲットを同時には作成しないように指示する必要があります。たとえば、.c.o 規則を使用して作成するオブジェクトを OBJECTS マクロで定義すると、次の指定により dmake は各ターゲットを直列形式で作成するようになります。
.NO_PARALLEL: $(OBJECTS)
大部分のオブジェクトを直列的に作成する場合は、依存関係のない .NO_PARALLEL: ターゲットを入れて、全オブジェクトをデフォルトとして強制的に直列処理するように設定するのが、安全で簡単な方法です。並列的に処理できるターゲットは、次のように .PARALLEL: ターゲットの依存関係として記述できます。
.NO_PARALLEL: .PARALLEL: $(LIB_OBJECT)
dmake が、さらに別の dmake コマンドを呼び出すターゲットを検出すると、並列ではなく、直列形式でターゲットが作成されます。これにより、 2 つの異なる dmake の呼び出しが、同じディレクトリ中の同じターゲットを作成することはなくなります。このような問題は、 2 つの異なるプログラムを同時に作成して、両方が同じライブラリにアクセスする場合などに起きます。それぞれの dmake の呼び出しで、必ず最新のライブラリを適用するには、dmake を繰り返し呼び出して、このライブラリを作成する必要があります。dmake ユーティリティは、CLI で $(MAKE) マクロが使用されている場合に、入れ子になった呼び出しを認識します。
入れ子のコマンドを使用した場合でも、必ず正しい結果が得られる確信があれば、.PARALLEL: 構文を使用して、並列的に処理することができます。
メークファイルの中に、並列的に実行する入れ子のコマンドが多数含まれている場合は、負荷平均化アルゴリズムのために、ローカルのマシンに割り当てられる構築ジョブが超過する場合があります。この結果、スワップ空間の不足といった別の問題が生じたり、処理負荷が高くなったりすることも考えられます。こうした問題が起きた場合は、入れ子にしたコマンドをすべて直列形式で実行するように変更してください。
dmake ホストで dmake を実行することにより、各ジョブを構築サーバーに分散させることができます。また dmake ホストにジョブを分散させることもできます。この場合は、dmake ホストも構築サーバーの 1 つとみなされます。dmake ユーティリティは、同時構築可能と判断されるメークファイルターゲットの各ジョブを (ユーザーのメークファイルにもとづいて) 分散させます。次の条件を満たしているマシンであれば、構築サーバーとして使用できます。
dmake ホスト (ユーザーが使用しているマシン) から、パスワードなしで rsh を使用して、構築サーバー上でコマンドをリモートに実行できる
rsh コマンドの詳細については、rsh(1) のマニュアルページや、システムの AnswerBook を参照してください。たとえば、次のようにコマンドが実行可能である必要があります。
demo% rsh build_server which dmake /opt/SUNWspro/bin/dmake
dmake がインストールされている bin ディレクトリが、構築サーバーからアクセスできる
共有ファイルシステムの作成に関する詳細については、share(1M) と mount(1M) のマニュアルページや、システムの AnswerBook を参照してください。
デフォルトで、dmake が、構築サーバー上の dmake 実行可能ファイルの論理パスは dmake ホストと同じであるとみなしている
次に示すように、実行時構成ファイルにホストエントリの属性としてパス名を指定 することで、論理パスを変更することができます。
group sparc-cluster { host wren { jobs = 10 , path = "/export/SUNWspro/bin"} host stimpy { path = "/opt/SUNWspro/bin" }
dmake ホストからは、使用する構築サーバーや、各構築サーバーに割り当てる dmake ジョブの数を制御することができます。特定の構築サーバーに割り当てられる dmake ジョブの数を、そのサーバーだけに限定することもできます。
-m オプションに parallel を指定した場合、または DMAKE_MODE 変数の値やマクロを parallel と設定した場合には、実行時構成ファイルの走査は行われません。したがって、-j オプションまたは DMAKE_MAX_JOBS 変数またはマクロを使用して、ジョブの数を指定する必要があります。この方法で値を指定しない場合は、デフォルトとして 2 個のジョブが使用されます。
分散モードで、dmake を使用して DMAKE_MAX_JOBS 変数やマクロを変更する場合や、-j オプションで、最大ジョブ数を変更する場合は、実行時構成ファイルに記述された値が、新たに指定された値に置き換えられます。ここで指定した値が、全構築サーバーに分散させるジョブの合計数として使用されます。
dmake ジョブの分散は、次の 2 つの方法で制御することができます。
dmake ホスト上の dmake ユーザーは、構築サーバーとして使用するマシンを指定できます。また各構築サーバーに分散させるジョブの数を制御できます。
構築サーバーの「所有者」は、この構築サーバーで分散できる dmake ジョブの最大合計数を制御できます。所有者とは、/etc/opt/SPROdmake/dmake. conf ファイルを変更する権限を持つユーザーのことです。
GUI (「構築」ウィンドウ) を使用して dmake にアクセスしている場合は、オンラインヘルプを使用して、構築サーバーやジョブの指定方法を確認できます。一方、コマンド行 (CLI) を使用して dmake にアクセスしている場合は、dmake のマニュアルページ (dmake.1) を参照してください。