日本-日本語

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

OpenVMS マニュアル


≫ 

OpenVMS
ライブラリ

タイトルページ
目次
まえがき
第 1 章:移行プロセスの概要
第 2 章:移行方法の選択
第 3 章:アプリケーションの移行
第 4 章:再コンパイルと再リンクの概要
第 5 章:ページサイズの拡大に対するアプリケーションの対応
第 6 章:共用データの整合性の維持
第 7 章:アプリケーションデータ宣言の移植性の確認
第 8 章:アプリケーション内の条件処理コードの確認
第 9 章:OpenVMS I64コンパイラ
付録 A :アプリケーション評価チェックリスト
用語集
索引
PDF
OpenVMS ホーム

OpenVMS
OpenVMS VAX から OpenVMS I64 への
アプリケーションの移行


目次 索引

第 6 章
共用データの整合性の維持

この章では,共用データの整合性を維持するために必要な同期メカニズムについて説明します。たとえば,ある種の VAX 命令で保証されている不可分性などについて説明します。

6.1 概要

アプリケーションで複数の実行スレッドを使用しており,これらのスレッドが同じデータをアクセスする場合には, I64 システムで共用データの整合性を保護するためには,アプリケーションに明示的な同期メカニズムを追加しなければなりません。正しく同期をとらなかった場合には, 1 つのアプリケーション・スレッドによって開始されたデータ・アクセスが,別のスレッドによって同時に開始されたアクセスに干渉する可能性があり,データは予測できない状態になります。

VAX システムでは,必要な同期のレベルは,以下のような実行スレッドの関係に応じて異なります。

  • 1 つのプロセス内で実行される複数のスレッド。たとえば,非同期システム・トラップ (AST) スレッドによってメイン・スレッドが割り込まれる場合
    AST スレッドはアプリケーションによって開始されますが,オペレーティング・システムによって開始されることもあります。たとえば,オペレーティング・システムは,AST を使用して状態を入出力状態ブロックに書き込みます。また,オペレーティング・システムは,AST を使用して,指定したユーザ・バッファへのバッファード I/O の読み取り操作を終了します。

  • 1 つのプロセッサ上で複数のプロセスが実行されており,各プロセス内の複数のスレッドが共通のグローバル・セクションにアクセスする場合

  • 複数のプロセッサ上で複数のプロセスが並列に実行されており,各プロセス内のスレッドがグローバル・セクションにアクセスする場合

VAX システムでは,マルチプロセッサ・システムの並列処理機能を利用するアプリケーションは,必ずロック,セマフォ,インターロック命令などの明示的な同期メカニズムを準備することにより,共用データを保護しなければなりません。しかし,ユニプロセッサ・システムで複数のスレッドを使用するアプリケーションは,明示的に共用データを保護していない可能性があります。これらのアプリケーションは,VAX ユニプロセッサ・システムで実行されるアプリケーションのスレッド間の同期を保証する VAX アーキテクチャの機能によって提供される,暗黙の保護に依存している可能性があります ( 第 6.1.1 項 を参照)。

6.1.1 不可分性を保証する VAX アーキテクチャの機能

VAX アーキテクチャの次の機能は,ユニプロセッサ・システムで実行される複数の実行スレッド間で同期を保証します (ただし VAX アーキテクチャでは,マルチプロセッサ・システムに対してはこのような不可分性は保証されません)。

  • 命令の不可分性---VAX アーキテクチャによって定義されている多くの命令は,単一プロセッサで実行される複数のアプリケーション・スレッドの観点から見ると, 1 つの割り込み不可能なシーケンス (不可分な操作と呼ぶ) として読み取り/変更/書き込み操作を実行できます。 Intel Itanium アーキテクチャでは,このような命令がサポートされます。
    VAX システムとの互換性を維持するために,Intel Itanium アーキテクチャでは,読み取り/書き込み操作が不可分な方法で実行されることを保証する,いくつかの命令が定義されています。これらの命令についての説明と,高級言語で作成されたプログラムでこの機能を利用するために, I64 システムのコンパイラが提供している機能については, 第 6.1.2 項 を参照してください。
    しかし,VAX システムでも, VAX 命令の不可分性に暗黙に依存することは望ましくありません。 VAX システムのコンパイラは,最適化を実行するため,インクリメント操作 ( x = x+ 1) のようなプログラム文に対して,VAX の不可分な命令が使用できる場合でも,それを使用するという保証はありません。

  • メモリ・アクセスの粒度---VAX アーキテクチャは,バイト・サイズのデータとワード・サイズのデータを 1 つの割り込み不可能な操作で処理できる命令をサポートしています (VAX アーキテクチャは他のサイズのデータを処理する命令もサポートしています)。 Intel Itanium アーキテクチャでも,バイト・サイズのデータとワード・サイズのデータを操作する命令をサポートしています。

  • 読み取り/書き込みの順序---VAX ユニプロセッサ・システムおよびマルチプロセッサ・システムでは,順次書き込み操作と読み取り操作は,すべてのタイプの外部実行スレッドから見て,要求した順序と同じ順序で実行されます。I64 ユニプロセッサ・システムでも,ユニプロセッサで実行される単一プロセス,または複数プロセス内で実行される複数の実行スレッドに対して,読み取り操作と書き込み操作の順序は同期がとられているように見えます。しかし,I64 マルチプロセッサ・システムで同時に実行されるスレッドからの書き込み操作では,明示的に同期をとることが必要です。
    VAX システムとの互換性を維持するために,Intel Itanium アーキテクチャでは,システム内のすべてのプロセッサから見て,読み取り/書き込み操作が指定した順に実行されるようにする命令をサポートしています。この命令についての説明と,高級言語でこの命令をどのように使用するかについての説明は, 第 6.1.2 項 を参照してください。この同期をとるために Intel Itanium アーキテクチャが提供する機能についての説明と,高級言語で作成されたプログラムでこの機能を利用するために, I64 システムのコンパイラが提供している機能については, 第 6.3 節 を参照してください。



