2005年05月

2005年05月14日

カーネルモードでのスレッド制御

 Windowsは、カーネルディスパッチャオブジェクトを提供し、特定のスレッドを制御できるようになっている。これらのオブジェクトは、任意の時点において、シグナルノンシグナルかどちらかの状態にある。コードの実行コンテキストをブロックしてよいときは、KeWaitForSingleObjectKeWaitForMultipleObjectsかを呼び出して1つ以上のオブジェクトがシグナル状態にあるのを待つことができる。これらカーネルによって提供されたオブジェクトは5種類ある。

オブジェクト データ型 説明
イベント KEVENT ほかのスレッドによってイベントが発生したことが検出されるまでスレッドをブロックする
セマフォ KSEMAPHORE 任意の数の待ちを満足させたいときにイベントの代わりに使われる
ミューテックス KMUTEX 他のスレッドがコードの特定の部分を実行することを禁止する
タイマ KTIMER 指定した時間だけスレッド実行を遅らせる
スレッド KTHREAD 他のスレッドが終了するまでスレッドをブロックする


参考文献 1. Microsoft WDMプログラミング―WindowsXP対応 2.WindowsXPフィルタドライバプログラミング 入門と実践 3. WDMデバイスドライバプログラミング完全ガイド〈上〉 4. WDMデバイスドライバプログラミング完全ガイド〈下〉

2005年05月13日

ドライバでの同期処理

 Windowsの中で、ドライバコードはあるスレッドのコンテキストで実行される。また、マルチタスク処理の緊急性のためにドライバはほぼ任意のタイミングで制御を奪われる可能性がある。

 Windowsでは、IRQLとスピンロックを用いてドライバの同期問題を解決する。IRQLは実行の優先順位を設けて単一CPUのもとで同期メカニズムを実現し、スピンロックを使ってマルチプロセッサ環境で同期を実現する。

IRQL

 Windowsでは、ハードウェア割り込みといくつか選ばれたソフトウェア割り込みにIRQLと呼ばれる優先順位を割り当てる。x86プラットフォームにおけるIRQLの値を下図に示す。ユーザプログラムはPASSIVE_LEVELで実行され、それより高いIRQLから割り込みを受ける。


 デバイスドライバの中で、DriverEntryやAddDeviceおよび多くのディスパッチルーチンは、PASSIVEL_LEVELで実行される。StartIo、DPCルーチンなどはPASSIVE_LEVELよりも上のDISPATCH_LEVELで実行される。DISPATCH_LEVEL以上のIRQLではスレッドスイッチは発生しない。

 DISPATCH_LEVELとPROFILE_LEVELの間には、さまざまなハードウェア割り込みレベルのためのスペースがある。一般的に、割り込みを生成するデバイスは、自分のIRQLを持つ。WDMドライバは、マイナーファンクションコードがIRP_MN_START_DEVICEのIRP_MJ_PNPリクエスト受け取ったときに自分のIRQLレベルを知る。このレベルはデバイスIRQL(DIRQL)と呼ばれる。

 高いIRQLでコードを実行すると、システムはページフォルトを処理できなくなる。なので、DISPATCH_LEVEL以上で実行されるコードは、ページフォルトを起こしてはならず、ノンページメモリに格納されていなければならない。また、IRQLがあがればあがるほど、使えるカーネルモードサービスルーチンは減っていく。

 IoStartPacketで行われるIRPのキューイングなど、IRPキューにアクセスするすべてのルーチンはDISPATCH_LEVELを使うので、キュー操作途中で割り込まれることはない。このように、ドライバのニーズとシステムがドライバを呼び出すレベルが一致しているので、明示的なIRQL制御が必要になることはまれである。必要であれば、KeRaiseIrqlKeLowerIrqlを呼び出して、IRQLを制御することができる。

KIRQL oldirql;
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
KeRaiseIrql(DISPATCH_LEVEL, &oldirql);
...
KeLowerIrql(oldirql);

 IRQLをDISPATCH_LEVELにあげたいときには、専用ルーチンを使うことができる。

KIRQL oldirql = KeRaiseIrqlToDpcLevel();
...
KeLowerIrql(oldirql);

