日本-日本語

製品  >  ソフトウェア  >  OpenVMS  >  マニュアル

OpenVMS マニュアル


≫ 

OpenVMS V8.3
ライブラリ

タイトルページ
目次
まえがき
第 1 章:はじめに
第 2 章:仮想アドレス空間の概要
第 3 章:システム・サービスの 64 ビット・アドレッシングのサポート
第 4 章:メモリ管理 VLM 機能
第 5 章:64 ビット・アドレッシングを対象とする RMS インタフェースの強化
第 6 章:ファイル・システムの 64 ビット・アドレッシングのサポート
第 7 章:OpenVMS Alpha デバイスの 64 ビット・アドレッシングのサポート
第 8 章:OpenVMS Alpha 64 ビット API ガイドライン
第 9 章:64 ビット・アドレッシングをサポートする OpenVMS Alpha ツールおよびユーティリティ
第 10 章:言語およびポインタの 64 ビット・アドレッシング・サポート
第 11 章:DEC C RTL の 64 ビット・アドレッシング・サポート
付録 A :64 ビット・アドレッシングのための C マクロ
付録 B :64 ビット・アドレッシングのための MACRO-32 マクロ
付録 C :64 ビット・プログラム例
付録 D :VLM プログラム例
索引
PDF
OpenVMS ホーム
Open VMS Alpha オペレーティング・システム | HPE 日本

Open VMS Alpha オペレーティング・システム
64 ビット・アドレッシングおよび VLM 機能説明書


目次 索引

第 8 章
OpenVMS Alpha 64 ビット API ガイドライン

本章では,OpenVMS Alpha 64 ビット仮想アドレッシングをサポートする, 64 ビット・インタフェースを開発する際のガイドラインについて説明します。独自の 64 ビット・アプリケーション・プログラミング・インタフェースを開発しているアプリケーション・プログラマにとって,このガイドラインは大変有用です。

本章で推奨するガイドラインは,難しく厳密な規則ではありません。適切なプログラミングの例を紹介しながら,ガイドラインを説明します。

C ポインタ・プラグマについての詳細は,『DEC C User's Guide for OpenVMS Systems』を参照してください。

8.1 クォドワード/ロングワード引数ポインタのガイドライン

OpenVMS Alpha 64 ビット・アドレッシングのサポートは,アプリケーション・プログラムが 64 ビット・アドレス空間のデータへアクセスすることを認めています。そのためアプリケーションの中では, 32 ビット符合拡張値でないポインタ(64 ビット・ポインタ)がより一般的になります。既存の 32 ビット API も引き続きサポートされるため,プログラマは 64 ビット・ポインタの存在を特に注意する必要があります。

たとえば 64 ビット・アドレスを,誤って 32 ビット・アドレスしか処理できないルーチンに渡す可能性が考えられます。また,新しい API が 64 ビット・ポインタをデータ構造に埋め込む場合も考えられます。このようなポインタは,新しいデータ構造の中では符号拡張の 32 ビット値として存在しますが,最初は 32 ビット・アドレス空間のポイントに制限されます。

どのルーチンも,32 ビット・アドレスに代わって 64 ビット・アドレスが渡される場所では,プログラミング・エラーに注意する必要があります。このようなチェックは符号拡張チェックと呼ばれるもので,ビット 31 の値に一致した状態で,アドレスの上位 32 ビットがすべて 0,またはすべて 1 であることを確認します。このチェックは,この制限を適用しているルーチン・インタフェースで実行できます。

ルーチン・インタフェースを新しく定義するときは, 32 ビット・ソース・モジュールから,ルーチンの呼び出しを簡単にプログラムできるように考慮しなければなりません。また,64 ビット・アドレッシングのサポートをもともと意図している言語だけでなく,すべての OpenVMS プログラミング言語で作成された呼び出しを考慮しなければなりません。新規ルーチンの 32 ビット呼び出し側に対して不慣れなプログラミングを強いることを防ぐために, 64 ビット呼び出し側だけでなく 32 ビット呼び出し側にも配慮しなければなりません。

32 ビット・アドレス空間 (P0/P1/S0/S1) での常駐が禁止されている参照渡しの引数は,その参照アドレスを符号拡張チェックする必要がある

OpenVMS 呼び出し規則では,ルーチンに渡される 32 ビット値は,ルーチンが呼び出される前に,64 ビットに符号拡張されることが要求されます。このため,呼び出されるルーチンは,常に 64 ビット値を受け取ります。 32 ビット・ルーチンは,引数に対する参照を符号拡張チェックしない限り,その呼び出し側が 32 ビット・アドレスで正しくルーチンを呼び出したかどうかを判断できません。