6.1.2 Intel Itanium の互換性機能

Intel Itanium アーキテクチャには,VAX アーキテクチャの不可分性機能との互換性を維持するためのいつかのメカニズムがあります。

  • Compare and Exchange 命令---Intel Itanium 命令セットでは, Compare and Exchange 命令と呼ばれる 4 つの命令 (cmpxchg1,cmpxchg2, cmpxchg4,cmpxchg8) が定義されており,不可分な比較とメモリ入れ替え操作が可能になっています。

  • Exchange 命令---Intel Itanium 命令セットでは, Exchange 命令と呼ばれる 4 つの命令 (xchg1,xchg2,xchg4,xchg8) が定義されており,不可分なメモリの入れ替え操作が可能になっています。

  • Fetchadd 命令---Intel Itanium 命令セットでは, Fetch and Add Immediate と呼ばれる 2 つの命令 (fetchadd4, fetchadd8) が定義されており,メモリ位置の不可分なインクリメント操作およびデクリメント操作が可能になっています。

  • メモリ・フェンス---I64 命令セットには,マルチプロセッサ・システムで複数のプロセッサで実行される複数のスレッドが要求した読み取り/書き込み操作が要求した順に実行されているかのように見えるようにするための命令が準備されています。この命令はメモリ・フェンス (MF) と呼ばれ,複数の実行スレッドから見て,前のすべてのロード/ストア命令がメモリ・アクセスを完了するまで,後続のロード/ストア命令がメモリをアクセスしないことを保証します。
    MF 命令以外にも,前述したすべての命令には,後続のすべてのデータ・メモリ・アクセスの前,または前のすべてのデータ・メモリ・アクセスの後にメモリの読み取り/書き込みが見えるようにすることを保証する形式があります。



6.2 アプリケーションにおける不可分性への依存の検出

アプリケーションで同期が保証されると仮定している部分を検出するための 1 つの方法として,複数の実行スレッド間で共用されるデータを識別し,各スレッドからのデータ・アクセスを確認する方法があります。共用データを検出する際には,意図的に共用されるデータだけでなく,暗黙のうちに共用されるデータも検出しなければなりません。暗黙のうちに共用されるデータとは,複数の実行スレッドによってアクセスされるデータに近接しているために共用されるデータです。たとえば,$QIO,$ENQ,$GETJPI などのシステム・サービスの結果としてオペレーティング・システムが生成した AST によって書き込まれるデータは,このような暗黙のうちに共用されるデータです。

I64 システムのコンパイラは,どのデータに対してもデフォルトでクォドワード命令を使用する場合があるため,共用データが格納されているクォドワード内のすべてのデータは暗黙のうちに共用されてしまう可能性があります。たとえば,コンパイラは,自然な境界にアラインされていないデータにアクセスするためにクォドワード命令を使用します。 (アドレスがデータ・サイズで割り切れる場合には,データは自然にアラインされています。詳細は, 第 7 章 を参照してください。コンパイラは,デフォルトで,明示的に宣言されたデータを自然な境界にアラインします。)

データ・アクセスを調べる場合には,処理途中の状態のデータを別のスレッドが参照する可能性がないかどうかを判断し,このような可能性がある場合には,それがアプリケーションにとって重要な問題であるかどうかを判断してください。場合によっては,共用データの値が正確であることがそれほど重要でない場合もあります。たとえば,アプリケーションが変数の相対値だけを必要とする場合には,正確な値は必要ありません。これらを調べるために,次の事項をチェックしてください。

  • 共用データに対して実行される操作は,他の実行スレッドの観点から見たときに不可分ですか。

  • 関係するデータ型に対する不可分な操作を実行できますか。

