0000〜01ff | カレントプロセス空間 |
0200〜03ff | 共用DLL領域 |
0400〜05ff | プロセス2 |
0600〜07ff | プロセス3 |
0800〜09ff | プロセス4 |
| | | |
4000〜41ff | プロセス32 |
4200〜7fff | ファイルシステム操作 メモリマップファイル |
8000〜9fff | 実アドレス0000〜1fff (含カーネルコード) |
a000〜bfff | 実アドレス0000〜1fff (キャッシュ無効) |
c000〜c1ff | 空き |
c200〜c3ff | プロセス97 カーネル(nk.exe) |
c400〜dfff | OEM使用領域 |
e000〜ffff | カーネル使用領域 (ページテーブル等) |
メモリマップだって簡単です。
プロセスのメモリ空間は0x0000.0000から0x01ff.ffffの32MBの範囲を占めます。
プロセススイッチで実アドレス⇔仮想アドレスの対応が変わるのはこの部分だけで、
すべてのプロセスから0x0200.0000以上のメモリ空間は同一です。
0x0200.0000〜0x03ff.ffffは共用DLL領域(CE4以降)です。
0x0400.0000〜0x41ff.ffffの空間はプロセス毎に割り当てられたメモリ空間です。
プロセス数は最大31なので、31x32MBの空間を占めます。
0x4200.0000〜0x7fff.ffffはファイルシステム操作やメモリマップファイルのための
ワーク領域です。
0x8000.0000〜0x9fff.ffffの512MBはx86の場合、システムの実アドレス0x0000.0000〜0x1fff.ffffが
そのままマップされています。
0xa000.0000〜0xbfff.ffffも実アドレスのそのままのマップですが、
こちらの領域はMMUでキャッシュが無効になっています。
つまりI/Oレジスタアクセス用です。
0xc000.0000〜0xc1ff.ffffは空き、
0xc200.0000〜0xc3ff.ffffはカーネル(nk.exe)のプロセス空間ですが、
あくまでnk.exeをプロセスとして扱うための仮想プロセス空間であり、
カーネルコードは0x8000.0000〜の実アドレス空間で直接実行されます。
StrongARMやXScaleは仮想アドレスでキャッシュします。 この方が仮想アドレス→実アドレス変換の前にミスヒットかどうか判断できるので 性能は高いのですが、 プロセススイッチの度にすべてのキャッシュをフラッシュする必要があるので 性能が低下します。 そこでStrongARMはプロセス番号レジスタ(PID)を装備しました。 これは0x0000.0000〜0x01ff.ffffをPIDに応じ 例えば0x1200.0000〜0x13ff.ffffとしてキャッシュするもので、 これによりプロセススイッチが発生する度にキャッシュをクリアする必要がなくなり、 性能が高くなります。 ただし、1プロセス空間は最大32MB、 総プロセス数は最大でも127に制限されることになります。 上記CEのメモリマップはこのあたりの事情を大きく反映しています。このようになっているため、device.exe中のデバイスドライバから、 呼び出し元プロセス中にあるメモリ領域にアクセスするのが簡単になっています。 MapPtrToProcess(p, GetCallerProcess())で 呼び出し元のメモリ空間のポインタを device.exe中からアクセスできるポインタに変換できますが、 この関数の中身はアドレスの上位7ビットを当該プロセスのものに変更するという、 極めて簡単なものです。
LinuxとかをXScaleに載せると、 プロセススイッチの度にキャッシュのフラッシュが行われるので遅いです。 が、CEでは割り込みが発生するとカーネル中のISRを経由して device.exeのIST(Interrupt Service Thread)にプロセススイッチが発生しますが、 Linuxではデバイスドライバはカーネル空間に存在し、 デバイスからの割り込みの度にプロセススイッチが発生するわけではないので、 CEよりもましになっています。 その代わりデバイスドライバはカーネル空間にあるのでそれなりに開発が難しいです。
性能向上のための手段をどのようにアプリケーションに提供するのかは デバイスドライバ開発者の手に委ねられています。 例えば非同期用のDeviceIoControlを提供し、 呼び出しはすぐにリターンして別スレッドで転送を行い、 終わったらMutexに通知するような仕組みを持ってもかまいません。 また、連続して入力を行うようなデバイスドライバでは、 入力バッファを管理し、 機器からの入力は別スレッドでこのバッファに対して行うことにより スループットを向上させることも考えられます。 あるいはデバイスドライバ側では一切このような処理を行わず、 アプリケーション側でスレッドを切って、 転送用スレッドが転送している間に別の処理を行わせることもできます。
WinCEの設計思想はあっさりしていて、 他のもので代替が利くものは実装しないようになってます。 そういう意味では昔からのしがらみがあるWin32APIよりも 洗練されたものになっており、 この非同期非サポートなんかはその最たるものだと思います。 その代わり、スレッドや同期オブジェクトといったものの扱いに精通している 必要があります。