スピンロック

 スピンロックは、Windowsで定義されたプリミティブオブジェクトで、それを取得することによってメモリ変数をテスト、セットし、処理が終わるまでほかのCPUが変数にアクセスできなくすることができる。しかし、すでにスピンロックを所有しているCPUがもう一度それを取得しようとすると、デッドロックを引き起こす。また、スピンロックを待っているCPUでは意味のある仕事が行われないため、ドライバはスピンロックを保持する期間を極力抑える必要がある。

 スピンロックを使うためには、ノンページメモリにKSPIN_LOCKオブジェクトを確保してからKeInitializeSpinLockで初期化しなければいけない。
 
typedef struct _DEVICE_EXTENSION {
 ...
 KSPIN_LOCK QLock;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;

NTSTATUS AddDevice(...)
{
 ...
 PDEVICE_EXTENSION pdx = ...;
 KeInitializeSpinLock(&pdx->QLock);
 ...
}

NTSTATUS DispatchSomething(...)
{
 KIRQL oldirql;
 PDEVICE_EXTENSION pdx = ...;
 KeAcquireSpinLock(&pdx->QLock, &oldirql);
 ...
 KeReleaseSpinLock(&pdx->QLock, oldirql);
}

 スピンロックで保護されるコードは、高いIRQLで実行されるため、ノンページドメモリに配置しなければいけない。DPC,やStartIoなどすでにDISPATCH_LEVELで実行されているルーチンでは、KeAcquireSpinLockAtDpcLevelKeReleaseSpinLockFromDpcLevelが使われる。




参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉


2005年05月12日

PS/2マウス・インターフェース

 AT互換機のマウス・インターフェースとしては、シリアル・ポートを利用したシリアルマウス、専用ポートをもつPS/2マウスとUSBが最も一般的である。

マウスの歴史

 PC/ATでは、マウスが標準装備されていなかった。用途がCADやゲームなどアプリケーションが限られていたため、必要なときだけマウスをシリアルポートに接続するシリアルマウスや、ISAバスに専用拡張ボードを装着してそこに接続するバスマウスが用いられていた。

 GUIとパソコンの普及に伴い、マウスの需要が増えたため、従来のキーボードインターフェースを拡張したPS/2マウスが広く使われるようになった。

マウスインターフェース

 PS2マウスインターフェースは、キーボードコントローラの8042の空ポートを利用して拡張したもので、ハードウェア上キーボードインターフェースが2チャンネルに増設されたものとみなせる。割り込みチャンネルも、従来のキーボード割り込みIRQ1に加えて、新たにマウス割り込みとしてIRQ2 が割り当てられている。コネクタは、キーボード同様6ピンミニDIPが使われる。


参照文献

 OADGテクニカル・リファレンス
blackcat_1973 at 11:01|この記事のURLComments(0)TrackBack(0)回路設計 

2005年05月11日

PS/2キーボード・インターフェース

 AT互換機のキーボード・インターフェースは、PC本体側にキーボード・コントローラとして8ビット・マイコンの8042が内蔵され、キーボード・ユニット側にも同クラスの8ビット・マイコン(8048, 8051, 6805など)が内蔵されるケースが多い。インターフェースは、データ線(DATA)、クロック線(CLK)、5V電源、GNDの4線からなっている。

キーボードの歴史

 1984年に発売されたPC/ATには5ピンDINコネクタ(ATキーボード)のキーボードが搭載されていたが、1987年には6ピンのミニDINコネクタ(PS2キーボード)に変わった。しかし、そのどちらも実際に使われているピンは4本だけで、両者の違いはコネクタだけだった。DIN-ミニDIN変換アダプタを使って相互接続が可能である。

 コネクタに比べて、キーボードそのものの変化は大きい。初代IBM PCは84キーを利用していたが、PC/ATではファンクションキー、カーソルキーなどが増設され、101キーとなった。その後、Windowsの普及にあわせてwindowsキーやapplicationキーがさらに追加され、キー数が104キーに増えた。一方日本語キーボードは、106キー、109キーとより多くのキーが必要となる。

インターフェースの仕様

 キーボードのインターフェースは、クロック同期式のシリアルインターフェースとなっている。5V電源とGNDは、キーボードへの電源供給に使われ、データ線(DATA)とクロック線(CLK)は、パソコン側もキーボード側もが対称な双方向回路となっている。両出力ともオープン・コレクタのバッファが入っており、双方が同時に信号を出力しても壊れることはない。信号が出力されないときは、プルアップ抵抗によって信号線はHに保たれる。信号レベルはLが0.7V以下、Hが2.4V以上と規定されている。

 DATAやCLKのタイミングにはかなり幅を持たせてある。パソコンはDATA線を使ってキーボードにコマンドを書き込んだり、キーボードからデータを読み出したりできる。パソコン→キーボードの転送時は、キーボード側でDATA出力をオフ(H)にする。キーボード→パソコンの転送は、パソコン側でDATA出力をオフ(H)にする。いずれの場合もCLKはキーボード側が出力し、パソコン側はCLK出力をオフ(H)にする。ただし、パソコン側がCLK出力を強制的にLにすることによって、キーボード出力を禁止したり、転送を中止したりできる。このように、DATAとCLKの状態はステータス信号として使われている。

 キーボードインターフェースは11ビットのデータ・ストリームを使ってデータのやり取りを行う。データストリームは、1ビットのスタートビット(L)で始まり、8ビットデータ(LSB先行)、1ビットパリティ(奇数パリティ)、1ビットのストップビット(H)から構成されている。

 各キーに個別の8ビットの走査コードが割り当てられ、キーが押されたときにシステムに送られる。キーが放たれたとき、プリフィックスコード(0xF0)と該当キーの走査コードが続いた2バイト情報がシステムに送信される。

 また、キーボード側では、SHIFT+Xなど複数キーの組み合わせの認識は行わない。SHIFTキーが押されればその走査コードがパソコンに転送し、Xキーが押されればその走査コードを転送する。


参考文献

OADGテクニカル・リファレンス
blackcat_1973 at 15:07|この記事のURLComments(1)TrackBack(0)回路設計 

2005年05月10日

ドライバでの浮動小数点演算

 カーネルモードドライバでは、浮動小数点演算するのを避けた方がよいとされている。その原因は以下の通りである。

1.浮動小数点コプロセッサが搭載されていないコンピュータでは、浮動小数点演算機能がソフトウェアエミュレーションによって実現されるため、エミュレーションルーチンの起動にはプロセッサ例外が必要であり、カーネルモードドライバなど高いIRQLでの実装が難しい。

2.浮動小数点コプロセッサが搭載されたコンピュータでは、コプロセッサが加わってレジスタ数が増え、コンテキストスイッチなど割り込み発生時に必要なレジスタ値保存および復元にかかるコストが大きい。

 しかし、DISPATCH_LEVEL以下のIRQLで実行されるドライバは、浮動小数点コプロセッサを利用することが可能である。それは以下のように実現される。

ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
KFLOATING_SAVE FloatSave;
NTSTATUS status = KeSaveFloatingPointState(&FloatSave);
if (NT_SUCCESS(status)) {
 <浮動小数点の利用>
 KeRestoreFloatingPointState(&FloatSave);
}




参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉

2005年05月09日

ドライバからのファイルアクセス

 ハードウェアに大量のマイクロコードをダウンロードする場合や、詳細な情報ログを作成する場合など、ドライバから通常のディスクファイルをリード・ライトしたいときがある。その実現方法は以下の通りである。

■ファイルのオープン

 ドライバから読み出し用に既存ファイルオープンするときは、以下のようにする。

NTSTATUS status;
OBJECT_ATTRIBUTES oa;
IO_STATUS_BLOCK iostatus;
HANDLE hfile;
PUNICODE_STRING pathname;

InitializeObjectAttribute(&oa, pathname, OBJ_CASE_INSENSITIVE,
 NULL, NULL);
status = ZwCreateFile(&hfile, GENERIC_READ, &oa, &iostatus,
 NULL, 0, FILE_SHARE_READ, FILE_OPEN,
 FILE_SYNCHRONOUS_TO_NONALERT, NULL, 0);

 新しいファイルを作成したり、既存ファイルをオーブンしてサイズを0にするには、

status = ZwCreateFile(&hfile, GENERIC_WRITE, &oa, &iostatus,
 NULL, FILE_ATTRIBUTE_NORMAL, 0, FILE_OVERWRITE_IF,
 FILE_SYNCHRONOUS_TO_NONALERT, NULL, 0);

 これらのコードでは、OBJECT_ATTRIBUTES構造体をセットアップしているが、その主な目的はオープンしようとしているファイルのフルパス名を提供することである。

■ファイルの読み書き

 ドライバは、ZwCreateFileに渡すフラグ次第で同期、非同期のリード/ライトが可能となる。アクセスが完了するまで制御を返さない同期アクセスのコードは以下のようになる。

PVOID buffer;
ULONG bufsize;
status = ZwReadFile(hfile, NULL, NULL, NULL, &iostatus, buffer,
 bufsize, NULL, NULL);

または、

status = ZwWriteFile(hfile, NULL, NULL, NULL, &iostatus, buffer,
 bufsize, NULL, NULL);

 関数が制御を返したら、ファイルアクセスで転送されたバイト数がiostatus.informationに入る。

 メモリバッファにファイル全体を読み出すつもりで、ファイルサイズを調べるには関数ZwQueryInformationFileが有用である。

FILE_STANDARD_INFORMATION si;
ZwQueryInformationFile(hfile, &iostatus, &si, sizeof(si),
 FileStandardInformation);
ULONG length = si.EndOfFile.LowPart;

 WDMドライバは、通常IRP_MN_START_DEVICEリクエストが送られてきてデバイスを初期化しているときに、ディスクファイルを読み出す。安全を期するために、データファイルはシステムルートディレクトリ下の任意ディレクトリに格納し、\SystemRoot\dir\file.extなどの名前でアクセスするとよい。




参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉

2005年05月08日

ドライバからのレジストリアクセス

 Windowsでは、設定情報などの重要情報は、レジストリデータベースに記録される。

■レジストリキーのオープン

 レジストリキーを開くとき、まずはOBJECT_ATTRIBUTES構造体を初期化しなければならない。初期化はInitializeObjectAttributesマクロを利用することができる。

NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject,
 PUNICODE_STRING RegistryPath)
{
 ...
 OBJECT_ATTRIBUTES oa;

 InitializeObjectAttributes(&oa, RegistryPath, 0, NULL, NULL);
 HANDLE key;
 status = ZwOpenKey(&hkey, KEY_READ, &oa);
 if (NT_SUCCESS(status)) {
  ZwClose(hkey);
 }
}