図 6-1 はこの判断を下す過程を示しています。

図 6-1 同期に関する判断




例 6-1 のプログラムは,VAX アプリケーションで不可分性が保証されると仮定した部分を簡単に示しています。このプログラムでは,flag という変数を使用しており, AST スレッドはこの変数を通じてメイン処理スレッドと通信します。この例では,カウンタ変数が前もって定義した値に到達するまで,メイン処理ループは処理を継続します。プログラムは AST 割り込みをキューに登録します。この割り込みにより,flag に最大値が設定され,処理ループを終了します。

例 6-1 AST スレッドを含むプログラムにおける不可分な処理への依存

#include <ssdef.h> 
#include <descrip.h> 
 
#define MAX_FLAG_VAL 1500 
 
int    ast_rout(); 
long  time_val[2]; 
short int    flag;    /* accessed by main and AST threads */ 
 
main( ) 
{ 
     int      status = 0; 
     static  $DESCRIPTOR(time_desc, "0 ::1"); 
 
     /*  changes ASCII time value to binary value  */ 
 
     status = SYS$BINTIM(&time_desc, &time_val); 
 
     if ( status != SS$_NORMAL ) 
     { 
        printf("bintim failure\n"); 
        exit( status ); 
     } 
 
     /*  Set timer, queue ast */ 
 
     status = SYS$SETIMR( 0, &time_val, ast_rout, 0, 0 ); 
 
     if ( status != SS$_NORMAL ) 
     { 
        printf("setimr failure\n"); 
        exit( status ); 
     } 
 
     flag = 0;   /* loop until flag = MAX_FLAG_VAL */ 
     while( flag < MAX_FLAG_VAL )  
     { 
           printf("main thread processing (flag = %d)\n",flag); 
           flag++;              
     } 
     printf("Done\n"); 
} 
 
ast_rout()     /*  sets flag to maximum value to stop processing */ 
{ 
      flag = MAX_FLAG_VAL; 
} 

例 6-1 では,flag という名前の変数がメイン実行スレッドと AST スレッドの間で明示的に共用されます。このプログラムでは,この変数の整合性を保護するために同期メカニズムを使用していません。つまり,インクリメント操作が不可分な方法で実行されることを暗黙のうちに仮定しています。

I64 システムでは,このプログラムは常に意図したとおりに動作するわけではありません。これは, 図 6-2 に示すように,メイン実行スレッドのインクリメント操作の途中で,新しい値をメモリに書き戻す前に AST スレッドによって割り込まれる可能性があるからです (実際のアプリケーションでは,多くの AST スレッドがあるため,問題が発生する可能性がさらに高くなります)。この例では,AST スレッドはインクリメント操作が終了する前にこの操作に割り込みをかけ,変数の値として最大値を設定します。しかし,制御がメイン・スレッドに戻った後,インクリメント操作は完了し, AST スレッドの値が上書きされます。ループ・テストを実行すると,値は最大値でないため,処理ループは継続されます。

図 6-2 前の例での不可分性の仮定


対処方法

このような不可分性への依存を修正するには,次の処理を実行してください。

  • データにアクセスしている間,$SETAST システム・サービスを使用して AST の実行要求を禁止し,アクセスが終了した後で実行要求を可能にします。

  • コンパイラのメカニズムを使用して,データを明示的に保護します。たとえば,HPE C for OpenVMS I64 システムは,不可分性に関する組み込み関数をサポートしています。さらに,このデータへのアクセスの同期をとるために他のメカニズムを使用できます。たとえば,$ENQ システム・サービスを使用したり (マルチプロセッサ・システムで動作する複数のスレッドによってアクセスされるデータの場合), LIB$BBCCI や LIB$BBSSI などのランタイム・ライブラリ・ルーチンやインターロック・キュー・ルーチンを使用することもできます。
    たとえば, 例 6-1 では, C のインクリメント演算子 (flag++) によって実行されるインクリメント操作の代わりに, HPE C for OpenVMS I64 システムがサポートする不可分性に関する組み込み関数 (__ADD_ATOMIC_LONG(&flag,1,0)) を使用してください。詳しい例については 例 6-2 を参照してください。
    共用変数を不可分性に関する組み込み関数によって保護するには,これらの変数はアラインされたロングワードまたはアラインされたクォドワードでなければなりません。

  • バイト・サイズまたはワード・サイズのデータをロングワードまたはクォドワードに変更できない場合には,データをアクセスするときにコンパイラが使用する粒度を変更してください。 I64 システムの多くのコンパイラでは,特定のデータをアクセスするときやモジュール全体を処理するときに使用する粒度を指定できます。ただし,バイト粒度やワード粒度を指定すると,アプリケーションの性能が低下するおそれがあります。

