変数
Dには、複数の変数タイプ(スカラー変数、連想配列、スカラー配列および多次元スカラー配列)があります。 変数は明示的に宣言することで作成できますが、多くの場合、最初の使用時に暗黙的に作成されます。 変数は、名前の競合を回避して変数の存続期間を明示的に制御するために、節またはスレッドのスコープに制限できます。
- スカラー変数
-
スカラー変数は、整数やポインタなど、固定サイズの個別のデータ・オブジェクトを表すために使用されます。 スカラー変数は、1つ以上のプリミティブ型や複合型で構成された固定サイズのオブジェクトに使用することもできます。 Dには、オブジェクトの配列と複合構造体を作成する機能があります。 また、DTraceでは、事前定義された最大長まで拡張できるため、文字列を固定サイズのスカラーとして表します。
スカラー変数を作成するには、次の形式の代入式を記述します:
nameは任意の有効なD識別子です。expressionは変数に格納する任意の値または式です。name = expression ;DTraceには、Dプログラム内で参照できる複数の組込みスカラー変数が含まれています。 こうした変数の値は、DTraceによって自動的に移入されます。 これらの変数の完全なリストは、「DTraceの組込み変数リファレンス」を参照してください。
- 連想配列
-
連想配列は、キーを指定することで取得できるデータ要素の集合を表現するために使用します。 連想配列は、通常の固定サイズの配列とは異なり、要素数に事前定義された制限がなく、任意の式をキーとして使用できます。 さらに、連想配列内の要素は、連続した格納場所には格納されません。
連想配列を作成するには、次の形式の代入式を作成します:
name [ key ] = expression ;ここで、「名前」は任意の有効なD識別子、「キー」は1つ以上の式のカンマ区切りリスト(文字列値など)で、expressionは指定されたキーの配列に含まれる値です。
配列に格納される各オブジェクトの型も、配列内のすべての要素に対して固定されます。 「型、演算子および式」で定義されている代入演算子を使用すると、演算子ごとに定義されたオペランド・ルールに従って、連想配列の要素を変更できます。 互換性のない代入を実行しようとすると、Dコンパイラは該当するエラー・メッセージを返します。 連想配列のキーまたは値には、スカラー変数で使用可能な任意の型を使用できます。
連想配列の値は、配列名と適切なキーを指定することで参照します。
連想配列内の要素を削除するには、要素のデータ型に関係なくリテラル
0を割り当てます。 アレイ内のエレメントを削除すると、そのエレメントに使用されるストレージは割り当て解除され、システムで使用できるようになります。 - スカラー配列
-
スカラー配列は、それぞれに同じ型の値が格納される、連続する固定長のメモリーの場所のグループです。 スカラー配列にアクセスするには、0から始まる整数を使用して各場所を参照します。 スカラー配列は、Dでは連想配列ほどは多用されません。
次のように、
int型を使用して5つの整数によるDスカラー配列を宣言します。宣言には接尾辞として要素数を大カッコで囲んだものを付加します。int s[5];D式
s[0]は最初の配列要素を参照し、s[1]は2番目の配列要素を参照しています(以下同様)。 DTraceは、コンパイル時にスカラー配列の索引に対して境界チェックを実行して、不正な索引参照を早期に捕捉できるようにしています。ノート:
構文的には、スカラー配列と連想配列は類似しています。 次のように、整数によって参照される整数の連想配列を宣言できます。
int a[int];この配列は、式
a[0]を使用して参照することもできますが、記憶域と実装の観点からすると2つの配列は異なったものです。 スカラー配列のsは、ゼロから番号付けされた連続する5つのメモリーの場所で構成され、索引は配列に割り当てられた記憶域でのオフセットを示します。 ところが、連想配列aには事前定義されたサイズがなく、要素は連続したメモリーの場所に格納されません。 また、連想配列のキーは、対応する値の記憶域の場所とは関係ありません。 連想配列の要素a[0]とa[-5]にアクセスすると、DTraceによって2ワードの記憶域のみが割り当てられます。 さらに、これらの要素は連続している必要はありません。 連想配列のキーは、対応する値の抽象名であり、値の記憶域の場所とは関係ありません。初期割当てを使用して配列を作成し、単一の整数式を配列索引として使用する場合(たとえば、
a[0] = 2など)、この式aでもスカラー配列への代入として解釈できますが、Dコンパイラは常に新しい連想配列を作成します。 この場合、Dコンパイラで配列サイズの定義を認識し、配列がスカラー配列であることを推測できるように、スカラー配列を事前に宣言する必要があります。 - 多次元スカラー配列
-
Dでは、多次元のスカラー配列はあまり使用されません。これは、ANSI Cとの互換性を実現するためや、この機能を使用してCで作成されたOSデータ構造を監視およびアクセスするために提供されています。 多次元配列は、基本型の後ろに連続する一連のスカラー配列サイズを大カッコ
[]で囲んだ形式で宣言します。 たとえば、12行x34列の整数値で構成される固定サイズの2次元配列を宣言するには、次の宣言を記述します:int s[12][34];多次元スカラー配列は、同様の表記法を使用してアクセスできます。 たとえば、行
0および列1に格納されている値にアクセスするには、次のようにD式を記述します:s[0][1]多次元スカラー配列値の記憶域の場所を計算するには、行番号に、宣言された列数の合計を乗算してから、列番号を加算します。
多次元配列の構文と、連想配列アクセスのD構文を混同しないように注意してください(
s[0][1]とs[0,1]は同じでないということです)。 連想配列に互換性のないキー式を使用した場合や、スカラー配列の連想配列アクセスを試行した場合は、Dコンパイラにより該当するエラー・メッセージが報告され、プログラムのコンパイルが拒否されます。
変数のスコープ
変数スコープは、プログラム内で変数名が有効になる場所を定義して、変数の名前の競合を回避するために使用されます。 スコープ付き変数を使用すると、プログラム全体、特定のスレッドまたは特定の節に対して、変数インスタンスの可用性を制御できます。
次の表に、使用可能な3つの主要変数スコープをリストして説明します。 外部変数は、Dプログラムの制御外にある4番目のスコープを提供する点に注意してください。
| スコープ | 構文 | 初期値 | スレッド・セーフ対応 | 説明 |
|---|---|---|---|---|
|
グローバル |
|
0 |
いいえ |
任意のスレッドで起動されるプローブは、変数の同じインスタンスにアクセスします。 |
|
スレッド・ローカル |
|
0 |
はい |
スレッドで起動されるすべてのプローブは、変数のスレッド固有のインスタンスにアクセスします。 |
|
節ローカル |
|
未定義 |
はい |
プローブの特定の起動に固有の変数のインスタンスにアクセスするプローブ。 |
ノート:
次の点に注意してください。
-
スカラー変数と連想配列はグローバルなスコープのため、マルチプロセッサ・セーフ(MPセーフ)ではありません。 このような変数の値は複数のプロセッサによる変更の可能性があるため、複数のプローブが変数を変更すると、変数が破損する可能性があります。
-
独立したコピーは最終集計の前にローカルで更新され、グローバルな結果が生成されるため、集計はグローバルなスコープを持ちますが、Mpセーフです。
グローバル変数
グローバル変数は、Dプログラム全体にわたって永続する変数記憶域を宣言するために使用します。 グローバル変数は、最も広いスコープを提供します。
Dプログラムでは、連想配列を含む任意の型のグローバル変数を定義できます。 次に、グローバル変数の定義例を示します:
x = 123; /* integer value */
s = "hello"; /* string value */
a[123, 'a'] = 456; /* associative array */グローバル変数は、最初の代入時に自動的に作成され、最初の代入文の右辺に適した型を使用します。 スカラー配列を除き、グローバル変数は使用前に明示的に宣言する必要はありません。 あえて宣言を作成する場合は、プログラム節の外側に宣言を配置する必要があります。次に例を示します:
int x; /* declare int x as a global variable */
int x[unsigned long long, char];
syscall::read:entry
{
x = 123;
a[123, 'a'] = 456;
}D変数の宣言では初期値を代入することはできません。 初期値の代入には、BEGINプローブ節を使用できます。 すべてのグローバル変数の記憶域は、最初にこの変数を参照する前に、DTraceによってゼロが入力されます。
スレッド・ローカル変数
スレッド・ローカル変数は、各OSスレッドにローカルな変数記憶域を宣言するために使用します。 スレッド・ローカル変数は、プローブを有効にし、プローブを起動するすべてのスレッドにタグや他のデータでマークする場合に便利です。
スレッド・ローカル変数を参照するには、次のように、 ->演算子を特殊な識別子selfに適用します:
syscall::read:entry
{
self->read = 1;
}
このDフラグメントの例では、read()システム・コールでプローブを有効にし、readという名前のスレッド・ローカル変数をプローブを起動する各スレッドに関連付けます。 スレッド・ローカル変数は、グローバル変数と同様に、最初の代入時に自動的に作成され、最初の代入文の右辺で使用された型(この例ではint)が適用されます。
Dプログラム内でself->read変数が参照されるたびに、それに対応するDTraceプローブの起動時に実行されていたOSスレッドに関連付けられているデータ・オブジェクトが参照されます。 スレッド・ローカル変数は、システム内でスレッドのIDを示すタプルによって暗黙的に索引が付けられた連想配列とみなすことができます。 スレッドのIDは、システムの存続期間中は一意です。スレッドが終了し、同じOSのデータ構造を使用してスレッドが作成された場合、このスレッドに同じDTraceのスレッド・ローカル記憶域のIDが再利用されることはありません。
スレッド・ローカル変数を定義すると、その変数は以前に特定のスレッドに割り当てられていない場合でも、システム内の任意のスレッドで参照できます。 スレッド・ローカル変数のスレッドのコピーがまだ割り当てられていない場合は、コピーのデータ記憶域はゼロで満たされるように定義されます。 連想配列の要素と同様に、スレッド・ローカル変数の基礎となる記憶域は、この変数にゼロ以外の値が代入されるまでは割り当てられません。 また、連想配列要素の場合と同様に、スレッド・ローカル変数にゼロを割り当てると、DTraceにより基礎となる記憶域の割当てが解除されます。 使用していないスレッド・ローカル変数には、常にゼロを割り当ててください。
Dプログラムでは、連想配列を含む任意の型のスレッド・ローカル変数を定義できます。 スレッド・ローカル変数定義の例を次に示します:
self->x = 123; /* integer value */
self->s = "hello"; /* string value */
self->a[123, 'a'] = 456; /* associative array */
スレッド・ローカル変数は使用前に明示的に宣言する必要はありません。 あえて宣言を作成する場合は、キーワードselfを先頭に付けてプログラム節の外側に配置します。次に例を示します:
self int x; /* declare int x as a thread-local variable */
syscall::read:entry
{
self->x = 123;
}
スレッド・ローカル変数は、グローバル変数とは別のネームスペースに保存されるため、名前を再利用できます。 プログラム内で名前をオーバーロードする場合、xとself->xは同じ変数にならないことに注意してください。
節ローカル変数
節ローカル変数は、変数の格納をプローブの特定の起動に制限するために使用します。 節ローカルは、最も狭いスコープです。 プローブがCPUで起動すると、Dスクリプトがプログラム順に実行されます。 各節ローカル変数は、スクリプトで初めて使用されるときに、未定義の値でインスタンス化されます。 Dスクリプトがプローブの特定の起動の実行を完了するまで、変数の同じインスタンスがすべての節で使用されます。
節ローカル変数は、this->の接頭辞を付けて参照および割り当てることができます:
BEGIN
{
this->secs = timestamp / 1000000000;
...
}
節ローカル変数を使用前に明示的に宣言するには、thisキーワードを使用します:
this int x; /* an integer clause-local variable */
this char c; /* a character clause-local variable */
BEGIN
{
this->x = 123;
this->c = 'D';
}
プログラムに1つのプローブに対する複数の節が含まれている場合、節が順番に実行され同じプローブを有効にしている異なる節にわたって永続するため、節ローカル変数はそのまま残ることに注意してください。 節ローカル変数は、同じプローブを有効にしている複数の節にわたって永続しますが、指定されたプローブに対して処理される最初の節では、その値は未定義になります。 予期しない結果を回避するには、それぞれの節ローカル変数に適切な値を代入してから使用します。
節ローカル変数は、任意のスカラー変数型を使用して定義できますが、連想配列は、節ローカル・スコープを使用して定義できません。 節ローカル変数のスコープは、対応する変数データにのみ適用されます。この変数に定義された名前や型のアイデンティティには適用されません。 節ローカル変数が定義されると、それ以降のすべてのDプログラム節で、この名前と型のシグネチャを使用できます。
節ローカル変数を使用して、計算の途中結果を累積できます。また、節ローカル変数を他の変数の一時コピーとして使用することもできます。 節ローカル数には、連想配列よりも高速にアクセスできます。 このため、同じDプログラム節内で連想配列値を複数回参照する必要がある場合は、その値を最初に節ローカル変数にコピーして、ローカル変数を繰り返し参照すると、効率がよくなります。
外部変数
D言語では、特別なスコープ演算子として逆引用符(`)を使用することで、Dプログラム自体の外部にあるOSで定義されているシンボルや変数にアクセスします。
DTraceインストゥルメンテーションは、Oracle Linux OSカーネル内で実行されます。 したがって、DTraceの特別な変数およびプローブ引数にアクセスするだけでなく、カーネル・データ構造、シンボルおよび型にもアクセスできます。 DTraceのユーザー、管理者、サービス担当者およびドライバ開発者は、これらの機能を利用して、OSカーネルやデバイス・ドライバの低レベルの動作を調査できます。
たとえば、Oracle Linuxカーネルには、max_pfnという名前のシステム変数のC宣言が含まれています。 この変数は、次のように、カーネル・ソース・コード内にCで宣言されます。
unsigned long max_pfn
Dプログラムでこの変数の値をトレースするには、次のD文を記述します:
trace(`max_pfn);
DTraceでは、カーネル・シンボルに、対応するOSのCコード内のシンボルに使用されている型が関連付けられます。このため、ローカルOSのデータ構造にソース・ベースでアクセスできます。
カーネル・シンボル名は、D変数および関数識別子とは別の名前空間に保持されます。そのため、これらの名前が別のD変数名が競合することを懸念する必要はありません。 変数の前に逆引用符を付けると、Dコンパイラによって既知のカーネル・シンボルが検索され、ロードされたモジュールのリストを使用して一致する変数定義が検索されます。 Oracle Linuxカーネルでは、個別のシンボル名前空間のモジュールを動的にロードできるため、アクティブなOSカーネル内で同じ変数名を複数回使用されることがあります。 こうした名前の競合を解消するには、アクセスする変数を含むカーネル・モジュール名をシンボル名の逆引用符より前に指定します。 たとえば、次のようにして、fooという名前のカーネル・モジュールによって提供される_bar関数のアドレスを参照します:
foo`_bar
外部変数には、オペランド型の通常ルールに従い、値を変更するもの以外の任意のD演算子を適用できます。 必要に応じて、Dコンパイラはアクティブなカーネル・モジュールに対応する変数名をロードするため、これらの変数を宣言する必要はありません。 外部変数には、その値を変更するような演算子(=や+=など)は適用できません。 安全上の理由により、DTraceは監視対象のソフトウェアの状態を破損できないようにしています。
Dプログラムから外部変数へアクセスすることは、OSカーネルやそのデバイス・ドライバなどの別のプログラムの内部実装の詳細にアクセスすることを意味します。 そうした実装の詳細は、信頼できる安定したインタフェースを形成しません。 そうした詳細に依存するDプログラムを作成すると、対応するソフトウェアの次回アップグレード時に動作しなくなる可能性があります。 このため、外部変数は通常、DTraceを使用してパフォーマンスまたは機能上の問題をデバッグするために使用されます。