データがディスクリプタでルーチンに渡される場合には,ディスクリプタへの参照に対してもこの符号拡張チェックが適用されます。

符号拡張チェックが失敗した場合,呼び出されたルーチンは,エラー状態 SS$_ARG_GTR_32_BITS を返します。

あるいは,呼び出されるルーチンが,エラーがない状態で 64 ビットの位置に渡されるデータを受け取ることが求められる場合,符号拡張チェックが失敗したときは,データを 32 ビット・アドレス空間にコピーすることができます。ルーチンがデータをコピーする 32 ビット・アドレス空間は,ローカル・ルーチン記憶域 (つまり,現在のスタック) です。ローカル記憶域以外の 32 ビット位置にデータがコピーされる場合,メモリ・リークおよびリエントラントについて考慮する必要があります。

新規ルーチンを開発する場合,コードへのポインタおよび新規ルーチンへ渡されるすべてのデータ・ポインタは,できるだけ 64 ビット・アドレス空間を利用することが望まれます。これは,データがルーチンの場合,またはプログラマやコンパイラ,リンカが通常は 64 ビット・アドレス空間に配置しない 静的データと一般に考えられる場合であっても同様です。コードと静的データが 64 ビット・アドレス空間の中でサポートされる場合,このルーチンを改めて変更する必要はありません。

32 ビット・ディスクリプタ引数が 32 ビット・ディスクリプタであることを確認する必要がある

ディスクリプタを受け取るルーチンは, 32 ビットおよび 62 ビット・ディスクリプタ形式を区別するフィールドをチェックしなければなりません。64 ビット・ディスクリプタを受け取ると,ルーチンはエラーを返します。

既存の大半の 32 ビット・ルーチンは, 64 ビット・ディスクリプタが間違って指定されると,次の理由でエラー状態 SS$_ACCVIO を返します。

  • ディスクリプタの 64 ビット形式は, 32 ビット・ディスクリプタの LENGTH が位置しているオフセットで, MBO(0 でなければならない)ワードを含む。

  • 図 8-1 に示すように, 32 ビット・ディスクリプタの POINTER が位置しているオフセット 4 で, MBMO(-1 でなければならない) ロングワードを含む。

図 8-1 32 ビット・ディスクリプタ




64 ビット・ディスクリプタで渡される引数を受け取るルーチンは, 64 ビット・ディスクリプタと同様に 32 ビット・ディスクリプタに対処する必要がある

新規ルーチンは,同じルーチンの中で 32 ビット・ディスクリプタと 64 ビット・ディスクリプタに対処します。同じ引数で 32 ビット・ディスクリプタまたは 64 ビット・ディスクリプタを指すことができます。オフセット 0 での 64 ビット・ディスクリプタ MBO ワードは,値が 1 であることをテストし,オフセット 4 での 64 ビット・ディスクリプタ MBMO ロングワードは -1 であることをテストすることによって, 32 ビット・ディスクリプタと 64 ビット・ディスクリプタを区別します。

32 ビット・ディスクリプタに加えて 64 ビット・ディスクリプタの処理を目的として変換されている,既存の 32 ビット・ルーチンを考えてみます。入力ディスクリプタが 64 ビット・ディスクリプタであると判断されると, 64 ビット・ディスクリプタが指すデータを,まず 32 ビットのメモリ位置にコピーした上で, 32 ビット・メモリの中で 32 ビット・ディスクリプタが作成されます。この新しい 32 ビット・ディスクリプタを既存の 32 ビット・コードに渡すことで,ルーチン内部での変更はこれ以上必要ありません。

32 ビット項目リスト引数が 32 ビット項目リスト引数であることを確認する必要がある

項目リストは,item_list_2 および item_list_3 という 2 種類の形式で定義されます。 item_list_2 形式の項目リストは 2 つのロングワードで構成されており,先頭のロングワードには長さと項目コードのフィールドが含まれ, 2 番目のロングワードには通常バッファ・アドレスが含まれています。

これに対して item_list_3 形式の項目リストは 3 つのロングワードで構成されています。先頭のロングワードには長さと項目コードのフィールドが含まれ, 2 番目と 3 番目のロングワードには,通常バッファ・アドレスおよび戻り長アドレス・フィールドが含まれています。

