Windows の限界に挑む: USER オブジェクトと GDI オブジェクト – 第 1 部

翻訳元: Pushing the Limits of Windows: USER and GDI Objects – Part 1 (英語)

"Windows の限界に挑む" シリーズでは、これまでに、Windows オペレーティング システム カーネルによって管理されるリソース (物理メモリと仮想メモリ、ページ プールと非ページ プール、プロセス、スレッド、ハンドルなど) を取り上げてきました。ですが、今回と次回の記事では、Windows ウィンドウ マネージャーによって管理される、ウィンドウ要素 (ウィンドウやメニューなど) とグラフィック要素 (ペン、ブラシ、描画領域など) を表す 2 つのリソース (USER オブジェクトと GDI オブジェクト) を取り上げます。以前の記事で取り上げたその他のリソースの場合と同様に、さまざまな USER リソースと GDI リソースを限界まで使い果たすと、予測できない動作が発生する可能性があります (アプリケーション エラーが発生しする、システムを使用できなくなるなど)。

いつもと同様に、この記事を読む前に今までの記事をお読みになることをお勧めします。USER リソースと GDI リソースに関する制限には、今までの記事で取り上げた制限をベースとするものもあるからです。"Windows の限界に挑む" シリーズの他の記事の一覧を以下に示します。

セッション、ウィンドウ ステーション、およびデスクトップ

USER オブジェクト、GDI オブジェクト、およびシステムの関係をより明確にする概念がいくつかあります。1 つ目はセッションです。セッションは、独自のキーボード、マウス、およびディスプレイを持つ対話型のユーザー ログオンを表し、セキュリティ境界とリソース境界の両方を表します。

セッションの概念は Windows NT 4 Terminal Server Edition のターミナル サービス (今で言うリモート デスクトップ サービス) で最初に導入されました。Windows NT 4 Terminal Server Edition のターミナル サービスでは、リモートでシステムに対話的にログオンしているユーザーごとに物理的なディスプレイ、キーボード、およびマウスの概念が仮想化されました。そして、Windows 2000 Server にはコア ターミナル サービス機能が組み込まれていました。Windows XP では、ユーザーの簡易切り替え (FUS) 機能を作成するためにセッションが利用されました。この機能を使用すると、同じ物理的なディスプレイ、キーボード、およびマウスで複数の対話型ログインを切り替えることができます。

したがって、セッションは、システムに取り付けられている物理的なディスプレイや入力デバイスと接続することも、リモート デスクトップ クライアント アプリケーションが提供するような論理的なディスプレイや入力デバイスと接続することも、また、(ユーザーの簡易切り替えを使用してセッションから離れた状態に切り替えたり、セッションをログオフせずにリモート デスクトップ クライアント接続を終了したりした場合のように、) 切断された状態にすることもできます。

すべてのプロセスは特定のセッションに一意に関連付けられており、Sysinternals の Process Explorer に Session 列を追加するとこれを確認することができます。次のスクリーンショット (親を持たないプロセスのみを示すためにプロセス ツリーを折りたたんであります) は、4 つのアクティブ セッションを持つリモート デスクトップ サービス (RDS。旧称 ターミナル サービス) システムのものです。セッション 0 は、Windows Vista およびそれ以上のバージョン上でシステム プロセスが実行されている専用のセッションです。セッション 1 は、私がこの記事の執筆を行っているセッションです。セッション 2 は、私が別のシステムから同時にログインしている別のユーザー アカウントのセッションです。そして、セッション 3 は、リモート デスクトップ サービスが次の対話型ログオンに備えて事前に作成したセッションです。

図 1

図 1

すべてのプロセスは特定のセッションに関連付けられており、オペレーティング システムは通常、現在のプロセスのセッションに固有のデータにだけアクセスできればよいので、Windows では、プロセスの仮想アドレス領域内にプロセスのセッション データのビューが定義されています。したがって、システムが異なるプロセスのスレッド間の切り替えを行うと、アドレス領域と現在のセッション ビューも切り替わります。たとえば、セッション 0 の Csrss.exe プロセスが現在のプロセスである場合は、アドレス領域マッピングには、システム アドレス領域 (すべてのプロセスのアドレス領域に含まれています)、Csrss のアドレス領域、およびセッション 0 のアドレス領域が含まれます。セッションのデータにマップされているメモリ領域は、セッション ビュー領域またはセッション領域と呼ばれます。システムがセッション 1 のエクスプローラー プロセスのスレッドに切り替えると、それに応じてマッピングも変化し、システムがメモ帳のスレッドに切り替えると、セッション 1 のセッション領域はマップされたままです (下図参照)。

