IOプロバイダ
ioプロバイダは、ディスク入出力に関連したプローブを使用可能にします。
たとえばioプロバイダを使用して、デバイス、I/Oの種類、I/Oサイズ、プロセス、アプリケーション名を基準にI/Oを確認できます。
ioプローブ
次の表に、ioプロバイダのプローブを示します。 すべてのioプローブについて、モジュールはvmlinuxで、関数は空の文字列です。
表9-3 ioプローブ
| プローブ | 説明 |
|---|---|
|
|
周辺機器またはNFSサーバーに対してI/Oリクエストが発行される直前に起動します。 |
|
|
I/Oリクエストが満たされた後に起動します。 doneプローブは、I/Oが完了してから完了処理がバッファ上で実行されるまでの間に起動します。 |
|
|
スレッドがI/Oリクエストの完了を待機する直前に起動します。 |
|
|
スレッドがI/Oリクエストの完了の待機を終了したときに起動します。 |
ioプローブは、周辺機器に対するI/Oリクエストが発行されたときと、NFSサーバーに対するファイルの読取り/書込みリクエストが発行されたときに起動します。 たとえば、NFSサーバーからのメタデータのリクエストは、readdir()リクエストのためにioプローブをトリガーしません。
ioプローブの引数
次の表では、ioプローブの引数について説明します。 argNは実装固有です。 args[]を使用して、プローブ引数にアクセスします。
表9-4 ioプローブの引数
| プローブ | args[0] | args[1] | args[2] |
|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ノート:
DTraceは、fileinfo_tをioプローブとともに使用するオプションを提供しません。 Oracle Linuxでは、ioプローブがI/Oリクエストが発生したファイルに関して起動するレベルでは情報にアクセスできません。
bufinfo_t
bufinfo_t構造体は、I/Oリクエストについて説明する抽象化オブジェクトです。 I/Oリクエストに対応するバッファは、start、done、wait-startおよびwait-doneプローブのargs[0]によってポイントされます。 このデータ構造体の詳細は、/usr/lib64/dtrace/version/io.dを参照してください。 bufinfo_tの定義は、次のとおりです。
typedef struct bufinfo {
int b_flags; /* flags */
size_t b_bcount; /* number of bytes */
caddr_t b_addr; /* buffer address */
uint64_t b_lblkno; /* logical block # on device */
uint64_t b_blkno; /* expanded block # on device */
size_t b_resid; /* not supported */
size_t b_bufsize; /* size of allocated buffer */
caddr_t b_iodone; /* I/O completion routine */
int b_error; /* not supported */
dev_t b_edev; /* extended device */
} bufinfo_t;
ノート:
DTraceは、カーネル・バージョンに応じて、Oracle Linux I/Oリクエスト構造に対するbuffer_headまたはbioからbufinfo_tのメンバーを変換します。
b_flagsはI/Oバッファの状態を表し、ビット単位または各種の状態値で構成されます。 次の表では、状態の値について説明します。
表9-5 b_flags値
| b_flags | 値 | 説明 |
|---|---|---|
|
|
|
I/Oリクエストが非同期であり、待機していないことを示します。
一部のI/Oから非同期とダイレクトされた場合、 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ページングされたI/Oリクエストでバッファが使用されていることを示します。 |
|
|
|
バッファがユーザー・データ領域への物理(直接)I/Oに使用されていることを示します。 |
|
|
|
データが周辺機器からメイン・メモリーへ読み込まれることを示します。 |
|
|
|
データがメイン・メモリーから周辺機器へ転送されることを示します。 |
b_bcount: I/Oリクエストの一部として転送されるバイト数です。
b_addr: I/Oリクエストの仮想アドレス(判明している場合)。
b_lblkno: デバイス上のどの論理ブロックにアクセスするかを識別します。 論理ブロックから物理ブロック(シリンダ、トラックなど)へのマッピングは、デバイスごとに定義されています。
b_blkno: デバイス上のどのブロックにアクセスするかを識別します。
b_bufsize: 割り当てられたバッファのサイズです。
b_iodone: I/Oの完了時にコールされるカーネル内の特定のルーチンを識別します。
b_edev: アクセスされたデバイスのメジャー・デバイス番号とマイナー・デバイス番号が含まれます。 Dのサブルーチンgetmajorとgetminorを使用すると、b_edevフィールドからメジャーおよびマイナーのデバイス番号を抽出できます。
devinfo_t
devinfo_t構造体は、デバイスに関する情報を提供します。 I/Oの宛先デバイスに対応するdevinfo_t構造体は、start、done、wait-startおよびwait-doneプローブでargs[1]によってポイントされます。 このデータ構造体の詳細は、/usr/lib64/dtrace/version/io.dを参照してください。 devinfo_tの定義は、次のとおりです。
typedef struct devinfo {
int dev_major; /* major number */
int dev_minor; /* minor number */
int dev_instance; /* not supported */
string dev_name; /* name of device */
string dev_statname; /* name of device + instance/minor */
string dev_pathname; /* pathname of device */
} devinfo_t;
ノート:
DTraceは、devinfo_tのメンバーを、Oracle Linux I/Oリクエスト構造体のbuffer_headから変換します。
dev_major: デバイスのメジャー番号です。
dev_minor: デバイスのマイナー番号です。
dev_name: デバイスを管理するデバイス・ドライバの名前です。
dev_statname: iostatによってレポートされるデバイスの名前です。 このフィールドは、異常なiostatの出力を実際のI/Oアクティビティに迅速に対応付けるために提供されています。
dev_pathname: デバイスのフルパスです。 dev_pathnameで指定されたパスには、デバイス・ノード、インスタンス番号およびマイナー・ノードを表すコンポーネントが含まれます。 ただし、これらの3つの要素はすべて、必ずしも統計名で表されるわけではありません。 デバイスによっては、統計名がデバイス名とインスタンス番号で構成されている場合もあります。 あるいは、デバイス名とマイナー・ノード番号で構成されている場合もあります。 したがって、同じdev_statname mighを持つ2つのデバイスは、dev_pathnameで異なります。
fileinfo_t
ノート:
Oracle Linuxでは、ioプローブのfileinfo_t引数args[2]はサポートされていません。 ただし、fileinfo_t構造体を使用すると、組込み変数fds[]配列を使用して、プロセスのオープン・ファイルに関する情報を取得できます。
fileinfo_t構造体は、ファイルに関する情報を提供します。 ファイル情報が存在するかどうかは、I/Oリクエストをディスパッチするときにこの情報を提供するファイル・システムによって決まります。 一部のファイル・システム、特にサードパーティのファイル・システムには、この情報を提供しないものもあります。 また、I/Oリクエストの発行元であるファイル・システムにファイル情報が存在しない場合もあります。 たとえば、ファイル・システム・メタデータとの間のI/Oは、いずれのファイルにも関連付けられません。 さらに、高度に最適化されたファイル・システムは、複数の無関係なファイルからのI/Oを集積して、単一のI/Oリクエストを生成することがあります。 この場合、ファイル・システムは、ほとんどのI/Oを表すファイルまたは一部のI/Oを表すファイルのファイル情報を提供することがあります。 または、この場合はファイルシステムがファイル情報をまったく提供しない可能性があります。
このデータ構造体の詳細は、/usr/lib64/dtrace/version/io.dを参照してください。 fileinfo_tの定義は、次のとおりです。
typedef struct fileinfo {
string fi_name; /* basename */
string fi_dirname; /* not supported */
string fi_pathname; /* not supported */
loff_t fi_offset; /* offset within file */
string fi_fs; /* file system */
string fi_mount; /* not supported */
int fi_oflags; /* open() flags for file descriptor */
} fileinfo_t;
fi_nameフィールドにはファイルの名前が含まれますが、ディレクトリ・コンポーネントは含まれません。 ファイル情報がI/Oに関連付けられていない場合、fi_nameフィールドは文字列<none>に設定されます。 まれに、ファイルに関連付けられているパス名が不明になることがあります。 この場合、fi_nameフィールドは文字列<unknown>に設定されます。
fi_dirnameフィールドには、ファイル名のディレクトリ・コンポーネントのみが含まれています。 fi_nameと同様に、この文字列は、ファイル情報が存在しない場合は<none>、ファイルに関連付けられているパス名が不明な場合は<unknown>に設定できます。
fi_pathnameフィールドには、ファイルに対するフル・パス名が含まれています。 fi_nameと同様に、この文字列は、ファイル情報が存在しない場合は<none>、ファイルに関連付けられているパス名が不明な場合は<unknown>に設定できます。
fi_offsetフィールドには、ファイル内のオフセット、またはファイル情報が存在しない場合、またはオフセットがファイル・システムによって指定されていない場合、-1が含まれます。
fi_fsフィールドには、ファイル・システムのタイプ、または情報がない場合は<none>,が含まれています。
fi_oflagsフィールドには、ファイルを開くときに指定されたフラグが含まれています。
ioの例
次のサンプル・スクリプトは、発行されるすべてのI/Oの情報を表示します。 次のソース・コードを入力し、iosnoop.dという名前のファイルに保存します。
#pragma D option quiet
BEGIN
{
printf("%10s %2s\n", "DEVICE", "RW");
}
io:::start
{
printf("%10s %2s\n", args[1]->dev_statname,
args[0]->b_flags & B_READ ? "R" : "W");
}
このスクリプトからの出力は、次のようになります:
DEVICE RW
dm-00 R
dm-00 R
dm-00 R
dm-00 R
dm-00 R
dm-00 R
...
次の例に示すように、連想配列を使用して各I/Oに要した時間(ミリ秒)を追跡することによって、スクリプト例を少し高度にすることができます:
#pragma D option quiet
BEGIN
{
printf("%10s %2s %7s\n", "DEVICE", "RW", "MS");
}
io:::start
{
start[args[0]->b_edev, args[0]->b_blkno] = timestamp;
}
io:::done
/start[args[0]->b_edev, args[0]->b_blkno]/
{
this->elapsed = timestamp - start[args[0]->b_edev, args[0]->b_blkno];
printf("%10s %2s %3d.%03d\n", args[1]->dev_statname,
args[0]->b_flags & B_READ ? "R" : "W",
this->elapsed / 10000000, (this->elapsed / 1000) % 1000);
start[args[0]->b_edev, args[0]->b_blkno] = 0;
}
変更されたスクリプトは、出力にMS (ミリ秒)列を追加します。
次の例に示すように、デバイス、アプリケーション、プロセスIDおよび転送されたバイトを集計し、whoio.dという名前のファイルに保存できます:
#pragma D option quiet
io:::start
{
@[args[1]->dev_statname, execname, pid] = sum(args[0]->b_bcount);
}
END
{
printf("%10s %20s %10s %15s\n", "DEVICE", "APP", "PID", "BYTES");
printa("%10s %20s %10d %15@d\n", @);
}
このスクリプトを数秒間実行すると、次のような出力になります:
DEVICE APP PID BYTES
dm-00 evince 14759 16384
dm-00 flush-252:0 1367 45056
dm-00 bash 14758 131072
dm-00 gvfsd-metadata 2787 135168
dm-00 evince 14758 139264
dm-00 evince 14338 151552
dm-00 jbd2/dm-0-8 390 356352
あるデバイスから別のデバイスにデータをコピーする場合は、いずれかのデバイスがコピーのリミッタとして機能するかどうかを知りたい場合があります。 この質問に回答するには、各デバイスで転送される1秒当たりのバイト数ではなく、各デバイスの有効なスループットを知る必要があります。 試験では、次のスクリプトを使用してスループットをcopy.dという名前のファイルに保存することで、スループットを確認できます:
#pragma D option quiet
io:::start
{
start[args[0]->b_edev, args[0]->b_blkno] = timestamp;
}
io:::done
/start[args[0]->b_edev, args[0]->b_blkno]/
{
/*
* We want to get an idea of our throughput to this device in KB/sec.
* What we have, however, is nanoseconds and bytes. That is we want
* to calculate:
*
* bytes / 1024
* ------------------------
* nanoseconds / 1000000000
*
* But we cannot calculate this using integer arithmetic without losing
* precision (the denominator, for one, is between 0 and 1 for nearly
* all I/Os). So we restate the fraction, and cancel:
*
* bytes 1000000000 bytes 976562
* --------- * ------------- = --------- * -------------
* 1024 nanoseconds 1 nanoseconds
*
* This is easy to calculate using integer arithmetic.
*/
this->elapsed = timestamp - start[args[0]->b_edev, args[0]->b_blkno];
@[args[1]->dev_statname, args[1]->dev_pathname] =
quantize((args[0]->b_bcount * 976562) / this->elapsed);
start[args[0]->b_edev, args[0]->b_blkno] = 0;
}
END
{
printa(" %s (%s)\n%@d\n", @);
}
ハード・ディスクからUSBドライブにデータをコピーする際に、前のスクリプトを数秒間実行すると、次のような出力が得られます:
sdc1 (/dev/sdc1)
value ------------- Distribution ------------- count
32 | 0
64 | 3
128 | 1
256 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 2257
512 | 1
1024 | 0
dm-00 (/dev/dm-00)
value ------------- Distribution ------------- count
128 | 0
256 | 1
512 | 0
1024 | 2
2048 | 0
4096 | 2
8192 |@@@@@@@@@@@@@@@@@@ 172
16384 |@@@@@ 52
32768 |@@@@@@@@@@@ 108
65536 |@@@ 34
131072 | 0
前述の出力は、USBドライブ(sdc1)がデバイスの制限を明確にしていることを示しています。 sdc1のスループットは256K/秒から512K/秒の間で、dm-00は8MB/秒から64MB/秒超までのいずれかでI/Oを提供しています。