■レジストリキーの読み書き

 以下は、ドライバのサービスキーのImagePath値を取得するコード例である。

UNICODE_STRING valname;
RtlInitUnicodeString(&valname, L"ImagePath");
size = 0;
status = ZwQueryValueKey(hkey, &valname, KeyValuePartialInformation,
  NULL, 0, &size);
if (status == STATUS_OBJECT_NAME_NOT_FOUND || size == 0)
 <エラー処理>;
PKEY_VALUE_PARTIAL_INFORMATION vpip = (PKEY_VALUE_PARTIAL_INFORMATION)
 ExAllocatePool(PagedPool, size);
if (!vpip)
 <エラー処理>;
status = ZwQueryValueKey(hkey, &valname, KeyValuePartialInformation,
 vpip, size, &size);
if (!NT_SUCCESS(status))
 <エラー処理>;

ExFreePool(vpip);

 レジストリキーの書き込みでは、関数ZwSetValueKeyが使われる。

RtlInitUnicodeString(&valname, L"TheAnswer");
ULONG value = 42;
ZwSetValueKey(hkey, &valname, 0, REG_DWORD, &value, sizeof(value));

■サブキーの削除

 レジストリサブキーの削除には、関数RtlDeleteRegistryValueかZwDeleteKeyが使われる。

