たまたま別の事を調べていたときに、「Mac on PC(笑)」なるエントリを見かけて、ああ、もう DTK の事は忘却の彼方なのね、と思ったので少々昔話を残しておく。
なぜなら、先の記事には
『残念ながらVirtualBoxで読み込んで動きませんでしたが、
これを作ったハッカーの人々はすごいものです。』
とあるが、そんなものではなく単に DTK から盗みだしたものだから、っていうのが答えだからだ。
当時 Apple は PowerPC をCPUとする Mac を販売しており、OS X というOS もその上で動くアプリケーションも PowerPC 向けにビルドされていた。
WWDC 2005 にて、 Apple は Intel CPU への移行を宣言、CPUアーキテクチャの変更を強行、成功裏に終わらせたのは知っての通りだ。
( NeXT屋としては Intel Transition は2度目で、「またか」って気分になったのだが...さておき )
円滑な移行の支援のため、 Apple は開発者向けに Intel CPU 搭載の Mac を先行配布をおこなった、これが DTK、 Developer Transition Kit だ。
申し込みは先の WWDC2005 で行われており、その場にいたので申し込むかかなり迷ったのだが、有償(たしか$1000ほど)で、かつ「貸与」で返却が必要だった事からあきらめた記憶がある。
後に、DTKは Intel CPU を搭載した初代 iMac 17inch と無償交換となった。このマシンは $1299 で販売されたので、DTKを申し込んだ方が得だったのだが...まあ、それも後から見ての話。
さて、このDTKだが、実は後の Intel Mac とはアーキテクチャが大きく異なっていた。
Intel Mac は初代が Core Duo, 二代目以降が Core2 Duo で、インテルCoreアーキテクチャを採用した、Core世代前提のハードウェアとなっている。
( 厳密に言うなら、初代の Core Duo 、すなわち Yonah は Coreアーキテクチャではなく、Snow Leopard(10.6.x)までしかサポートされないとか、その次もしばらくは 32bit EFI の制約があり 64bit Kernel オンリーになった Mountain Lion (10.7.x)に移行できない、など移行期のごたごたがあり、ハードウェアが固まったのは 2008年頃からではあるが。)
DTK の Mac は、PowerMac G5 の巨大なアルミ筐体の中に Pentium4 をCPUとする普通のPCのマザーボードを突っ込んだという、なんとも強引なものだった。
そう、この DTK は、EFI ではなく、PC-BIOS をファームウェアとしているのだ。
DTK Mac の貴重な写真がこちらにあるので、見ていただければと思う。
正式にリリースされた Intel CPU向け OS X は EFIでしか起動せず、さらに HFS Plus を読み込める必要がある。なぜなら、OS X のブートローダは EFIシステムパーティションだけにあるとは限らず、HFS Plus のファイルシステム中にもあるからだ。(/System/Library/CoreServices/boot.efi 。むしろこちらがデフォルトか。)
Apple は Mac に搭載した EFI には HFS Plus 向けのドライバーを搭載しているが、普通の EFI にはその手のドライバーは搭載されていない。
(なお、EFI に HFS Plus ドライバを加える事自体は悪い事ではない。EFI はその名の通り拡張可能なファームウェアであり、そうしたベンダーごと必要な機能を加えやすいことが特徴だからだ。ただ、boot.efi とを ファームウェアにも EFIシステムパーティションにも配置しなかった事については行儀がよくないなぁとは思うが。)
しかし、DTKは普通のPCであるため、通常のBIOSからのブートローダを保持していた。
これは Darwin の一部としても配布されていた。Darwin 単体での配布はこれまた 2005年頃に終了しているが、Intel PC上で動作させるため、BIOSからのブートローダを持っていたのだ。
ここまででおわかりだろう、普通のPCで動作する OS X 10.4.x がなぜ存在するか、それは 当時の Darwin のソースコードからブートローダを持ってきたか、あるいは DTK のブートローダをコピーしたか、なのだ。
余談。
なお、この DTK、ハードウェアだけではなく搭載されていた OS X にもいろいろ違いが見られる。
顕著な点としてはメモリマップがあげられる。DTKに搭載されていた OS X 10.4 では仮想メモリのアドレス空間が 1G/3G に分割されていた。32bit の Linux や /3G スイッチを有効にした 32bit の Windows と同じく、プロセスに割り当てられる 4GBの仮想メモリのうち、プロセスが本当に扱う事ができるのは 3G までで、残り1GBはカーネルに固定的に割り当てられていた。
※ 仮想メモリのアドレス空間とは、要するに「アプリから見えるメモリ構成」であり、それに対して物理メモリのアドレス空間は、「実際のメモリとか、PCIバスのデバイスがどこのアドレスに割り当てられているか」を意味する。
別に仮想メモリの 4GB全てをユーザ空間に割り当てる事も不可能ではないのだが、すると、システムコールを発行しカーネルに処理が写る瞬間に、4GBの仮想メモリのレイアウト(実際にどこの物理メモリと対応づけられているか)が全て入れ替わる。システムコールの引数など、ユーザ空間にあるメモリ内容を参照するにはいったんそれをカーネル側の4GBのメモリ空間にコピーする必要があり、システムコールごとの手間がかかるし、何よりメモリマップをごっそり変えてしまうと、CPUのTLBが全て無効化され、メモリのアクセス性能ががたんと落ちてしまう。プロセス切り替えである程度TLBが吹っ飛ぶのは仕方ないとしても、システムコールごとに吹っ飛ばされてはたまったものではないだろう。
※ TLBとは、要するに仮想メモリと物理メモリの対応付けのキャッシュである。歩かそうメモリの内容が、実際に物理のメモリのどこに配置されているかはメモリ上に対応表が存在し、これを参照するが、対応表自体が非常に大きいため、TLBでキャッシュをかけて速度を稼いでいる訳だ。
そこで、32bit のメジャーなOSは、仮想メモリ空間のうち、上位(大きい方から)1GBをカーネル空間に割り当て、残り3GBをユーザ空間に割り当てている。システムコールを発行してカーネル側に処理が切り替わっても、仮想メモリ上の配置は変わらない。TLB が吹っ飛ぶ事もないし、カーネル側からは3GBのユーザ空間を自由にアクセスできるので、システムコールごとの情報の受け渡しも楽になる。
※ もちろんCPUが実行するプロセスが切り替わるとTLBの内容は吹っ飛ぶが、その場合でもカーネルの1GBは共通のため全滅にはならない
その代わり、アプリケーションから見ると上位1GBは「アクセス禁止」となり、3GBまでのメモリしか使えない。
/3G スイッチを入れて起動していない、通常の 32bit Windows の場合は、2G:2Gで分けられており、ユーザプロセスは2GBまでのメモリしか使えず、さらに窮屈な思いになる。
1GBでも貴重なため解放したのが /3G スイッチであり、またアクセスできる 3GBのアドレス空間中の一部を必要に応じて差し替えることで、3GB以上のメモリを使えるようにする、PAE という拡張も用意された。
(要はバンクメモリとかEMSと同じ発想ですよ、PAEって...。 )
ところが、2006年に実際にリリースされた OS Xでは、4G:4G になっている。
そう、ユーザプロセスは4GBの仮想メモリのアドレス空間がほぼ全て使える。最上位 2MBだけはカーネル空間とされ、トラップテーブルなどが用意されている。
こうなったということ自体は Apple の開発者から説明を受けたが、なぜそうしたかの理由はされなかった。おそらく、次に書くように部分的な 64bit 対応がすぐそこに迫っていたため、そちらで解決されるという見込みだったのだろう。
素人考えではその後に出てきた初代 iMac より DTKのアーキテクチャの方がTLBが吹っ飛ばない分性能が良さそうに感じるが、「そんなことよりデバイスドライバのデキの方が性能に影響するだろ」とか、「Pentium4 で動かしても仕方ないだろ」で、さして速度差はなかったかもしれない。
どちらにせよ、$1000に迷って DTKに手を出さなかったのは未だにちょっとした後悔にはなってます、ええ。
● さらに余談
先の完全32bit の OS X は、実は CoreDuo を搭載した初代 Intel Mac でしか起こりえない。
二世代目の、Core2 Duo を搭載した Intel Mac は Intel64(EM64T)対応で、64bitアーキテクチャだったからだ。
Apple はそこに目をつけ、32bit カーネルだが、一部を 64bit で動作させ、(物理、仮想ともに)4GBのメモリ制限を回避するなどの荒技に出たのだ。
OS X のブートローダは CPU が Intel64(EM64T とか x86_64 か呼ばれる)を検出すると、IA-32e モードに切り替えた後に、32bit のカーネルを起動する。
IA-32e モードでは「互換モード」と「ロングモード」の二つのモードがあり、互換モードでは従来の x86のコードのほとんどを動作させる事ができ、ロングモードでは64bit のコードを実行する事ができる。IA-32eモードにさえ切り替えておけば、この二つのモードは高速に切り替える事ができる。
Apple はこの点に注目し、カーネルのほとんどの部分とデバイスドライバは 32bit で動作させつつ、メモリ管理と先のトランポリン部分は 64bit で動かすようにした。
さて、32bitアプリケーションはユーザ空間で 4GB(-2MB)の仮想メモリを使う事ができる。システムコールを発した瞬間に、カーネルに処理を切り替えると同時にロングモードに切り替えが行われる。すると、64bit の仮想メモリのアドレス空間に切り替わる。これまで先頭4GBしか見えてなかったのが、いきなり広がる訳だ。
膨大なメモリ空間の最上位512GB がトランポリンとなり、ここでロングモードのまま必要な処理を行った後に、互換モードに切り替え 32bit のカーネル空間に飛び込む。
また、OS X では 32bit カーネルでも 64bitアプリケーションを実行する事ができた。
(10.4 では Libc など一部の非GUIライブラリまでが 64bit に対応、10.5 で Cocoa/Carbon などGUIを含むフレームワークが 64bit 対応して、64bit アプリケーションが利用可能となった)
64bitアプリケーションの場合、ユーザ空間上のアプリケーションは64bitの仮想メモリ空間中、128TB(-4GB)までの仮想メモリを扱う事ができる。128TB以上は予約空間とされ、アクセスできない。また、32bit プロセスで使われていた 下位4GBのメモリは「Page0」とされ、すべて0で埋め尽くされ使えないメモリ空間、ということにされている。こちらもアクセスできない、したらSEGVとなる。
※ なぜ128TBとかというと、実際の Intel64 では、64bitモードでも物理アドレスが40〜52bit、仮想アドレスも48bit に制限されている。そしてこの仮想メモリ48bit、つまり 256TBのメモリ空間は連続ではなく、上位128TBと下位128TBが利用可能となっている。将来的に 56bit〜64bit のメモリ空間が利用可能に拡張されたときにも、問題がないようにあえて真ん中に利用禁止区間を作っている訳だ。な訳で、アドレスとしては 64bit で、うち下位128TBがユーザ用、上位128TBがカーネル用と想定されている。
そして、システムコールを呼ぶと、64bit アドレス空間で最上位から 512GBの空間のトランポリンに処理がうつるまでは 32bitアプリと同じ。ただし、このときに互換モードからロングモードへの切り替わりが必要ないのでその点は効率がよくなる。
そこから処理が進むと、互換モードに切り替わり、下位4GBのアドレス空間に存在する 32bitカーネルに処理が飛び込む。
これによって、初代 Intel Mac は物理的な制約もあり 2GB〜3GBしかメモリが搭載できなかったのが、2006年頃の Mac では 16GBまで搭載できるのが現れ、2008年には 64GBまで搭載可能となってきた。
※ Intel64 自体は 52bit までの物理アドレスに対応しているが、初期のCPUでは 40bit で1TBまでのメモリしか対応してなかった。これは少しずつ拡張されてきており、たとえば現在の Xeon E7 v2 2800/4800/8800 では 46bit のアドレッシングに対応、つまり 64TB までは使えるようになっている。
このような阿漕な事をしない、言い換えれば素直な設計とも言える Windows や Linux では、(PAE を使わない限り) 4GBまでの物理アドレス空間しか扱えず、そこからPCIバスのマップなどハードウェア側の予約分(512MB)をさっ引いた、3.6GB 程度しかRAMを搭載できない。
※ PAEでのメモリ管理の方法と、Intel64 での管理の方法は互換性があり、互換モードでPAE対応のアプリケーションを使って 4GB以上のメモリを使う、と言う事もできる。
より多くのメモリを利用するためにも、64bit対応の Windows が必要になった訳だ。
64bit の Windows は IA-32eモードで起動、通常はロングモードで実行しつつ、アプリケーションの実行に関しては互換モードを利用する。その間 Win32APIの呼び出しなどについては、Wow64という仕組みで対応している。
...単に、DTKと PC-BIOS, EFi の話をするだけのつもりが、余談が非常に長くなってしまった...。