例 6-2 は, 例 6-1 に示したプログラムに対してこれらの変更を行う方法を示しています。

例 6-2 前の例の同期バージョン

#include <ssdef.h> 
#include <descrip.h> 
#include <builtins.h> (1)
 
#define MAX_FLAG_VAL  1500 
int    ast_rout(); 
long  time_val[2]; 
int (2)       flag;    /* accessed by mainline and AST threads */ 
 
main( ) 
{ 
     int      status = 0; 
     static  $DESCRIPTOR(time_desc, "0 ::1"); 
 
     /*  changes ASCII time value to binary value  */ 
 
     status = SYS$BINTIM(&time_desc, &time_val); 
 
     if ( status != SS$_NORMAL ) 
     { 
        printf("bintim failure\n"); 
        exit( status ); 
     } 
 
     /*  Set timer, queue ast */ 
 
     status = SYS$SETIMR( 0, &time_val, ast_rout, 0, 0 ); 
 
     if ( status != SS$_NORMAL ) 
     { 
        printf("setimr failure\n"); 
        exit( status ); 
     } 
 
     flag = 0; 
     while( flag < MAX_FLAG_VAL )   /* perform work until flag set to zero */ 
     { 
           printf("mainline thread processing (flag = %d)\n",flag); 
           __ADD_ATOMIC_LONG(&flag,1,0); (3)
     } 
     printf("Done\n"); 
} 
 
ast_rout()     /*  sets flag to maximum value to stop processing */ 
{ 
      flag = MAX_FLAG_VAL; 
} 

以下の項目は 例 6-2 に示した番号に対応しています。

  1. HPE C for OpenVMS I64 システムの不可分性に関する組み込み関数を使用するためには, builtins.h ヘッダ・ファイルをインクルードしなければなりません。

  2. このバージョンでは,不可分なアクセスを可能にするために,変数 flag はロングワードとして宣言されています (不可分性に関する組み込み関数を使用するために必要です)。

  3. インクリメント操作は不可分性に関する組み込み関数を使用して実行されます。



6.2.2 無意識に共用されるデータの保護

例 6-1 では,2 つのスレッドはどちらも同じ変数をアクセスします。これに対しては,I64 システムでは,暗黙のうちに共用される変数に対してアプリケーションで不可分性を持たせることが可能でした。次の例では,2 つの変数はロングワードまたはクォドワードの範囲内で物理的に隣接しています。 VAX システムでは,各変数は個別に操作できます。 I64 システムでは,ロングワード・データとクォドワード・データのみ,不可分な読み取り操作と書き込み操作がサポートされるため,操作の対象となるバイトを変更する前に,ロングワード全体をフェッチしなければなりません (データ・アクセス粒度についての変更の詳細は, 第 7 章 を参照してください)。

この問題を示すために, 例 6-1 のプログラムを変更したバージョンについて考えます。このバージョンでは,メイン・スレッドと AST スレッドはそれぞれ構造体で宣言された別々のカウンタ変数をインクリメントします。カウンタ変数は次の文によって宣言されます。


struct { 
    short int     flag; 
    short int ast_flag; 
    }; 

メイン・スレッドと AST スレッドがどちらも,処理の対象となるワードを同時に変更しようとした場合には, 2 つの操作が実行されるタイミングに応じて,結果は予想できなくなります。

対処方法

同期に関するこの問題を解決するには,次のことを行ってください。

  • 共用変数のサイズをロングワードまたはクォドワードに変更します。しかし,I64 システムのコンパイラは状況によってはクォドワード命令を使用するため,データの整合性を確実に実現するためにはクォドワードを使用しなければなりません。たとえば,データが自然な境界にアラインされていない場合には,コンパイラはデータにアクセスするためにクォドワード命令を使用します。
    構造体の各要素が自然なクォドワード境界に強制的にアラインされるように,データの間にバイトを挿入することもできます。 OpenVMS I64 コンパイラは,デフォルトでデータを自然な境界にアラインします。
    たとえば,他の実行スレッドからの干渉を受けずに,構造体の各フラグ変数を確実に変更できるようにするには, 64 ビットの変数となるように変数の宣言を変更します。 C の場合は doubleデータ型を使用できます。次のコードを参照してください。


    struct { 
        double      flag; 
        double  ast_flag; 
        }; 
    

  • 不可分性に関する組み込み関数や volatile 属性などのコンパイラ・メカニズムを使用して,データを明示的に保護してください。さらに,マルチプロセッサ・システムで実行される複数の実行スレッドによるデータ・アクセスは,$ENQ システム・サービスや, LIB$BBCCI や LIB$BBSSI などのランタイム・ライブラリ・ルーチンを使用するか,インターロック・キュー操作を使用することにより同期させることができます。


目次 索引

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