図 2

図 2

この図は 32 ビットの Windows Vista およびそれ以上については厳密には正しくありません。動的なシステム アドレス領域とは、セッション領域が必ずしも連続的ではなく、こうしたシステム上で必要に応じてセッション領域を拡大および縮小できるということだからです。

2 つ目の概念はデスクトップ (英語) です。これは、デスクトップに関連付けられたウィンドウを含む仮想ディスプレイを表す、ウィンドウ マネージャーによって定義されたオブジェクトのことです (これはエクスプローラーにおけるデスクトップの定義とは異なることに注意してください。エクスプローラーにおけるデスクトップの定義は、ユーザーによって配置されるショートカットなどのオブジェクトを格納する、ユーザーのディレクトリです)。既定のデスクトップには "Default" という名前が付けられますが、アプリケーションでは、追加のデスクトップを作成して接続を論理的なディスプレイに切り替えることができます。Sysinternals の Desktops ユーティリティでは、こうした論理的なディスプレイを使用して、ユーザーが切り替えられる最大 4 つの仮想デスクトップを作成します。

3 つ目の概念は、ウィンドウ ステーション (英語) オブジェクトです。ウィンドウ マネージャーは、同じウィンドウ マネージャー インスタンスに関連付けられている複数の仮想ディスプレイをサポートするために、このオブジェクトを定義します。ウィンドウ ステーションは特定のセッションに関連付けられ、1 つのセッションは複数のウィンドウ ステーションを持つことができます。ただし、物理的または論理的なディスプレイ、キーボード、およびマウスに接続できる対話型のウィンドウ ステーション (Winsta0 と呼ばれます) は、1 つのセッションにつき 1 つだけです。その他のウィンドウ ステーションは基本的に "ヘッドレス" であり、その他のウィンドウ ステーションのサポートは、ウィンドウ マネージャー サービスを要求するが要求するべきではないプロセスを分離するためにのみ存在します。たとえば、Windows サービスはユーザー インターフェイスを表示するべきではないので、システムは、各サービス アカウント用に、そのアカウント内で実行されているプロセスを関連付ける対話型でないウィンドウ ステーションを作成します。

Sysinternals の WinObj (英語) ツールを使用して \Windows ディレクトリの下にあるオブジェクト マネージャー名前空間内を見ると、セッション 0 に関連付けられたウィンドウ ステーションを確認することができます (このディレクトリを表示するには、管理者特権で実行する必要があります)。ここでは、Microsoft Windows Search サービスが検索フィルターを実行するために作成したウィンドウ ステーション、3 つの組み込みのサービス アカウント (System、Network Service、および Local Service) それぞれのウィンドウ ステーション、およびセッション 0 の対話型のウィンドウ ステーションを確認することができます (下図参照)。

図 3

図 3

オブジェクト マネージャー名前空間の Sessions ディレクトリで、他のセッションに関連付けられたウィンドウ ステーションを確認することができます。私のログオン セッションに関連付けられている唯一のウィンドウ ステーションである対話型の WinSta0 ウィンドウ ステーションを次のように確認できます。

図 4

図 4

次の図は、システムのセッション、ウィンドウ ステーション、およびデスクトップの関係を示しています。このシステムでは、1 人のユーザーが物理的なコンソールでセッション 1 にログオンしており、別のユーザーがリモート デスクトップ接続を通じてセッション 2 にログオンしています。セッション 2 にログオンしているユーザーは、仮想デスクトップ ユーティリティを実行してディスプレイを Desktop1 に切り替えました。

図 5

図 5

プロセスは、特定のセッションに関連付けられているだけでなく、特定のウィンドウ ステーションとデスクトップにも関連付けられています。ただし、プロセスは両方の間の切り替えを行うことができ、スレッドはデスクトップ間の切り替えを行うことができます。したがって、すべてのプロセスの関連付けは、"Session 1\WinSta0\Default" のような階層型のパスで表現することができます。ほとんどの場合、Process Explorer のハンドル ビューでプロセスのハンドル テーブルを見て、そのプロセスが開いたオブジェクトの名前を確認することで、プロセスの接続先のウィンドウ ステーションとデスクトップを間接的に特定することができます。以下に示す、エクスプローラー プロセスのハンドル テーブルのスクリーンショットは、このプロセスがセッション 1 の WinSta0 の Default デスクトップに接続されていることを示しています。