32 ビット項目リストが 2 種類存在するため, 64 ビット項目リストも 2 種類定義されています。新しく加えられた item_list_64a および item_list_64b は, 32 ビットの項目リストにそれぞれ対応します。どちらの形式の項目リストも,オフセット 0 および 4 の位置に, MBO および MBMO フィールドをそれぞれ含んでいます。また,両方の項目リストとも,ワード・サイズの項目コード・フィールド,クォドワード・サイズの長さフィールド,およびクォドワード・サイズのバッファ・アドレス・フィールドをそれぞれ含んでいます。なお,item_list_64b 形式の項目リストは,これとは別にクォドワード・サイズの戻り値長アドレス・フィールドも含んでいます。戻り値長は 64 ビットです。

項目リストを受け取るルーチンは,フィールドをテストして,これが 32 ビット項目リストと 64 ビット項目リストのどちらであるかを区別しなければなりません。 64 ビット項目リストを受け取った場合,ルーチンはエラーを返します。

既存の大半の 32 ビット・ルーチンは, 64 ビット項目リストが間違って渡されたことを次の理由で判別した場合,エラー状態 SS$_ACCVIO を返します。またはこれをシグナル通知します。

  • 64 ビット形式の項目リストはオフセット 0 の位置に MBO (must be one) ワードを含んでいるが, 32 ビット項目リストの場合は,ここに LENGTH が位置している。

  • 64 ビット項目リストはオフセット 4 の位置に MBMO (must be minus one) ロングワードを含むが, 32 ビット項目リストの場合は,ここに BUFFER ADDRESS が位置している。 図 8-2 および 図 8-3 はこの状態を示している。

図 8-2 item_list_64a


図 8-3 item_list_64b




64 ビット項目リスト引数で渡される引数を受け取るルーチンは, 64 ビット項目リスト同様 32 ビット項目リストにも対処する必要がある

新規ルーチンは,同一ルーチン内で 32 ビット項目リストと 64 ビット項目リストに対処しなければなりません。同じ引数が, 32 ビット項目リストと 64 ビット項目リストのどちらも指すことができます。 64 ビット項目リストのオフセット 0 にある MBO ワードは 1 でなければならず, 64 ビット項目リストのオフセット 4 にある MBMO ロングワードは -1 でなければなりません。そのため,これをテストすることによって, 64 ビット項目リストと 32 ビット項目リストを区別します。

ポインタの参照渡しを避ける

特定のメモリ管理ルーチンなどで,ポインタを参照渡しする必要がある場合,ポインタは 64 ビットで定義します。

32 ビット・ポインタと 64 ビット・ポインタが混在すると, 64 ビット・ポインタが期待されている場所に,呼び出し側が間違って 32 ビット・ポインタを参照渡しするなどの,プログラミング・エラーを引き起こします。

プログラマによってロングワードしか割り当てられていないのに,呼び出されたルーチンが 64 ビット・ポインタを読み込むと,ルーチンの中で間違ったアドレスが使用されることになります。

また,呼び出されたルーチンが 64 ビット・ポインタを返すと,プログラマによって割り当てられているロングワードに対して 64 ビット・アドレスが書き込まれるため,データの破損が発生します。

ポインタを参照渡しされる既存のルーチンには, 64 ビット・サポート用の新規インタフェースが必要です。旧ルーチン・インタフェースは, 32 ビット・メモリ位置の中でポインタをこれまでのように渡し,これに対して新規ルーチン・インタフェースは, 64 ビット・メモリ位置でポインタを渡します。同じインタフェースを保持した上で,64 ビット・ポインタを渡すと,既存のプログラムに異常が発生します。

例: SYS$CRETVA_64 サービスで使用される戻り仮想アドレスは,参照渡しでポインタを渡すことのできる例です。 P0 空間および P1 空間の中で作成される仮想アドレスは, 64 ビットすべてが返されますが,意味があるのは 32 ビットだけであることが保証されています。 SYS$CRETVA_64 は 64 ビット空間にアドレス空間を作成し, 64 ビット・アドレスを返すこともできます。64 ビット・アドレスが返るため,返される値は常に 64 ビットであることが必要です。

メモリ割り当てルーチンは,可能な場合,値 (つまり R0) で割り当てられるデータへのポインタを返します。 C 割り当てルーチン,malloc,calloc,realloc がこの例です。

メモリ管理ルーチンでないルーチンの新規インタフェースで,出力引数を定義してアドレスを受け取ることは避けてください。 64 ビット・サブシステムがメモリを割り当て, 32 ビット呼び出し側に対してポインタを出力引数で戻すと,問題が発生します。呼び出し側は, 64 ビット・ポインタをサポートまたは指定できない可能性があります。あるデータへのポインタを返す代わりに,呼び出し側はバッファへのポインタを用意して,呼び出されたルーチンがこのユーザ・バッファにデータをコピーすることが望まれます。

