この章では、分散メーク (dmake) ユーティリティを使用して、構築を複数のホストに分散し、複数のワークステーションや複数の CPU で同時にプログラムを構築できるようにする方法について説明します。
この章は、以下の節で構成されています。
分散メーク (dmake) を使用すると、多数のプログラムから構成される大規模なプロジェクトを構築する処理を、複数のワークステーション上、またはマルチプロセッサシステムの複数の CPU 上に分散させることができます。dmake ユーティリティは、メークファイルを構文解析し、次のことを行います。
どのターゲットを同時に構築できるか判断します。
ユーザーが設定した複数のホストにそれらのターゲットの構築を分散します。
dmake ユーティリティは、make ユーティリティの一種です。
dmake を理解するためには、次の事柄についての知識が必要です。
構成ファイル (実行時構成ファイルおよび構築サーバー構成ファイル)
dmake ホスト
構築サーバー
dmake ユーティリティは、どの構築サーバーにジョブを分散するか、また各サーバーにどれだけのジョブを分散するかを決定する際に、2 つのファイルを参照します。
dmake ユーティリティは、dmake ホスト上の実行時構成ファイルにおいて、ジョブの分散先に関する情報を参照します。通常、このファイルは dmake ホストのホームディレクトリに .dmakerc という名前で格納されています。このファイルには、構築サーバーおよび各構築サーバーに分散されるジョブ数が記述されています。詳細は、「dmake ホスト」を参照してください。
構築サーバーのファイルシステム内に /etc/opt/SPROdmake/dmake.conf というファイルがあります。このファイルには、各構築サーバーに分散できる、全 dmake ユーザーによる dmake ジョブの最大合計数を指定します。また、すべての dmake ジョブの実行に適用される nice 優先度も指定できます。
詳細は、「構築サーバー」を参照してください。
dmake ユーティリティは、実行時構成ファイルを検索し、ジョブの分散先に関する情報を得ます。通常、このファイルは、.dmakerc という名前で dmake ホストのホームディレクトリに置かれている必要があります。dmake ユーティリティは、実行時構成ファイルの場所を次の順序で検索します。
コマンド行で -c オプションを使って指定されたパス名
DMAKE_RCFILE メークファイルマクロを使って指定されたパス名
DMAKE_RCFILE 環境変数を使って指定されたパス名
$(HOME)/.dmakerc
実行時構成ファイルが見つからなかった場合、dmake ユーティリティは dmake ホストに 2 つのジョブを分散します。
実行時構成ファイルは、ユーザーが編集して、構築サーバーのリストと、各構築サーバーに分散させるジョブ数を設定します。次に .dmakerc ファイルの例を示します。
# My machine. This entry causes dmake to distribute to it. falcon { jobs = 1 } hawk eagle { jobs = 3 } # Manager's machine. She's usually at meetings heron { jobs = 4 } avocet
falcon、hawk、eagle、heron、avocet は、構築サーバーのリストです。
構築サーバーごとに、分散させたいジョブ数を指定できます。デフォルトのジョブ数は 2 です。
"#"の文字で始まる行は、コメントとして解釈されます。
この構築サーバーのリストには、dmake ホストである falcon が含まれています。dmake ホストも構築サーバーとして指定することができます。実行時構成ファイルに 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 オプションの引数として指定されたグループ
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 というファイルがあります。このファイルは、この構築サーバーで同時に実行できる (すべてのユーザーからの) dmake ジョブの最大数を指定するために使用します。また、すべての dmake ジョブの実行に適用される nice 優先度も指定できます。次に /etc/opt/SPROdmake/dmake.conf ファイルの例を示します。このファイルでは、1 つの構築サーバーで実行可能な (すべての dmake ユーザーからの) dmake ジョブの最大数を、 8 に設定しています。
max_jobs: 8 nice_prio: 5
構築サーバーに /etc/opt/SPROdmake/dmake.conf ファイルが存在しない場合、そのサーバーでは、dmake ジョブを実行することはできません。構築サーバーとして使用したい場合は、手動でこのファイルを作成してください。
分散メークを実行するには、標準の make ユーティリティの代わりに、dmake を使用します。dmake を使用する前に、Solaris の make ユーティリティを理解しておく必要があります。make ユーティリティについての詳細は、Solaris の『Programming Utilities Guide』を参照してください。make ユーティリティを使用している場合、ほとんど変更なしで 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 の構築の際に更新されます。一方、構築が並列モードで行われた場合、aux.o が構築される前に prog2 のリンクが開始されるため、正しい結果が得られないことになります。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
並列処理で発生する可能性があるもう一つの問題は、ライブラリを構築するためのデフォルト規則によって、同じファイル (ライブラリ) が同時に変更されることです。不適切な .c.a 規則を使用すると、dmake は各オブジェクトファイルを構築した後、そのオブジェクトファイルをアーカイブします。dmake が 2 つのオブジェクトファイルを並列処理によってアーカイブしたときに同時に更新が行われると、アーカイブファイルが破壊されることになります。この例を次に示します。
.c.a: $(COMPILE.c) -o $% $< $(AR) $(ARFLAGS) $@ $% $(RM) $%
この問題を回避するためには、すべてのオブジェクトファイルの構築が終了した後、それらのオブジェクトファイルをアーカイブする方法があります。適切な接尾辞の規則とそれに対応するライブラリの指定方法を次に示します。
.c.a: $(COMPILE.c) -o $% $< $(COMPILE.c) -o $% $< lib.a: lib.a($(OBJECTS)) $(AR) $(ARFLAGS) $(OBJECTS) $(RM) $(OBJECTS)
ファイルの同時更新の例は、複数のターゲットに対して同一の規則が定義されている場合にも発生します。例として、lex(1) で使用するためにプログラムとヘッダーの両方を構築する yacc(1) というプログラムを次に示します。同一規則によって複数のターゲットを構築する場合、+ を使用してそれらが 1 つのグループであることを指定する必要があります。これは、並列構築を行う場合に特に重要です。
y.tab.c y.tab.h: parser.y $(YACC.y) parser.y
この規則は、次のように 2 つの規則が指定された場合と同等になります。
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 を開始します。このように 2 つの yacc が、同じファイル (y.tab.c および y.tab.h) に書き込みを行うため、これらのファイルは破壊され、正しい結果が得られません。この規則を正しく指定するには、 + を使って 2 つのターゲットが同じ規則により同時に構築されることを示します。正しい規則の例を次に示します。
y.tab.c + y.tab.h: parser.y $(YACC.y) parser.y
1 つのメークファイルの中でファイルの衝突が避けられない場合があります。次の例で、xstr(1) は C プログラムから文字列を抽出することによって文字列を共有しています。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
上記の規則を使用した場合、dmake ユーティリティでは同時に複数のターゲットを構築することはできません。これは、各ターゲットを構築するたびに、同じ x.c ファイルと strings ファイルを書き換えることになり、しかも使用するファイルを変更することはできないためです。このような状況では、特殊ターゲットの .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 を再帰的に呼び出してライブラリを構築することができます。コマンド行で $(MAKE) マクロを指定すると、dmake ユーティリティが入れ子呼び出しを認識できます。
入れ子にしたコマンドが衝突を起こさないとわかっている場合は、.PARALLEL: を使用して、強制的に並列構築が行われるようにすることができます。
メークファイルの中に、並行して実行される入れ子のコマンドが多数含まれている場合、負荷平均化アルゴリズムによって、強制的にローカルマシンへの構築処理が過剰に割り当てられることがあります。これによって、負荷が大きくなったり、スワップ領域が不足するなどの問題を引き起こすことがあります。こうした問題が発生した場合は、入れ子にしたコマンドを逐次的に実行するようにしてください。
dmake ホストで dmake コマンドを実行すると、構築サーバーにジョブが分散されます。また、dmake ホストにもジョブを分散することができます。この場合、dmake ホストも構築サーバーと見なされます。dmake ユーティリティは、(メークファイルをもとにして) 並列構築が可能であると判断したターゲットに基づいて、ジョブを分散します。次の条件を満たしているマシンを、構築サーバーとして使用できます。
dmake ホスト (dmake を実行するために使用しているマシン) からは、パスワードを入力しなくても、rsh を使用して、リモートから構築サーバー上でコマンドを実行できる必要があります。rsh コマンドに関する詳細は、rsh(1) のマニュアルページまたはオペレーティングシステムの AnswerBook を参照してください。rsh コマンドの使用例を次に示します。
demo% rsh <構築サーバー> 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 を設定している場合、dmake は、実行時構成ファイルの検索は行いません。したがって、ジョブの数は、-j オプション、DMAKE_MAX_JOB 変数またはマクロを使用して指定しなければなりません。この方法でジョブ数を指定しない場合、デフォルトのジョブ数の 2 が使用されます。
dmake を分散モード (dmake のデフォルト設定、またはオプション、変数、マクロによる指定) で実行している時に、-j オプション、DMAKE_MAX_JOBS 変数またはマクロを使用してジョブの最大数を変更した場合、指定した値が実行時構成ファイルに設定されている値に上書きされます。ユーザーが指定した値は、分散できるジョブの最大数として、すべての構築サーバーに適用されます。
dmake ジョブの分散は、次の 2 通りの方法で管理できます。
dmake ホストの dmake ユーザーは、構築サーバーとして使用するマシンと、各構築サーバーに分散されるジョブの数を指定できます。
構築サーバーの「所有者」は、その構築サーバーに分散される dmake ジョブの最大数を管理できます。「所有者」とは、/etc/opt/SPROdmake/dmake.conf ファイルを変更できるユーザーを指します。
GUI (「構築」ウィンドウ) から dmake を実行する場合は、オンラインヘルプで構築サーバーとジョブ数の指定方法についての説明を参照してください。コマンド行で dmake を実行する場合は、dmake(1) のマニュアルページを参照してください。