図 6

図 6

USER オブジェクト

基本的な概念を理解したところで、まずは USER オブジェクトについて見ていきましょう。USER オブジェクト (英語) という名前は、このオブジェクトがデスクトップ、ウィンドウ、メニュー、カーソル、アイコン、アクセラレータ テーブル (メニューのキーボード ショートカット) などのユーザー インターフェイス要素を表すことに由来しています。USER オブジェクトは特定のデスクトップに関連付けられていますが、USER オブジェクトには、あるセッションのすべてのデスクトップからアクセスできる必要があります。たとえば、1 つのデスクトップのプロセスで、どのデスクトップでも入力できるホット キーを登録できるようにする必要があるからです。そのため、ウィンドウ マネージャーは、1 つのウィンドウ ステーションをスコープとする USER オブジェクト識別子を割り当てます。

ウィンドウ マネージャーによる基本的な制限は、どのプロセスも 10,000 個以上の USER オブジェクトを作成することはできないことです。この制限は、(オブジェクトを多数作成しすぎる可能性のあるアルゴリズムを使用してプログラミングされている、または、割り当てたオブジェクトを使い終わっても削除しないためにオブジェクトのリークが発生するという理由で) 1 つのプロセスが USER オブジェクトに関連付けられたリソースを使い果たしてしまうことを防ぐためのものです。–u スイッチを指定して Sysinternals の Testlimit (英語) ユーティリティを実行すると、この制限を簡単に確認することができます。–u スイッチを指定すると、できるだけ多くの USER オブジェクトを作成するように Testlimit に指示したことになります。

図 7

図 7

ウィンドウ マネージャーでは、プロセスによって割り当てられたオブジェクトの数を把握しておくことができるように、プロセスによって割り当てられた USER オブジェクトの数を追跡します。Process Explorer で USER Objects 列を表示項目に追加すると、この数を確認することができます。次のスクリーン ショットは、予想どおり、Lsass.exe (ローカル セキュリティ機関サブシステム) やサービス プロセス (Svchost など) を含む Windows システム プロセスはユーザー インターフェイスを持たないので USER オブジェクトを割り当てないことを示しています。

図 8

図 8

Process Explorer では、プロセスによって割り当てられた USER オブジェクトの数がプロセスのプロパティ ダイアログの [Performance] ページに表示されます (下図参照)。

図 9

図 9

USER オブジェクトの数に対する 1 つの根本的な制限は、初期のバージョンの Windows で USER オブジェクトの識別子が 16 ビット値だったことに由来しています (初期のバージョンの Windows は 16 ビットでした)。その後のバージョンで 32 ビットのサポートが追加されてからも、16 ビット プロセスが 32 ビット プロセスによって作成されたウィンドウなどの USER オブジェクトと対話できるように、USER 識別子は 16 ビット値に制限されたままでなければなりませんでした。そのため、1 つのセッションで作成できる USER オブジェクトの総数に対する制限は 65,535 (2^16) です (そして、歴史的な理由により、ウィンドウの識別子は偶数である必要があるので、作成できるウィンドウの数は 1 つのセッションにつき最大 32,768 個です)。–u スイッチを指定して Testlimit のコピーを複数 (それ以上作成できなくなる数まで) 実行することで、この制限を確認することができます。既に実行されているプロセスで使用しているオブジェクトの数が多すぎなければ、7 つのコピーを実行することができるでしょう。このうち最初の 6 つは 10,000 個のオブジェクトを割り当て、最後の 1 つは、既に割り当てられたオブジェクトの数と 65,535 との差の個数を割り当てます (下図参照)。

図 10

図 10

これを行う際は、デスクトップを使用できなくなる可能性があるので、システムの電源を強制的にオフにする用意をしておいてください。多くの操作には (たとえば、[スタート] メニューのシャットダウン メニューを開くためにも) USER オブジェクトが必要なので、USER オブジェクトをそれ以上割り当てられなくなると、システムは通常と異なる動作をします。USER オブジェクトを使い果たしてしまった後では、閉じるボタンをクリックして実行中のメモ帳プロセスを終了することすらできませんでした。