参照渡しされる 64 ビット・ポインタは,ルーチンへの呼び出しが 64 ビット言語または 32 ビット言語で作成できるような環境で定義する必要があります。 64 ビット・ポインタはすべての呼び出し側によって渡される必要があることを,明確に指示しなければなりません。

特に要求されない限りルーチンは 64 ビット・アドレスを返す必要はない

呼び出し側で 64 ビット・アドレスを処理できることが確実でない限り,メモリを割り当て,その呼び出し側に対してアドレスを返すルーチンは,常に 32 ビット・アドレス・メモリを割り当てることに,十分に注意してください。これは,関数の戻り値と出力パラメータの両方に当てはまります。この規則は,64 ビット・アドレスを求めていないアプリケーションに, 64 ビット・アドレスが侵入することを防ぐものです。この結果,呼び出し可能なライブラリを開発しているプログラマは,特に注意してこの規則に従う必要があります。

既存のルーチンが,割り当てられているメモリのアドレスをルーチン値として返す場合を考えます。呼び出し側が 64 ビットを処理できることがわかっていて,ルーチンが入力パラメータを受け取った場合, 64 ビット・アドレスを返すことに何も問題はありません。これ以外の場合は,引き続き 32 ビット符号拡張アドレスを返さなければなりません。 後者の場合,64 ビットを処理できる呼び出し側が 64 ビット・メモリの割り当てを優先するのであれば,既存のバージョンに代わって,ルーチンの新バージョンを提供できます。

例: 文字列ディスクリプタを操作する LIBRTL 内のルーチンは,渡されたディスクリプタが新しい 64 ビット形式であれば,呼び出し側が 64 ビットを処理できると判断します。この場合は,文字列データに 64 ビット・メモリを割り当てても安全です。これ以外の場合は,32 ビット・アドレス・メモリだけを引き続き使用します。

パブリック・インタフェースのデータ構造では埋め込みポインタを避ける

新規インタフェースの新しい構造で埋め込みポインタが必要な場合, (クォドワード・アラインされている) 64 ビット・ポインタ用の構造の中で記憶域を用意します。呼び出されたルーチンは,場合によって構造からポインタを読み込む必要がありますが,単純に 64 ビット全体を読み込みます。

ポインタが 32 ビット符号拡張アドレスの制約を受けている場合 (たとえば,ポインタが 32 ビット・ルーチンに渡される場合),ルーチンの入り口で,64 ビット・ポインタについて符号拡張チェックが実行されます。符号拡張チェックが失敗すると,エラー状態 SS$_ARG_GTR_32_BITS が呼び出し側に返されます。または 64 ビット・アドレス空間に常駐しているデータが検出されると,これが 32 ビット・アドレス空間にコピーされます。

新しい構造は,64 ビットの呼び出し側も 32 ビットの呼び出し側も,余分なコードを使用しないで済むように定義する必要があります。構造では,64 ビットの呼び出し側を対象とするクォドワード・フィールドと, 32 ビットの呼び出し側を対象とする 2 つのロングワード・フィールドが互いに重なり合って提供されます。先頭のロングワードは 32 ビット・ポインタ・フィールドで,次のロングワードはMBSE (must be sign-extension) フィールドです。 32 ビットの呼び出し側の大半では,MBSE フィールドは 0 になります。これは,ポインタが 32 ビット・プロセス空間アドレスとなるためです。ここで重要なのは,ポインタを 64 ビット値として定義し, 32 ビットの呼び出し側に対してクォドワード全体を入力する必要があることを明確にすることです。

次の例では,64 ビットと 32 ビットの両方の呼び出し側が,関数 routine を呼び出すときに,block 構造にポインタを渡し,同じ関数プロトタイプを使用します(data は別のモジュールで定義され,その構造は不定であるものとします)。


#pragma required_pointer_size save 
#pragma required_pointer_size 32 
 
typedef struct block { 
    int blk_l_size; 
    int blk_l_flags; 
    union  { 
#pragma required_pointer_size 64 
        struct data *blk_pq_pointer; 
#pragma required_pointer_size 32 
        struct  { 
            struct data *blk_ps_pointer; 
            int blk_l_mbse; 
            } blk_r_long_struct; 
        } blk_r_pointer_union; 
    } BLOCK; 
 
