Windows 95はなぜ高速なCPUでは起動に失敗することがあるのか?
by Yining Zhang
1995年にリリースされたWindows 95では、高速なCPUで動作させると起動に失敗するという問題がよく知られており、AMD製CPU「K6-2」に対してはパッチで対策が取られていました。そんな「Windows 95起動失敗問題」について、1987年から1997年にかけての古いPCやソフトウェアの情報を扱う「OS/2 Museum」のメディアマスターであるMichal Necasek氏が原因を解明し、そこから学べる教訓を語っています。
http://www.os2museum.com/wp/those-win9x-crashes-on-fast-machines/
Necasek氏は知り合いから「Windows for Workgroups 3.11が仮想マシンで起動しない」という話を聞いたとのこと。Intel製CPUである「Core i7-2600」ではまれに起動に失敗し、AMDの「Ryzen 7 3800X」では毎回失敗するという状況でした。
Necasek氏がOSのカーネルをデバッグしてみると、ゼロによる除算が発生し、システムをダウンさせていることが判明。さらに詳しく調査すると、ネットワーク機能にアクセスするためのプログラムであるNDIS内部で、下記の処理が行われていることが原因だったとのこと。
1:現在の時刻を取得する
2:LOOP命令を104万8576回実行する
3:実行後に時刻を再度取得し、1で取得した時刻との差を計算する
4:104万8576回を計算した差分で除算することで、ミリ秒あたりのLOOP命令実行回数を計算する
一見問題ない処理に見えますが、現在の時刻はミリ秒単位でしか取得できない点が問題だとNecasek氏は指摘。もしLOOP命令を104万8576回実行するのに1ミリ秒かからない「高速なCPU」で処理を行った場合は、1と3で取得する時刻の差分がゼロになってしまうので、ゼロによる除算が発生してしまうというわけ。
Windows 9x系が開発されていた当時、CPUのクロック周波数の上昇はすさまじかったとのこと。1993年にリリースされたPentiumのクロック周波数は66MHzでしたが、AMDが1998年にリリースしたK6-2では550MHzにまで到達していました。さらに、この問題がAMD製CPUで多数報告されていたのは、AMD製CPUの方がIntel製CPUよりもLOOP命令を高速に実行できたからだとNecasek氏は指摘。350MHzのPentium IIがLOOP命令を104万8576回実行するのに17ミリ秒かかるのに対し、同じく350MHzのK-2では3ミリ秒で実行できてしまうほどの差がありました。
by saramode さらもで
Windows for Workgroups 3.11とまったく同じ処理がWindows 95のNDISにも実装されているため、OSは同様の問題をかかえたままだったとのこと。しかし、Windows 95においてパッチが提供されたK-2でも、LOOP命令を104万8576回実行するのに3ミリ秒かかるので、ゼロによる除算は起こらないはず。Necasek氏の調査の結果、Windows 95にはストレージ関連の処理にNDISと同じ構造の処理が実装されていた上、取得した時刻の差分がゼロでなくても十分小さければオーバーフローが発生してしまうような処理になっていたため、LOOP命令を高速に実行できるCPUでは起動に問題が発生することが判明しました。
MicrosoftはWindows 95が抱えていたストレージ関連の問題を「K6-2向け」のパッチとして配布し、Windows 98では最初から修正を反映。しかし、NDISの問題についてはWindows 98の最終バーションまで修正されることはなかったとのこと。
一連の問題から、「取得した時刻の差分が1ミリ秒以下の場合はどうなるか」というテストをMicrosoftは行うべきだったとNecasek氏。また、ともすれば堅実そうに思える「CPUの性能がすぐに大幅に向上することはない」というソフトウェアエンジニアの仮定と、「命令を高速化することは常にいいことだ」というハードウェアエンジニアの仮定のどちらも堅実な仮定ではなかったことも教訓にすべきだNecasek氏は語っています。