ここまでのところでは、プロセスやウィンドウ ステーションが割り当てることができる USER オブジェクトの絶対数に関する制限についてしか説明しませんでしたが、USER オブジェクト自体に使用される記憶域が原因で生じる他の制限もあります。各デスクトップは、デスクトップ ヒープと呼ばれる独自のメモリ領域を持っており、デスクトップで作成されるほとんどの USER オブジェクトにはここから割り当てが行われます。デスクトップ ヒープはセッション領域に格納され、32 ビット アドレス領域はカーネルモード アドレス領域の量を制限するので、デスクトップ ヒープのサイズには、比較的少なめの量で上限が設定されます。また、デスクトップ ヒープのサイズは、そのデスクトップ ヒープを使用するデスクトップの種類や、システムが 32 ビット システムか 64 ビット システムかによって異なります。

Matthew Justice が NTdebugging Blog で執筆した「Desktop Heap Overview (デスクトップ ヒープの概要、英語)」、「Desktop Heap, Part 2 (デスクトップ ヒープ: 第 2 部、英語)」という記事では、Windows Vista SP1 までのデスクトップ ヒープのサイズがうまくまとめられています。次の表は、Windows Server 2008 R2 までの Windows の各バージョンのサイズをまとめたものです。

  対話型のデスクトップ 対話型でないデスクトップ Winlogon デスクトップ 切断されたデスクトップ
Windows XP 32 ビット 3 MB 512 KB 128 KB 64 KB
Windows Server 2003 32 ビット 3 MB 512 KB 128 KB 64 KB
Windows Server 2003 64 ビット 20 MB 768 KB 192 KB 96 KB
Windows Vista/Windows Server 2008 32 ビット 12 MB 512 KB 128 KB 64 KB
Windows Vista/Windows Server 2008 64 ビット 20 MB 768 KB 192 KB 96 KB
Windows 7 32 ビット 12 MB 512 KB 128 KB 64 KB
Windows 7/Windows Server 2008 R2 64 ビット 20 MB 768 KB 192 KB 96 KB

Windows Vista 32 ビットの最初のリリースでは、対話型のヒープ サイズは以前の 32 ビット バージョンの Windows と同じで 3 MB でした。リリース後に、ヒープをときどき使い果たしてしまうユーザーがいることが遠隔測定によってわかったので (おそらく、より多くのメモリを持つシステムで、より多くのアプリケーションを実行していたためだと思われますが)、SP1 では、このサイズが 12 MB に引き上げられました。Matthew の記事に書かれているレジストリ設定で既定のデスクトップ ヒープ サイズを上書きすることも可能です。

Windows Vista よりも前のバージョンの Windows では、Microsoft Desktop Heap Monitor (英語) ツールを使用して、デスクトップ ヒープのサイズ、および各デスクトップ ヒープのどれくらいが使用されているかを確認することができます。32 ビットの Windows XP システム上でこのツールを実行した際の出力を以下に示します。このスクリーンショットから、対話型のデスクトップ (Default) ではヒープの 5.6% (172 KB) しか使用されていないことがわかります。

図 11

図 11

このツールは Windows Vista 用に更新されていません。新しいバージョンの Windows ではデスクトップ ヒープのサイズが大きくなったので、他の USER オブジェクトの制限に達する前にデスクトップ ヒープが使い果たされることがめったにないためです。ですが、–u スイッチと –i スイッチを指定して Testlimit を実行すると、対話型のデスクトップのヒープが使い果たされた場合にシステムがどのように動作するかを確認することができます。この 2 つのスイッチを組み合わせると、Testlimit は、4 KB の余分なクラス記憶域を持つウィンドウ クラス データ構造を失敗するまで作成します。上のスクリーンショットに示した Desktop Heap Monitor の出力をキャプチャした直後に実行した Testlimit の出力を以下に示します。2823 KB に 172 KB (Desktop Heap Monitor によって示された、割り当て済みのサイズ) を足すと、約 3 MB になります。

図 12

図 12

新しいシステムでどのくらいのヒープが使用されているかを確認することはできませんが、ヒープが使い果たされると、ウィンドウ マネージャーに関する問題のトラブルシューティングを行うのに役立つ可能性のあるイベントがウィンドウ マネージャーによってシステム イベント ログに書き込まれます (下図参照)。

図 13

図 13

これで、USER オブジェクトの制限についての説明は終わりです。第 2 部では、ウィンドウ マネージャーの GDI オブジェクトに関する制限について説明しますので、どうぞお楽しみに。

公開: 2010 年 2 月 24 日水曜日 11:24 PM 投稿者: markrussinovich (英語)

ページのトップへ

共有

ブログにコピー: ([Ctrl] + [C] でコピーしてください)