前へ | 次へ | 目次 | 索引 |
アプリケーションで仮想アドレス空間の定義された領域にセクションをマッピングする場合には,ソース・コードを変更しなければならない可能性があります。これは,AXPシステムでは$CRMPSCおよび$MGBLSCシステム・サービスがVAXシステムと異なる方法で一部の引数を解釈するからです。相違点は次のとおりです。
可能な場合には,拡張された仮想アドレス空間にデータがマッピングされるように,アプリケーションを変更してください。アプリケーションがデータをマッピングする方法を変更できない場合には,次のガイドラインに従ってください。
セクションがマップされるときに,隣接データが重ね書きされないことを確認するためのよりよい方法は,リンカにバッファを独立したイメージ・セクションとして指定することです(リンカはイメージをイメージ・セクションから作成します。それぞれのイメージ・セクションは,イメージの各部分のメモリの必要量を定義しています)。リンカはイメージ・セクションをページ境界に配置し,隣接するデータはつぎのページ境界から配置します。このためバッファを独自のイメージ・セクションに独立されることによって,隣接データがマッピング操作によって重ね書きされないことが保証されます。このように,隣接するデータを破壊したり,バッファのサイズを変更することなく1ページ分のメモリをセクションにマップすることが可能です。
リンカがセクション・ファイルを独自のイメージ・セクションに置いたことを確認するために,リンカのPSECT_ATTRオプションを使用してSOLITARYプログラム・セクション属性を設定する必要があります(詳しくは『OpenVMS Linker Utility Manual』を参照してください)。また,使用している高級または中級のプログラミング言語を,コンパイラが定義したバッファを別のプログラム・セクションに置くことを確認するために,使用する必要があるかもしれません。詳しくは,コンパイラの解説書を参照してください。
セクションを独自のイメージ・セクションに分離する場合には,SOLITARYプログラム・セクション属性を使用して,先頭アドレスはページ境界にアラインされます。これは,実行時のホスト・マシンのページ・サイズにかかわらず,リンカが省略時の設定によりイメージ・セクションをページ境界にアラインするからです。
セクションの末尾アドレスがCPU固有のページ境界にアラインされたことを確認するためには,アプリケーションを実行しているマシンがサポートしているページ・サイズを知る必要があります。$GETSYI システム・サービスや LIB$GETSYI ランタイム・ライブラリ・ルーチンを呼ぶことにより,実行時にCPU固有のページ・サイズを得ることができます。また,得た値を使ってアラインされた末尾アドレスの値を計算し,inadr引数内でシステム・サービスに渡すこともできます。
システムがマップした使用可能なメモリ量を判断するためには,retadr 引数を指定してください。たとえアプリケーションがページの一部分しか使用しないとしても,オペレーティング・システムは最低でも1ページをマップします。retadr 引数で指定された末尾アドレスは使用できるメモリの上限を示します(AXPシステムでアプリケーションが$CRMPSCシステム・サービスに relpag 引数を指定する場合,必ず retadr 引数を指定しなければなりません)。
たとえば,例 2-4 に示すVAXプログラムは,第 2.3.1 項 で作成したセクション・ファイルを既存の仮想アドレス空間にマッピングします。アプリケーションはbufferという名前のバッファを定義します。このバッファのサイズは512バイトであり,これは VAXのページ・サイズを反映しています。プログラムはバッファの1バイト目のアドレスを先頭アドレスとして,また,バッファの最終バイトのアドレスを末尾アドレスとして inadr 引数に渡すことにより,セクションの正確な境界を定義します。
例 2-4 仮想アドレス空間の定義された領域へのセクションのマッピング |
---|
#include <ssdef.h> #include <stdio.h> #include <stsdef.h> #include <descrip.h> #include <dvidef.h> #include <rms.h> #include <secdef.h> struct FAB fab; char *filename = "maptest.dat"; char _align(page)buffer[512]; main(argc, argv) int argc; char *argv[]; { int status = 0; long flags = 0; long inadr[2]; long retadr[2]; int fileChannel; /******** create disk file to be mapped *************/ fab = cc$rms_fab; fab.fab$l_fna = filename; fab.fab$b_fns = strlen(filename); fab.fab$l_fop = FAB$M_CIF | FAB$M_UFO; /* must be UFO */ status = sys$create(&fab); if(status & STS$M_SUCCESS) printf("Opened mapfile %s\n",filename); else { printf("Cannot open mapfile %s\n",filename); exit(status); } fileChannel = fab.fab$l_stv; /********** create and map the section ****************/ inadr[0] = &buffer[0]; inadr[1] = &buffer[511]; printf("inadr[0]=%u,inadr[1]=%u\n",inadr[0],inadr[1]); status = SYS$CRMPSC(inadr, /* inadr=address target for map */ &retadr, /* retadr= what was actually mapped */ 0, /* acmode */ 0, /* flags */ 0, /* gsdnam, only for global sections */ 0, /* ident, only for global sections */ 0, /* relpag, only for global sections */ fileChannel, /* returned by SYS$CREATE */ 0, /* pagcnt = size of sect. file used */ 0, /* vbn = first block of file used */ 0, /* prot = default okay */ 0); /* page fault cluster size */ if(status & STS$M_SUCCESS) { printf("Map succeeded\n"); printf("retadr[0]=%u,retadr[1]=%u\n",retadr[0],retadr[1]); } else { printf("Map failed\n"); exit(status); } } |
例 2-4 に示したプログラムをAXPシステムで正しく実行するには,次の変更が必要です。
これらの目標を達成するための1つの方法として,SOLITARYプログラム・セクション属性を使用することにより,セクション・データを格納したプログラム・セクションを独自のイメージ・セクションに分離する方法があります。
この例では,bufferという名前のセクションはbufferという名前のプログラム・セクション内に示されています(プログラム・セクションの生成方法は,各プラットフォーム上の言語の種類により異なります。セクションが独自のプログラム・セクションにあることをコンパイラの解説書で確認してください)。次のリンク操作は,このプログラム・セクションのSOLITARY属性を設定する方法を示しています。
$ LINK MAPTEST, SYS$INPUT/OPT PSECT_ATTR=BUFFER,SOLITARY [Ctrl/Z] |
CPU固有のページ境界の末尾にアラインされる末尾アドレスをセクション・バッファに対して指定するには,実行時にCPU固有のページ・サイズを入手し,その値から1を減算し,その値を使用して配列の最終要素のアドレスを求めます。この値をinadr引数の2番目のロングワードとして渡します(実行時にページ・サイズを判断する方法については,第 2.4 節 を参照してください)。セクションがマッピングされるバッファの割り当てを変更する必要はありません。
アプリケーションが任意のページ・サイズのAXPシステムで正しく実行されるようにするには,/BPAGE=16修飾子を指定することにより,リンカがイメージ・セクションを64KBの境界に強制的にアラインするようにします。実際にマッピングされるメモリの総量は,使用可能なメモリの合計よりはるかに大きくなる可能性があります。使用可能なメモリのサイズは,ページ・カウント( pagcnt )引数の値とセクション・ファイルのサイズのうち,どちらか小さい方の値によって決定されます。セクションの範囲内に含まれないメモリを使用しないようにするには,retadr 引数に戻された値を使用します。
例 2-5 は,AXPシステムで正しく実行するために 例 2-4 に対して必要なソースの変更を示しています。
例 2-5 例 2-4をAXPシステムで実行するのに必要なソース・コードの変更 |
---|
#include <ssdef.h> #include <stdio.h> #include <stsdef.h> #include <string.h> #include <stdlib.h> #include <descrip.h> #include <dvidef.h> #include <rms.h> #include <secdef.h> #include <syidef.h> (1) char buffer[512]; (2) char *filename = "maptest.dat"; struct FAB fab; long cpu_pagesize; (3) struct itm { /* item list */ short int buflen; /* length of buffer in bytes */ short int item_code; /* symbolic item code */ long bufadr; /* address of return value buffer */ long retlenadr; /* address of return value buffer length */ } itmlst[2]; (4) main(argc, argv) int argc; char *argv[]; { int i; int status = 0; long flags = SEC$M_EXPREG; long inadr[2]; long retadr[2]; int fileChannel; char *mapped_section; /******** create disk file to be mapped *************/ fab = cc$rms_fab; fab.fab$l_fna = filename; fab.fab$b_fns = strlen(filename); fab.fab$l_fop = FAB$M_CIF | FAB$M_UFO; /* must be UFO */ status = sys$create(&fab); if(status & STS$M_SUCCESS) printf("%s opened\n",filename); else { exit(status); } fileChannel = fab.fab$l_stv; /********** obtain the page size at run time ****************/ itmlst[0].buflen = 4; itmlst[0].item_code = SYI$_PAGE_SIZE; itmlst[0].bufadr = &cpu_pagesize; itmlst[0].retlenadr = &cpu_pagesize_len; itmlst[1].buflen = 0; itmlst[1].item_code = 0; (5) status = sys$getsyiw(0, 0, 0, &itmlst, 0, 0, 0); if(status & STS$M_SUCCESS) { printf("getsyi succeeds, page size = %d\n",cpu_pagesize); } else { printf("getsyi fails\n"); exit(status); } /********** create and map the section ****************/ inadr[0] = &buffer[0]; inadr[1] = &buffer[cpu_pagesize - 1]; (6) printf("address of buffer = %u\n", inadr[0]); status = SYS$CRMPSC(&inadr, /* inadr=address target for map */ &retadr, /* retadr= what was actually mapped */ 0, /* acmode */ 0, /* noflags to set */ 0, /* gsdnam, only for global sections */ 0, /* ident, only for global sections */ 0, /* relpag, only for global sections */ fileChannel, /* returned by SYS$CREATE */ 0, /* pagcnt = size of sect. file used */ 0, /* vbn = first block of file used */ 0, /* prot = default okay */ 0); /* page fault cluster size */ if(status & STS$M_SUCCESS) { printf("section mapped\n"); printf("start address returned =%u\n",retadr[0]); } else { printf("map failed\n"); exit(status); } } |
次のリストの各項目は,例 2-5 の番号に対応しています。
2.3.4 オフセットによるセクション・ファイルのマッピング
アプリケーションではセクション・ファイルの一部だけをマッピングできます。その場合には,マッピングを開始するアドレスをセクション・ファイルの先頭からのオフセットとして指定します。このオフセットを指定するには,$CRMPSCシステム・サービスの relpag 引数に対して値を指定します。relpag 引数の値は,ファイルの先頭を基準にしてマッピングを開始するページ番号を指定します。
$CRMPSCシステム・サービスは互換性を維持するために,VAXシステムと AXPシステムの両方のシステムにおいて,relpag 引数の値を 512バイト単位で解釈します。しかし,AXPシステムのCPU固有のページ・サイズは512バイトより大きいため,relpag 引数にオフセットとして指定する値はおそらくCPU固有のページ境界にアラインされません。$CRMPSCシステム・サービスは仮想メモリをCPU固有のページ単位でのみマッピングできます。したがって,AXPシステムでは,セクション・ファイルのマッピングはオフセット・アドレスを含むCPU固有のページの先頭から開始され,オフセットによって指定されるアドレスから正確に開始されるわけではありません。
ルーチンは,オフセットによって指定されるアドレスを含むCPU固有のページの先頭からマッピングを開始しますが,retadr 引数に戻される先頭アドレスはオフセットによって指定されたアドレスであり,実際にマッピングが開始されたアドレスではありません。 |
アプリケーションでオフセットからセクション・ファイルにマッピングする場合には,AXPシステムでマッピングされる余分な仮想メモリ空間を格納できるように,inadr 引数に指定されるアドレス範囲のサイズを拡大する必要があります。指定されるアドレス範囲が小さすぎる場合には,アプリケーションはセクション・ファイルの中で必要な部分全体をマッピングできない可能性があります。これは,マッピングがセクション・ファイルの先頭アドレスから開始されるからです。
たとえば,VAXシステムでセクション・ファイルをマッピングするときに,ブロック番号15から始まる16ブロックをマッピングする場合には,アドレス範囲として 16*512バイトのサイズを inadr 引数に指定し,relpag 引数に対して15を指定できます。これと同じマッピングをAXPシステムで実行するには,ページ・サイズの違いを考慮しなければなりません。たとえば,8Kバイト・ページ・サイズのAXPシステムでは,relpag オフセットによって指定されるアドレスは,図 2-2 に示すように,15ページレットをCPU固有の 1ページに格納できます。AXPシステムでは,$CRMPSCシステム・サービスはセクション・ファイルのマッピングをCPU固有のページ境界から開始するため,16番目から30番目までのブロックを正しくマッピングできません。マッピングを正しく実行するには,AXPシステムで$CRMPSCシステム・サービス(または $MGBLSCシステム・サービス)がマッピングする追加の15ページレットを格納できるようにアドレス範囲のサイズを拡大しなければなりません。このようにサイズを拡大しなかった場合には,指定したセクション・ファイルの中で1ブロックだけしかマッピングされません。図 2-2 はこの状況を示しています。
図 2-2 オフセットによるマッピングに対してアドレス範囲が与える影響
relpag 引数に指定するアドレス範囲をどれだけ拡大するかを計算する場合には,次の公式を使用すると便利です。この公式は,特定の数のページレットをマッピングするのに充分なCPU固有のページ数を計算します。
(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ページに対応しなければならないことを示しています。
前へ | 次へ | 目次 | 索引 |