■サブキーの列挙

 サブキーを列挙するには、まずZwQueryKeyで、サブキーの情報を集める必要がある。

ULONG size;
ZwQueryKey(hkey, KeyFullInformation, NULL, 0, &size);
PKEY_FULL_INFORMATION fip = (PKEY_FULL_INFORMATION)
 ExAllocatePool(PagedPool, size);
ZwQueryKey(hkey, KeyFullInformation, bip, size, &size);
for (ULONG i = 0; i < fip->SubKeys; ++i) {
 ZwEnumerateKey(hkey, i, KeyBasicInformation, NULL, 0, &size);
 PKEY_BASIC_INFORMATION bip = (PKEY_BASIC_INFORMATION)
  ExAllocatePool(PagedPool, size);
 ZwEnumerateKey(hkey, i, KeyBasicInformation, bip, size, &size);
 Nameを使って何かをする>
 ExFreePool(bip);
}




参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉

2005年05月07日

カーネルモードランタイムライブラリ

■リスト
 
 片方向リストの場合、リスト管理にPushEntryListとPopEntryListが使われる。

typedef struct _ONEWAY {
 ...
 SINGLE_LIST_ENTRY linkfield;
} ONEWAY, *PONEWAY;

SINGLE_LIST_ENTRY SingleHead;
SingleHead.Next = NULL;