#define blk_pq_pointer     blk_r_pointer_union.blk_pq_pointer 
#define blk_r_long_struct  blk_r_pointer_union.blk_r_long_struct 
#define blk_ps_pointer     blk_r_long_struct.blk_ps_pointer 
#define blk_l_mbse         blk_r_long_struct.blk_l_mbse 
 
/* Routine accepts 64-bit pointer to the "block" structure */ 
#pragma required_pointer_size 64 
int routine(struct block*); 
 
#pragma required_pointer_size restore 

入力引数を指定する既存の 32 ビット・ルーチンの場合,これがポインタを埋め込む構造のときは,別の方法で既存の 32 ビット・インタフェースを保持できます。実行時に,32 ビット形式のデータ構造と区別できる, 64 ビット形式のデータ構造を開発できます。 32 ビット形式の構造だけを受け取る既存のコードは, 64 ビット形式の構造が指定されると,自動的に異常終了します。

新しい 64 ビット構造についての構造定義には, 32 ビット形式の構造を含む必要があります。 32 ビット形式の構造を含むことによって,呼び出されたルーチンは入力引数を, 64 ビット形式の構造へのポインタとして宣言し,いずれの場合でも明確に処理することができます。

言語に対して,型のチェックを行う 2 種類の関数プロトタイプを用意しなければなりません。省略時の設定時の関数プロトタイプは,引数を 32 ビット形式の構造へのポインタとして指定します。 64 ビット形式の関数プロトタイプは,マニュアルに説明されているように,シンボルを定義することによって選択できます。

64 ビット対 32 ビット・ディスクリプタは,これがどのように行われるかを示す例です。

例: 次の例では,シンボル FOODEF64の状態が,正しい関数プロトタイプと共に, 64 ビット形式の構造を選択します。シンボル FOODEF64が未定義の場合,古い 32 ビット構造が定義され,古い 32 ビット関数プロトタイプが使用されます。

関数 foo_printを実現するソース・モジュールはシンボル FOODEF64を定義し, 32 ビットおよび 64 ビットの呼び出し側からの呼び出しを処理することができます。 64 ビットの呼び出し側はフィールド foo64$l_mbm1を -1 に設定します。 foo_printはフィールド foo64$l_mbm1 が -1 であるかどうかをテストすることによって,呼び出し側が 64 ビット形式の構造を使用しているか,または 32 ビット形式の構造を使用しているかを判断します。


#pragma required_pointer_size save 
#pragma required_pointer_size 32 
 
typedef struct foo { 
    short int     foo$w_flags; 
    short int     foo$w_type; 
    struct data * foo$ps_pointer; 
    } FOO; 
 
#ifndef FOODEF64 
 
/* Routine accepts 32-bit pointer to "foo" structure */ 
int foo_print(struct foo * foo_ptr); 
 
#endif 
 
#ifdef FOODEF64 
 
typedef struct foo64 { 
    union  { 
        struct  { 
            short int     foo64$w_flags; 
            short int     foo64$w_type; 
            int           foo64$l_mbmo; 
#pragma required_pointer_size 64 
            struct data * foo64$pq_pointer; 
#pragma required_pointer_size 32 
            } foo64$r_foo64_struct; 
        FOO foo64$r_foo32; 
        } foo64$r_foo_union; 
    } FOO64; 
 
#define foo64$w_flags    foo64$r_foo_union.foo64$r_foo64_struct.foo64$w_flags 
#define foo64$w_type     foo64$r_foo_union.foo64$r_foo64_struct.foo64$w_type 
#define foo64$l_mbmo     foo64$r_foo_union.foo64$r_foo64_struct.foo64$l_mbmo 
#define foo64$pq_pointer foo64$r_foo_union.foo64$r_foo64_struct.foo64$pq_point 
er#define foo64$r_foo32    foo64$r_foo_union.foo64$r_foo32 
 
/* Routine accepts 64-bit pointer to "foo64" structure */ 
#pragma required_pointer_size 64 
int foo_print(struct foo64 * foo64_ptr); 
 
#endif 
 
#pragma required_pointer_size restore 

上の例で,構造 fooおよび foo64が同じソース・モジュールの中で交換して使用される場合,シンボル FOODEF64 を削除できます。この場合,ルーチン foo_print は次のように定義されます。


int foo_print (void * foo_ptr); 

FOODEF64 シンボルを削除することによって, 32 ビットの呼び出し側と 64 ビットの呼び出し側が同じ関数プロトタイプを使用できます。ただし C ソースのコンパイルの際に,厳密な型のチェックが行われることはありません。


目次 索引

印刷用画面へ
プライバシー 本サイト利用時の合意事項