この章では,アプリケーションで VAXのページ・サイズに依存している部分を識別する方法を説明し, これらの問題に対する対処方法を示します。
ページ・サイズは,オペレーティング・システムが操作するメモリの基本単位であり, 一般にアプリケーションのレベルでは意識する必要はありません。特に, 高級プログラミング言語や中級プログラミング言語で作成されたアプリケーションの場合には, ページ・サイズを直接操作することはほとんどありません。しかし, アプリケーションでシステム・サービスやランタイム・ライブラリ・ルーチンを呼び出し, 次のようなメモリ管理機能を実行する場合には, ページ・サイズに依存する部分がアプリケーションに含まれている可能性があります。
これらを実行するシステム・サービスやランタイム・ライブラリ・ルーチンは, メモリをページ単位で操作します。 これらのルーチンに対する引数として値を指定する場合は,1ページが 512バイトであるものと仮定しています。これは VAXアーキテクチャで定義されているページ・サイズです。Alphaアーキテクチャでは, 8Kバイト,16Kバイト,32Kバイト,または 64Kバイトのページ・サイズをサポートします。したがって, ルーチンへの引数として指定する値を調べ, それらの値がアプリケーションの必要条件を満足するかどうかを確認しなければなりません。 この後の節では,これらのルーチンを調べる方法について詳しく説明します。
このようなページ・サイズの違いは,
上位レベル・ルーチンを使用するメモリ割り当てには影響を与えません。たとえば,
Cの malloc
や free
ルーチンなど,
言語固有のメモリ割り当てルーチンや,
仮想メモリ領域を操作するランタイム・ライブラリ・ルーチンなどは,
ページ・サイズの違いの影響を受けません。
システム・サービスやランタイム・ライブラリ・ルーチンは, Alphaシステムにおいてもできる限り VAXシステムと同じインターフェイスや戻り値を維持しています。たとえば Alphaシステムでは, 引数としてページ・カウント値を受け付けるルーチンのこれらの引数を ページレットと呼ぶ512バイトの量として解釈することにより, CPU固有のページ・サイズと区別します。 各ルーチンはページレットの値をCPU固有のページに変換します。 ページ・カウント値を戻すルーチンは, CPU固有のページからページレットに変換することにより, アプリケーションで期待される戻り値が512バイト単位で表現されるようにします。
- 注意
- Alphaシステムでは,$CRMPSCシステム・サービスを使用して(さらに SEC$M_PFNMAPフラグ・ビットをセットして) ページ・フレーム・セクションを作成する場合には, ページ・カウント引数(pagcnt)に指定された値は CPU固有のページ・サイズとして解釈され, ページレットの値としては解釈されません。
互換性があるにもかかわらず,一部のルーチンのAlphaシステムでの動作は VAXシステムでの動作と異なっており, ソース・コードを変更しなければならない可能性があります。たとえば Alphaシステムでは,セクション・ファイルをマッピングするシステム・サービス ($CRMPSCと$MGBLSC)は, CPU固有のページ境界にアラインされたアドレス値を引数として指定しなければなりません。 VAXシステムでは,これらのルーチンは引数のアドレス値を VAXページ境界になるように調整します。Alphaシステムでは, これらのアドレスはCPU固有のページ境界には調整されません。
表 5-1は, ページ・サイズに依存する部分を含んでいる可能性のあるメモリ管理ルーチンと, それらのルーチンがサポートする引数を示しています。この表には,各引数の機能と, これらの引数がルーチンのOpenVMS Alphaバージョンでどのように解釈されるかが示されています。 この表には,ルーチンが受け付けるすべての引数が示されているわけではありません。 ルーチンとその引数リストについての詳しい説明は, 『OpenVMS System Services Reference Manual』を参照してください。
表 5-2に示したランタイム・ライブラリ・ルーチン は,メモリ・ページを割り当てるか,または解放します。互換性を維持する ために,これらのルーチンでは,ユーザが指定したページ・カウント情報を ページレットの値として解釈します。
ルーチン | 引き数 | Alphaシステムでの解釈 |
---|---|---|
LIB$GET_VM_PAGE | number-of-pages 割り当てる連続ページのページ数を指定する。 |
ページレット単位の値として解釈され,CPU固有のページに切り上げられるか, または切り捨てられる。 |
LIB$FREE_VM_PAGE | number-of-pages 割り当てを解除する連続ページのページ数を指定する。 |
ページレット単位の値として解釈され, CPU固有のページになるように切り上げられるか,または切り捨てられる。 |
アプリケーションが実行するメモリ割り当てを変更しなければならないかどうかを判断するには, メモリがどこで割り当てられるかを確認しなければなりません。 メモリ割り当てを実行するシステム・サービス・ルーチン ($EXPREGと$CRETVA)を使用すれば,次の 2種類の方法でメモリを割り当てることができます。
Alphaアーキテクチャでは, VAXアーキテクチャと同じ仮想アドレス空間レイアウトを定義しており, VAXシステムの場合と同じ方向にP0領域とP1領域を拡大できます。 図 5-1はこのレイアウトを示しています。
アプリケーションで$EXPREGシステム・サービスを使用して 仮想アドレス空間を拡張することによりメモリを割り当てる場合には, ソース・コードを変更する必要はありません。これは, VAXシステムで引数として指定した値がAlphaシステムでも正しく動作するからです。 この理由は次のとおりです。
ただし,システム・サービスはページ・カウントをCPU固有のページに切り上げるため, アプリケーションに対してシステムが実際に割り当てるメモリ・サイズは, VAXシステムの場合よりAlphaシステムの場合の方が大きくなる可能性があります。 割り当てられたメモリ全体はアプリケーションで使用できます。 アプリケーションは通常,必要なバッファを確保するためにメモリを割り当てます。 しかし,バッファのサイズは各プラットフォームで変化しないため, 指定した値はアプリケーションの必要条件を満足できます。
アプリケーションを変更する必要はありません。しかし, $EXPREGシステム・サービスが戻すメモリ・サイズは, Alphaアーキテクチャを実現した各システムで異なる可能性があるため, システムが割り当てた正確なメモリ境界を確認しておくことが適切でしょう。 正確なメモリ境界を確認するには, $EXPREGシステム・サービスに対して省略可能な引数である retadr 引数を指定します(アプリケーションでこの引数がまだ指定されていない場合)。 retadr 引数には, システム・サービスが割り当てたメモリの先頭アドレスと末尾アドレスが格納されます。
たとえば,例 5-1のプログラムは, $EXPREGシステム・サービスを呼び出すことにより10ページの追加メモリを要求します。 このプログラムをVAXシステムで実行した場合には,$EXPREGシステム・サービスは 5120バイトの追加メモリを割り当てます。このプログラムを Alphaシステムで実行した場合には,$EXPREGシステム・サービスは少なくとも 8192バイトを割り当てます。また, Alphaアーキテクチャの特定の動作のページ・サイズによっては, それ以上のサイズのメモリを割り当てることもあります。
#include <ssdef.h> #include <stdio.h> #include <stsdef.h> #include <descrip.h> #include <dvidef.h> #define PAGE_COUNT 10 【1】 #define P0_SPACE 0 #define P1_SPACE 1 main( argc, argv ) int argc; char *argv[]; { int status = 0; long bytes_allocated, addr_returned[2]; 【2】 status = SYS$EXPREG( PAGE_COUNT, &addr_returned, 0, P0_SPACE); bytes_allocated = addr_returned[1] - addr_returned[0]; if( status == SS$_NORMAL) printf("bytes allocated = %d\n", bytes_allocated ); else return (status); }この後の各項目は,例 5-1に示した番号に対応します。
【1】 | この例では,要求するページ数を意味するシンボルとして PAGE_COUNTを定義しています。 |
【2】 | この例では,仮想アドレス空間のP0領域の末尾に 10ページを追加することを要求しています。 |
アプリケーションで$CRETVAシステム・サービスを使用することにより, 仮想アドレス空間内のメモリを再割り当てする場合には,$CRETVAに対する引数のうち, 次の引数の値を変更する必要があるかもしれません。
アプリケーションを変更しなければならないかどうかを判断するには, 次の操作を実行してください。
#include <ssdef.h> #include <stdio.h> #include <stsdef.h> #include <descrip.h> #include <dvidef.h> char _align(page) buffer[1024]; main( argc, argv ) int argc; char *argv[]; { int status = 0; long inadr[2]; long retadr[2]; inadr[0] = &buffer[0]; inadr[1] = &buffer[1023]; printf("inadr[0]=%u,inadr[1]=%u\n",inadr[0],inadr[1]); status = SYS$CRETVA(inadr, &retadr, 0); if( status & STS$M_SUCCESS ) { printf("success\n"); printf("retadr[0]=%u,retadr[1]=%u\n",retadr[0],retadr[1]); } else { printf("failure\n"); exit(status); } }
$EXPREGシステム・サービスと $CRETVAシステム・サービスによって割り当てたメモリを解除するために $DELTVAシステム・サービスを呼び出す場合,$DELTVAシステム・サービスに対して inadr 引数として,retadr 引数に戻されたアドレス範囲 (メモリを割り当てるために使用したルーチンから戻された値) をアプリケーションで使用しているときは, アプリケーションを変更する必要はありません。 実際に割り当てられるサイズは各システムで異なるため, 割り当ての範囲に関してアプリケーションで何らかの仮定を設定することは望ましくありません。
アプリケーションで実行するメモリ・マッピングを変更しなければならないかどうかを判断するには, アプリケーションが仮想メモリのどの部分でマッピングを実行するかを確認しなければなりません。 メモリ・マッピング・システム・サービス($CRMPSCと$MGBLSC)を使用すれば, 次の方法でメモリをマッピングできます。
アプリケーションがセクションをマッピングする方法は, おもに$CRMPSCシステム・サービスと $MGBLSCシステム・サービスに対する次の引数によって決定されます。
$CRMPSCシステム・サービスと$MGBLSCシステム・サービスは少なくともCPU固有のページを 1ページ分マッピングします。セクション・ファイルが1ページ未満の場合には, ページの残りの部分には0が格納されます。 ページの残された空間をアプリケーションで使用すべきではありません。なぜなら, セクション・ファイルに格納できるデータだけがディスクに書き戻されるからです。
アプリケーションでアプリケーションの仮想アドレス空間の拡張領域にセクション・ ファイルをマッピングする場合には,ソース・コードを変更する必要はありません。 これは,拡張された仮想アドレス空間にマッピングされるため,たとえ Alphaシステムで割り当てられるメモリのサイズがVAXシステムより大きくても, 既存のデータの上にマッピングされる危険性がないからです。このように, VAXシステムで$CRMPSCシステム・サービスに対して引数として指定した値は, Alphaシステムでも正しく機能します。
セクションを仮想メモリの拡張領域にマッピングするアプリケーションは, 変更しなくても正しく動作できますが, retadr 引数をアプリケーションで指定していない場合には, この引数を指定することにより, 呼び出しによってマッピングされたメモリの正確な境界を判断するようにしてください。
例 5-3は,セクション・ファイルを拡張アドレス 空間にマッピングする$CRMPSCシステム・サービスの呼び出しを示しています。 この例では,次に示すようにDCLのCREATEコマンドを使用して作成された MAPTEST.DATという名前のセクション・ファイルをマッピングします。
- 注意
- アプリケーションで relpag 引数を指定する場合には, retadr 引数も指定しなければなりません。これは省略可能な引数ではありません。 relpag 引数の使用についての詳しい説明は, 第5.3.4項を参照してください。
$ CREATE maptest.dat test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data test data [Ctrl/Z]
アプリケーションでセクション・ファイルを 1ページのメモリにマッピングする場合には,Alphaシステムの$CRMPSCおよび $MGBLSCシステム・サービスでこの操作モードがサポートされないため, ソース・コードを変更しなければなりません。Alphaシステムのページ・サイズは VAXシステムのページ・サイズと異なっており,さらに Alphaアーキテクチャの各実装ごとに異なるため, アプリケーションでセクション・ファイルをマッピングするために正確なメモリ境界を指定しなければなりません。 このような使い方をした場合,$CRMPSCシステム・サービスは, 引数が誤っていることを示すエラー(SS$_INVARG)を戻します。
アプリケーションでこのモードを使用しているかどうかを判断するには, inadr 引数に指定した先頭アドレスと末尾アドレスを確認します。 両方のアドレスが同じであり,同時に,flags 引数の SEC$M_EXPREGビットがセットされていない場合には, アプリケーションはこのモードを使用しています。
このモードの$CRMPSCシステム・サービスの呼び出しを変更する場合には, 次のガイドラインに従ってください。
アプリケーションで仮想アドレス空間の定義された領域にセクションをマッピングする場合には, ソース・コードを変更しなければならない可能性があります。これは, Alphaシステムでは$CRMPSCおよび$MGBLSCシステム・サービスが VAXシステムと異なる方法で一部の引数を解釈するからです。相違点は次のとおりです。
可能な場合には,拡張された仮想アドレス空間にデータがマッピングされるように, アプリケーションを変更してください。 アプリケーションがデータをマッピングする方法を変更できない場合には, 次のガイドラインに従ってください。
セクションがマップされるときに, 隣接データが重ね書きされないことを確認するためのよりよい方法は, リンカにバッファを独立したイメージ・セクションとして指定することです (リンカはイメージをイメージ・セクションから作成します。 それぞれのイメージ・セクションは, イメージの各部分のメモリの必要量を定義しています)。 リンカはイメージ・セクションをページ境界に配置し, 隣接するデータはつぎのページ境界から配置します。 このためバッファを独自のイメージ・セクションに独立されることによって, 隣接データがマッピング操作によって重ね書きされないことが保証されます。 このように,隣接するデータを破壊したり,バッファのサイズを変更することなく 1ページ分のメモリをセクションにマップすることが可能です。
リンカがセクション・ファイルを独自のイメージ・セクションに置いたことを確認するために, リンカのPSECT_ATTRオプションを使用して SOLITARYプログラム・セクション属性を設定する必要があります(詳しくは 『OpenVMS Linker Utility Manual』を参照してください)。また, 使用している高級または中級のプログラミング言語を, コンパイラが定義したバッファを別のプログラム・セクションに置くことを確認するために, 使用する必要があるかもしれません。 詳しくは,コンパイラの解説書を参照してください。
セクションを独自のイメージ・セクションに分離する場合には, SOLITARYプログラム・セクション属性を使用して, 先頭アドレスはページ境界にアラインされます。これは, 実行時のホスト・マシンのページ・サイズにかかわらず, リンカが省略時の設定によりイメージ・セクションをページ境界にアラインするからです。
セクションの末尾アドレスが CPU固有のページ境界にアラインされたことを確認するためには, アプリケーションを実行しているマシンがサポートしているページ・サイズを知る必要があります。 $GETSYIシステム・サービスやLIB$GETSYIランタイム・ライブラリ・ルーチンを呼ぶことにより, 実行時にCPU固有のページ・サイズを得ることができます。また, 得た値を使ってアラインされた末尾アドレスの値を計算し, inadr引数内でシステム・サービスに渡すこともできます。
システムがマップした使用可能なメモリ量を判断するためには, retadr 引数を指定してください。 たとえアプリケーションがページの一部分しか使用しないとしても, オペレーティング・システムは最低でも1ページをマップします。 retadr 引数で指定された末尾アドレスは使用できるメモリの上限を示します (Alphaシステムでアプリケーションが$CRMPSCシステム・サービスに relpag 引数を指定する場合,必ず retadr 引数を指定しなければなりません)。
たとえば,例 5-4に示すVAXプログラムは,
第5.3.1項で作成したセクション・ファイルを既存の仮想アドレス空間にマッピングします。
アプリケーションは
buffer
という名前のバッファを定義します。このバッファのサイズは
512バイトであり,これはVAXのページ・サイズを反映しています。
プログラムはバッファの1バイト目のアドレスを先頭アドレスとして,また,
バッファの最終バイトのアドレスを末尾アドレスとして
inadr 引数に渡すことにより,セクションの正確な境界を定義します。
これらの目標を達成するための1つの方法として, SOLITARYプログラム・セクション属性を使用することにより, セクション・データを格納したプログラム・セクションを独自のイメージ・セクションに分離する方法があります。
この例では,buffer
という名前のセクションは
bufferという名前のプログラム・セクション内に示されています
(プログラム・セクションの生成方法は,
各プラットフォーム上の言語の種類により異なります。
セクションが独自のプログラム・セクションにあることをコンパイラの解説書で確認してください)。
次のリンク操作は,このプログラム・セクションの
SOLITARY属性を設定する方法を示しています。
$ LINK MAPTEST, SYS$INPUT/OPT PSECT_ATTR=BUFFER,SOLITARY [Ctrl/Z]CPU固有のページ境界の末尾にアラインされる末尾アドレスをセクション・バッファに対して指定するには, 実行時にCPU固有のページ・サイズを入手し, その値から1を減算し,その値を使用して配列の最終要素のアドレスを求めます。 この値をinadr引数の2番目のロングワードとして渡します (実行時にページ・サイズを判断する方法については, 第5.4節を参照してください)。 セクションがマッピングされるバッファの割り当てを変更する必要はありません。
アプリケーションが任意のページ・サイズの Alphaシステムで正しく実行されるようにするには, /BPAGE=16修飾子を指定することにより,リンカがイメージ・セクションを 64KBの境界に強制的にアラインするようにします。 実際にマッピングされるメモリの総量は, 使用可能なメモリの合計よりはるかに大きくなる可能性があります。 使用可能なメモリのサイズは,ページ・カウント ( pagcnt )引数の値とセクション・ファイルのサイズのうち, どちらか小さい方の値によって決定されます。 セクションの範囲内に含まれないメモリを使用しないようにするには, retadr 引数に戻された値を使用します。
例 5-5は,Alphaシステムで正しく実行するために 例 5-4に対して必要なソースの変更を示しています。
次のリストの各項目は, 例 5-5の番号に対応しています。
【1】 | ヘッダ・ファイルSYIDEF.Hには, $GETSYIシステム・サービスに対する OpenVMSアイテム・コードの定義が登録されています。 |
【2】 | バッファは _align(page)ストレージ記述子を使用せずに定義されています。 ページ・サイズはOpenVMS Alphaシステムで実行するまで判断できないため, DEC C for OpenVMS Alphaコンパイラは,_align(page)が指定されているときに, データをAlphaの最大ページ・サイズ(64KB)にアラインします。 |
【3】 | この構造は, 実行時にページ・サイズを入手するために使用される項目リストを定義します。 |
【4】 | この変数には, 戻されたページ・サイズ値が格納されます。 |
【5】 | $GETSYIシステム・サービスに対するこの呼び出しでは, 実行時にページ・サイズが入手されます。 |
【6】 | バッファの末尾アドレスは, 戻されたページ・サイズ値から1を減算することにより指定されます。 |
アプリケーションではセクション・ファイルの一部だけをマッピングできます。 その場合には, マッピングを開始するアドレスをセクション・ファイルの先頭からのオフセットとして指定します。 このオフセットを指定するには, $CRMPSCシステム・サービスの relpag 引数に対して値を指定します。 relpag 引数の値は, ファイルの先頭を基準にしてマッピングを開始するページ番号を指定します。
$CRMPSCシステム・サービスは互換性を維持するために,VAXシステムと Alphaシステムの両方のシステムにおいて,relpag 引数の値を 512バイト単位で解釈します。しかし,AlphaシステムのCPU固有のページ・サイズは 512バイトより大きいため,relpag 引数にオフセットとして指定する値はおそらく CPU固有のページ境界にアラインされません。$CRMPSCシステム・サービスは仮想メモリを CPU固有のページ単位でのみマッピングできます。したがって, Alphaシステムでは,セクション・ファイルのマッピングはオフセット・アドレスを含む CPU固有のページの先頭から開始され, オフセットによって指定されるアドレスから正確に開始されるわけではありません。
アプリケーションでオフセットからセクション・ファイルにマッピングする場合には, Alphaシステムでマッピングされる余分な仮想メモリ空間を格納できるように, inadr 引数に指定されるアドレス範囲のサイズを拡大する必要があります。 指定されるアドレス範囲が小さすぎる場合には, アプリケーションはセクション・ファイルの中で必要な部分全体をマッピングできない可能性があります。 これは,マッピングがセクション・ファイルの先頭アドレスから開始されるからです。
- 注意
- ルーチンは,オフセットによって指定されるアドレスを含む CPU固有のページの先頭からマッピングを開始しますが, retadr 引数に戻される先頭アドレスはオフセットによって指定されたアドレスであり, 実際にマッピングが開始されたアドレスではありません。
たとえば,VAXシステムでセクション・ファイルをマッピングするときに, ブロック番号15から始まる16ブロックをマッピングする場合には, アドレス範囲として16*512バイトのサイズを inadr 引数に指定し, relpag 引数に対して15を指定できます。これと同じマッピングを Alphaシステムで実行するには,ページ・サイズの違いを考慮しなければなりません。 たとえば,8Kバイト・ページ・サイズのAlphaシステムでは, relpag オフセットによって指定されるアドレスは, 図 5-2に示すように,15ページレットを CPU固有の1ページに格納できます。Alphaシステムでは, $CRMPSCシステム・サービスはセクション・ファイルのマッピングを CPU固有のページ境界から開始するため,16番目から 30番目までのブロックを正しくマッピングできません。 マッピングを正しく実行するには,Alphaシステムで$CRMPSCシステム・サービス (または$MGBLSCシステム・サービス)がマッピングする追加の 15ページレットを格納できるようにアドレス範囲のサイズを拡大しなければなりません。 このようにサイズを拡大しなかった場合には,指定したセクション・ファイルの中で 1ブロックだけしかマッピングされません。 図 5-2はこの状況を示しています。
(number_of_pagelets_to_map + (2*pagelets_per_page) - 2) pagelets_per_pageたとえば,この公式を使用すれば, 前の例に指定したアドレス範囲をどれだけ拡大すればよいかを計算できます。 次の式では,ページ・サイズは8Kであると仮定しています。したがって, pagelets_per_page は16になります。
16+((2x16)-2)/16=2.87...結果をもっとも近い整数に切り捨てることにより,この公式は inadr 引数に指定するアドレス範囲が,CPU固有のページの 2ページに対応しなければならないことを示しています。
Alphaシステムでサポートされるページ・サイズを確認するには, $GETSYIシステム・サービスを使用します。例 5-6は, このシステム・サービスを使用して実行時にページ・サイズを確認する方法を示しています。
#include <ssdef.h> #include <stdio.h> #include <stsdef.h> #include <descrip.h> #include <dvidef.h> #include <rms.h> #include <secdef.h> #include <syidef.h> /* defines page size item code symbol */ struct itm { /* define item list */ short int buflen; /* length in bytes of return value buffer */ short int item_code; /* item code */ long bufadr; /* address of return value buffer */ long retlenadr; /* address of return value length buffer */ } itmlst[2]; long cpu_pagesize; long cpu_pagesize_len; main( argc, argv ) int argc; char *argv[]; { int status = 0; itmlst[0].buflen = 4; /* page size requires 4 bytes */ itmlst[0].item_code = SYI$_PAGE_SIZE; /* page size item code */ itmlst[0].bufadr = &cpu_pagesize; /* address of ret_val buffer */ itmlst[0].retlenadr = &cpu_pagesize_len; /* addr of length of ret_val */ itmlst[1].buflen = 0; itmlst[1].item_code = 0; /* Terminate item list with longword of 0 */ status = sys$getsyiw( 0, 0, 0, &itmlst, 0, 0, 0 ); if( status & STS$M_SUCCESS ) { printf("getsyi succeeds, page size = %d\n",cpu_pagesize); exit( status ); } else { printf("getsyi fails\n"); exit( status ); } }
$LKWSETシステム・サービスは,VAXシステムでもAlphaシステムでも同様に, アドレス範囲として inadr 引数に指定したページ範囲をワーキング・ セットとしてロックします。このシステム・サービスは必要に応じて,アドレスを CPU固有のページ境界に調整します。
しかし,Alpha命令は完全な仮想アドレスを指定できないため, Alphaイメージはプロシージャ記述子に対するポインタを通じて, 間接的にプロシージャとデータを参照しなければなりません。 プロシージャ記述子には,実際のコード・アドレスも含めて, プロシージャに関する情報が格納されます。 プロシージャ記述子とデータに対するこれらのポインタは, リンケージ・セクションと呼ぶ新しいプログラム・セクションに収集されます。
Alphaシステムでは, 単にコード・セクションをメモリにロックするだけでは性能を向上するのに不十分です。 関連するリンケージ・セクションもワーキング・セットとしてロックしなければなりません。
リンケージ・セクションをメモリ内でロックするには, リンケージ・セクションの先頭アドレスと末尾アドレスを判断し, $LKWSETシステム・サービスを呼び出すときに, inadr 引数の値としてこれらのアドレスを指定しなければなりません。