PONEWAY psElement = (PONEWAY)ExAllocatePool(PagedPool,
 sizeof(ONEWAY));
PushEntryList(&SingleHead, &psElement->linkfield);

SINGLE_LIST_ENTRY psLink = PopEntryList(&SingleHead);
while (psLink) {
 psElement = CONTAINING_RECORD(psLink, ONEWAY, linkfield);
 ...
 ExFreePool(psElement);
 psLink = PopEntryList(&SingleHead);
}

 双方向リストの場合、関連関数にInitializeListHead、InsertHeadList、InsertTailList、IsListEmpty、RemoveEntryList、RemoveHeadList、RemoveTailListがある。

typedef struct _TWOWAY {
 ...
 LIST_ENTRY linkfield;
} TWOWAY, *PTWOWAY;

LIST_ENTRY DoubleHead;
InitializeListHead(&DoubleHead);
ASSERT(IsListEmpty(&DoubleHead));

PTWOWAY pdElement = (PTWOWAY)ExAllocatePool(PagedPool,
 sizeof(TWOWAY));
InsertTailList(&DoubleHead, &pdElement->linkfield);
...
if (!IsListEmpty(&DoubleHead)) {
 PLIST_ENTRY pdLink = RemoveHeadList(&DoubleHead);
 pdElement = CONTAINING_RECORD(pdLink, TWOWAY, linkfield);
 ...
 ExFreePool(pdElement);
}


■ルックアサイドリスト

 ルックアサイドリストを使用するとき、まずは、PAGED_LOOKASIDE_LISTかNPAGED_LOOKASIDE_LISTのオブジェクトをノンページドメモリから確保しなければならない。それは、ページングされるオブジェクトも、高いIRQLのもとでアクセスされる可能性があるからである。

PAGED_LOOKASIDE_LIST pagedlist;
PNPAGED_LOOKASIDE_LIST nonpagedlist;

ExInitializePagedLookasideList(pagedlist, Allocate, Free,
 0, blocksize, tag, 0);
ExInitializeNPagedLookasideList(nonpagedlist, Allocate, Free,
 0, blocksize, tag, 0);

PVOID p = ExAllocateFromPagedLookasideList(pagedlist);
PVOID q = ExAllocateFromNPagedLookasideList(nonpagedlist);

ExFreeToPagedLookasideList(pagedlist, p);
ExFreeToPagedLookasideList(nonpagedlist, q);

ExDeletePagedLookasideList(pagedlist);
ExDeleteNPagedLookasideList(nonpagedlist);




参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉


2005年05月06日

メモリ管理

 

 WDMドライバは、I/Oリクエストを送ってきたアプリケーションと同じスレッドコンテキストで実行されることはない。ドライバは、ユーザモードに属する仮想アドレスを使うことができない。

 OSは、物理メモリとスワップファイルを同じサイズのページフレームに組織する。WDMドライバは、マニフェスト定数PAGE_SIZE、またはPAGE_SHIFTからページサイズを知る。PAGE_SIZE == 1 << PAGE_SHIFTである。

 Windowsのカーネルモードアドレス空間が、ページングされるメモリプールとノンページングメモリブールに分けられ、ユーザモードアドレス空間は、常にページング可能である。Windowsシステムでは、メモリプールの使い方に原則があり、その原則が満たされたかどうかをPAGED_CODEマクロを使ってドライバのチェックビルドで調べることができる。

NTSTATUS DispatchPower(PDEVICE_OBJECT fdo, PIRP Irp)
{
 PAGED_CODE()
 ...
}

 割り込み発生時にスケジューリングされる可能性のあるISRやDPCをページングしてはならない。

■コンパイル時ページング制御

 カーネルモードドライバを含むWin32実行可能ファイルは、一つ以上のセクションから構成される。セクションごとに読み出し可能、書き込み可能、共有可能、実行可能、またはページング可能などの属性を設定できる。ドライバの一部を常駐させなければならない一方で、別の一部はページングできるときは、セクションの設定で実現できる。

 また、alloc_textプラグマを使ってドライバ内個々のサブルーチンの配置先を指定できる。

#ifdef ALLOC_PRAGMA
 #pragma alloc_text(PAGE, AddDevice)
 #pragma alloc_text(PAGE, DispatchPnp)
 ...
#endif

 データ変数の配置を制御するためには、異なるプラグマdata_segを使用する。

