杉本 拓也 松井 俊訓 杉本 拓也(すぎもと たくや/左)
松井 俊訓(まつい としのり/右)
株式会社富士通ソフトウェアテクノロジーズ


 情報システムを開発するときは、OSの内部構造や細かな動作を意識することはあまりありません。それに対して組み込みシステムはOSと密接に関連しているため、OSのアーキテクチャの知識なしに組み込み開発はできません。

 前回は、Windows Embedded CE 6.0がコンポーネント形式のアーキテクチャを採用していること、そしてメモリ空間がユーザースペースとカーネルスペースの2つに分かれていることを説明しました。

 さらに今回から4回にわたって、Windows Embedded CE 6.0のアーキテクチャを理解するうえで重要な「メモリモデル」「スケジューラ」「同期オブジェクト」「割り込み」について説明します。まず今回はメモリモデルです。

アプリケーションがクラッシュしてもカーネルに影響しない

 Windows Embedded CE 6.0は、以前のバージョンに比べ、カーネルのアーキテクチャが大幅に変更されました。中でも大きく変わったのは、メモリモデルです。これまでのバージョンのメモリ管理モデルとは異なり、Windows Embedded CE 6.0 ではカーネルスペースとユーザースペースが完全に分離されました。

 これによる利点は、セキュリティが大きく強化されたことです。ユーザースペースで実行されるアプリケーションが、カーネルスペースで動作する重要なコンポーネントやリソースから分離されてアクセスできなくなったことにより、強固なセキュリティモデルが実現されました。

 Windows CE 5.0までは、ユーザースペースのアプリケーションが自身をカーネルスペースで動作するような宣言ができました。これによって、CE 5.0のアプリケーションがカーネルスペースで動作して、さまざまな権限を取得することができました。

 しかしWindows Embedded CE 6.0からは、ユーザースペースのアプリケーションが自身をカーネル空間に移動することはもとより、物理メモリへの直接アクセスも制限されています。これによって、たとえアプリケーションに不具合があってもOSに重大な影響を与えることがなくなり、セキュリティと安定性が向上しました。

4GBの仮想メモリ空間と最大32,000のプロセス

 このユーザースペースとカーネルスペースは、図1のように、32ビットでアクセスできる仮想メモリ空間(全4GB)に配置されます。上位2GBがカーネルスペース、下位2GBがユーザースペースです。カーネルスペースにはカーネル、デバイスマネージャ、GWES、カーネルモードドライバが配置され、ユーザースペースにはアプリケーションや、ユーザーモードドライバ、サービスマネージャなどのDLLが配置されます。

図1●Windows Embedded CE 6.0のメモリモデル
図1●Windows Embedded CE 6.0のメモリモデル

  • カーネルスペース

     カーネルスペースの上位1GBの領域は、カーネルの他にカーネルプロセスにロードされるGWES、カーネルモードドライバ、FILESYSなどのDLLや、オブジェクトストアが配置されます。その他、割り込みベクタなどCPUに依存する処理で利用する領域もカーネルスペースに配置されます。

     カーネルスペースの下位1GBの領域には、物理メモリを仮想メモリへ静的にマップする領域が用意されています。キャッシュ有効領域とキャッシュ無効領域は各512MB配置され、ここにROMやRAMやメモリマップファイルの領域が静的にマッピングされます。カーネルスペースに静的にマッピングしたいメモリ領域の情報は、OSの起動時にアドレスのリストとしてカーネルに渡されます。このリストに格納されているのは、マッピングしたい物理メモリのアドレスとキャッシュ有効領域のマッピング先仮想メモリアドレス、およびマッピングするサイズの情報です。

  • ユーザースペース

     一方、ユーザースペースは下位2GBの領域を利用します。ユーザープロセスは最大32,000個まで起動できますが、これらのユーザープロセスはすべて、ユーザースペースで動作します。

     2GBの領域の上位1GBにあるのは、プロセス共有メモリ領域です。仮想メモリアドレス0x7000_0000~0x7FFF_FFFFのシェアード(共有)ヒープは、カーネルプロセスとユーザープロセス間の共有メモリとして用意された領域です。また、仮想メモリアドレス0x6000_0000 ~ 0x6FFF_FFFFのRAM Backed Mapfiles領域は、RAM上に配置されたメモリマップファイルの領域です。0x4000_0000 ~0x5FFF_FFFFには、ユーザープロセスによってロードされたDLLが配置されます。DLLのコードもデータも、この領域に配置されます。複数のユーザープロセスにロードされるDLLのコードは、すべてのプロセスで共有されます。つまり、各プロセスから参照するコードは同じ物理メモリ上に存在します。それに対して、データはプロセスごとに存在します。残りの0x0000_0000~0x3FFF_FFFFは、ユーザープロセスのコードやデータなどに使われます。

  • ヒープとスタック

     仮想メモリ上の領域には、プログラム内で確保された変数や、ダイナミックに割り当てられたバッファなどが配置されます。これらは通常、ヒープメモリまたはスタックメモリとして割り当てられます。

     プロセスを起動すると、デフォルトのヒープが自動的に作成されます。アプリケーションではHeapAllocというAPIを利用して、このデフォルトヒープからヒープ領域を確保します。アプリケーション処理を続行すると、ヒープ領域でフラグメンテーション(断片化)が発生する可能性があり、これが発生すると、ヒープ内に空き領域があっても、サイズの大きな領域を確保することができません。デフォルトヒープはプロセスがロードしているライブラリからも利用されるため、断片化が起こりやすい傾向があります。デフォルトヒープの断片化を回避するには、CreateHeapというAPIによってプライベートヒープを作成してください。

     またヒープには、カーネルプロセスとユーザープロセス間の効率的な情報伝達のためにシェアード(共有)ヒープが用意されています。これは仮想メモリ上のユーザースペースの上位に確保された領域で、カーネルプロセスからはRead/Writeアクセスができ、ユーザープロセスからはReadアクセスのみ可能です。シェアードヒープは、すべてのユーザープロセスから参照できます。

     一方、スタックはスレッドが生成されたときに確保されるメモリ領域です。ローカル変数はこの領域に配置されます。Windows Embedded CE 6.0はデフォルトで64KBのスタックが確保されます。

     図2は、プロセスのデフォルト(ローカル)ヒープとプライベートヒープ、およびメインスレッドや追加したスレッドが利用するスタック領域がユーザースペース内でどのように配置されるかを示したものです。プロセスやスレッドで使用するローカルのヒープやスタックは、ユーザースペースの下位1GB内に配置され、シェアードヒープは上位1GBの共有メモリ内に配置されます。

    図2●ヒープとスタックの配置例
    図2●ヒープとスタックの配置例