#ifdef ALLOC_DATA_PRAGMA
 #pragma data_seg("PAGE")
#endif
 <ページングされる静的データ変数の宣言>
#ifdef ALLOC_DATA_PRAGMA
 #pragma data_seg()
#endif

 data_segと似たプラグマcode_segを使ってコードの配置が可能である。

■ランタイムページング制御

 ランタイムページング制御に、以下の関数を使用することができる。

●MmLockPagableCodeSection
 指定されたアドレスを含むコードセクションをロックする。

●MmLockPagableDataSection
 指定されたアドレスを含むデータセクションをロックする。

●MmLockPagableSectionByHandle
 以前のMmLockPagableCodeSection呼び出しで得たハンドルを使ってコードセクションをロックする。

●MmPageEntireDriver
 ドライバに属するすべてのページのロックを解除する。

●MmResetDriverPaging
 ドライバ全体についてコンパイル時のページング属性を復元する。

●MmUnlockPagableImageSection
 ロックされたコード、データセクションのロックを解除する。

■ヒープの取得

 カーネルモードでは、関数ExAllocatePoolを使ってヒープを確保し、ExFreePoolで解放する。ExAllocatePoolを呼び出すときに決めなければならない最も基本的なことは、確保したメモリをスワップアウトできるようにすべきかどうかである。ドライバ内でDISPATCH_LEVEL以上のIRQLで動作するコードがアクセスするヒープ領域は、ノンページメモリプールから確保しなければいけない。





参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉


2005年05月05日

デバイスドライバのエラー処理

■ステータスコード

 カーネルモードサポートルーチンは、通常NTSTATUS型のステータスコードを返す。NTSTATUSは、32ビット整数であり、最上位2ビットは、報告される条件の深刻度(成功、情報、警告、エラー)を示す。カスタマフィールドは過去の遺物。昨日フィールドは、メッセージを生成したシステムコンポーネントを示し、コードフィールドは、報告しようとしている条件を指定する。

 


 ステータスコードの最上位ビットが0なら、残りのビットがどのようにセットされていても、コードは成功を示す。そのため、処理が成功か否かをチェックするときは、ステータスコードを単純に0と比較してはならない。代わりに、NT_SUCCESSマクロを使う。

■構造化例外処理

 構造化例外処理(structured exception handing)は、コンパイラのコードジェネレータと密接に統合された機能で、コードセクションにガードを配置するものである。ガードされたコードセクションがどのような形で終了しても、必ず実行されるクリーンアップ文をも提供できる。

 たとえば、MmProbeAndLockPagesProbeForReadProbeForWriteを使って、ユーザモードの仮想メモリに対して直接参照を行う場合、必ず構造化例外フレームで保護したほうがよい。

 __try文を使えば、複文を例外フレームの保護対象として指定できる。__finally文を使えば終了ハンドら、__except文を使えば例外ハンドラを指定できる。

try-finallyブロック
 この構文はクリーンアップコードを提供するために使える。「保護対象」とされる部分は、サイドエフェクトを持つサブルーチンの呼び出しである。「終了ハンドル」には、サイドエフェクトを取り消す文が含まれる。

__try {
 <保護対象>
}
__finally {
 <終了ハンドラ>
}
 コンピュータが保護対象の部分を実行する。どのような理由であれ保護対象から制御が離れたら、終了ハンドラが実行される。
 


try-exceptブロック
 「保護対象」は、例外を生成して失敗する可能性のあるコードである。

__try {
 <保護対象>
}
__except (<フィルタ式>) {
 <例外ハンドル>
}

 


 __except文で処理する例外は、EXCEPTION_EXECUTE_HANDLER、EXCEPTION_CONTINUE_SEARCH、EXCEPTION_CONTINUE_EXECUTIONとなり、
算術演算、ページフォルト、無効ポインタによる参照などは、トラップできない。

 またユーザモードアプリケーションで例外を生成するのに、RaiseExceptionを使い、カーネルモードドライバでは、ExRaiseStatusExRaiseAccessViolationExRaiseDatatypeMisalignmentが使われる。




参考文献

1. Microsoft WDMプログラミング―WindowsXP対応
2.WindowsXPフィルタドライバプログラミング 入門と実践
3. WDMデバイスドライバプログラミング完全ガイド〈上〉
4. WDMデバイスドライバプログラミング完全ガイド〈下〉

Profile
blackcat
スポンサー
訪問者数

QRコード
QRコード