Win16 メモリ管理

2026/06/05 20:16

Win16 メモリ管理

RSS: https://news.ycombinator.com/rss

要約

Japanese Translation:

要旨:核心となるメッセージは、16 ビット時代の初期 Windows(1.x から 3.1 まで)が、8086/286 ハードウェア上でページングサポートなしで動作したため、後続の Windows NT アーキテクチャとは根本的に異なる専用メモリ管理システムに依存していた点にある。安定性を保つために、これらのシステムは DOS マルチタスクリングに由来する「New Executable(NE)」形式を採用し、アプリケーションを不透明なセグメントハンドルによって管理されるオーバーレイとして扱っていた。これは重要なコードを物理的な RAM 内に保持するためのものである。開発者は厳格な制約に直面しており:セグメントは緊縮化とフラグメンテーション防止のために固定または移動可能として定義され、

GlobalAlloc
を使用してロック/アンロックを行うなどの特定の API ルールの遵守が必須だった。これらを無視すると、セグメントアドレスがアンロック時に無効になるような即座の論理エラーが発生する。さらに、初期の開発資料はこれらの複雑さを過小評価することが多く、開発者がメモリレイアウトに関する堅牢な規則、ローダーパッチング慣例(ローダーは自動的に関数のプロログを修正しデータセグメントを再読み込みさせる)、Windows 用コード用の
/Gw
コンパイラフラグ、または DLL 構築時の
/Aw
フラグなどを見逃す場合に脆弱性が高まる。ハードウェア保護機構が欠如したこの環境(限られたハードウェア上で複雑なアプリケーションを設計したもの)に隠されたバグを明らかにするために、SHAKER などの必須ツールの利用が必要だった。

本文

16 ビット Windows のメモリ管理:技術解説

本ドキュメントは、**Microsoft NT を除く 16 ビット Windows(Windows 1.x, 2.x, 3.x)**における独特なメモリ管理メカニズムを解説します。当時は「低レベルの詳細はツールが処理する」という前提があり、メモリ管理の重要性が軽視されがちでしたが、以下の要素を理解することで、当時のシステム動作の本質を把握できます。


1. Windows メモリ管理の概要

16 ビット Windows は、物理メモリ不足に対処するため、システム全体を**「高度なオーバーレイマネージャ」**として設計していました。

基本コンセプト

  • 物理メモリ制限への対応: PC の RAM がアプリケーションサイズを超えることが多かったため、物理メモリには常に最もアクティブなセグメントのみを配置し、その他はディスクに保存・読み込むオーバーレイ方式を採用しました。
  • アーキテクチャの制約: 当時の主流である8086および80286は「ページング(Paging)」機能をサポートしていなかったため、このオーバーレイ方式が必須でした。

セグメントの移動可能性

Windows はセグメントを物理メモリの固定位置ではなく、「移動可能」とみなします。

  • 理由: 実行時に
    CS
    (コード)、
    DS
    (データ)、
    SS
    (スタック)レジスタに適切なセグメントを設定していれば、実際の物理アドレスを意識する必要がありません。
  • 実装要件: コードやデータへのアクセスは「近接コール(Near Call/Jump)」や「近接ポインタ」のみを使用することで保証されます。

実行形式の拡張:NE フォーマット

DOS の従来の

MZ
形式を単一ブロックとして扱うのではなく、New Executable (NE) フォーマットを採用しました。

  • 構造: セグメント指向であり、各セグメントを個別にディスク上に保存します。
  • 利点: 個々のセグメントを独立してロード(または再ロード)し、メモリ内で自由に移動させることが可能になります。
  • エクスポート機能:
    • インポート
      : 外部コード(OS など)への呼び出し用。
    • エクスポート
      : 他から呼び出されるアプリケーション側(例:ウィンドウ手続き)への登録用。

2. メモリのシフトとハンドリング

Windows のメモリ管理の中心は**「ハンドル(Handle)」**によるセグメント識別です。

ハンドルの仕組み

  • 定義: 16 ビットの不透明な値。内部構造を操作すべきではありません。
  • 機能: x86 の保護モードのような「セレクトラ」に見えますが、意図的な設計(Intel 286 の保護モード機能をインスピレーション源にしたため)。
  • 特徴: セグメントの物理アドレスとは無関係です。
    GlobalAlloc
    でメモリを割り当てても返されるのはセグメントハンドルであり、実際のアドレスではありません。

アドレス取得とロック (
GlobalLock
/
GlobalUnlock
)

ハンドルはそのまま使用できず、アクセスには以下の処理が必要です。

  1. アクセス準備:

    GlobalLock(Handle)
    
    • セグメントアドレスを取得し、メモリをロック(ロックカウント増)します。
    • ロック中のみ、セグメントは移動せず、保持されたアドレスが有効です。
  2. 完了解放:

    GlobalUnlock(Handle)
    
    • ロックカウントを減らします。カウントがゼロになると、セグメントは再び移動・破棄される可能性があります。

⚠️ 重要:ロック解除後のリスク

GlobalUnlock
直後には即座に移動しない場合もありますが、無効化されるとの保証はありません

  • 危険性: アンロック後に取得したアドレスを使用してアクセスしてしまうと、セグメントがすでに移動していた場合「隠れバグ」を引き起こします。一度アンロックされたセグメントは、OS 側でいつでも移動・破棄される可能性があるため、厳密に解放後にアドレスを参照しないこと

セグメントの属性

Windows はセグメントを「固定」または「移動可能」、「破棄可能」と「破棄不可」のいずれかに分類します。

  • 固定 (Fixed) vs 移動可能 (Movable)

    • 移動可能: 通常の設定。解放時に Windows が効率的に再配置(シャッフル)できます。(例:通常のコード・データ)
    • 固定: 物理位置を保持。割り込みハンドラなど、ベクトルテーブルが特定のアドレスを指す必要がある場合のみ使用します。
  • 破棄可能 (Discardable) vs 破棄不可 (Non-discardable)

    • 破棄可能: 未使用時にディスクから削除可能。(例:コードセグメント、読み取り専用リソース)
    • 破棄不可: 変更されたため再ロードが困難。(例:書き込み可能なデータセグメント)

3. DLL(動的リンクライブラリ)

Windows は早期に DLL を標準化し、UNIX に先駆けて共有ライブラリシステムを構築しました。

特性

  • 形式: アプリケーションと同じNE フォーマットですが、直接実行できません。
  • 動作原理: 他プロセス(タスク)によってロード・呼び出されます。
  • ネームスペース:
    • UNIX と異なり、グローバルな名空間がありません。
    • モジュール名の指定と、その中の名前/順序番号による二段階解決を採用しています。
    • これにより同名シンボルによる衝突が防ぎつつ、順序番号を使った高速インポートが可能になります。

スタックセグメントの制限

  • DLL は独自のスタックを持たず、常に呼び出し元のプロセスのスタック上で動作します。
  • 条件:
    SS
    (スタック) $\neq$
    DS
    (データ) でなければならないため、ビルド時のコンパイラ設定が異なります。

プロローグの必要性

コンパイラが

SS == DS
前提のプロローグ(プロローグ/エピログ)を生成しますが、DLL はこれを満たしません。そのため、Windows のロードラによるパッチアップ処理が必要な「太い」プロローグを使用します。


4. コンパイラスイッチとプロローグ詳細

Microsoft C コンパイラは Windows 開発に不可欠でしたが、初期期は設定が秘匿的でした(SDK 上でのみ参照)。主要なスイッチと動作は以下の通りです。

スイッチ意味用途
/Aw
メモリモデル修飾子 (
SS != DS
)
DLL の生成時に必須(関数内で
DS
を再ロードしないため)
/Gw
Windows 特有のプロローグ生成エクスポートされた FAR 関数への必須設定

自動パッチ処理 (
CMACROS.INC
)

コンパイラは NE モジュールからエクスポートされた関数に対し、Windows ロードラが修正可能なように以下を行います。

  1. デフォルトデータセグメントの読み込み:
    • ロードラは関数の開始直後(最初の 3 バイト)をパッチして、
      AX
      レジスタにモジュールのデフォルトデータセグメントを読み込ませます。
  2. スタックフレームの維持:
    • Windows がスタックをウォークするため、
      BP
      の増減処理で「オフセットとセグメント情報がスタックに存在する」ことを示します。
  3. プロローグ更新:
    • セグメントが移動した場合でも、エクスポートされた関数のプロローグを更新し、正しいアドレスを指すようにします。
    • DEF
      ファイルで
      NODATA
      が指定された場合のみ、このパッチ処理はスキップされます。

⚠️ 警告: デフォルトデータセグメント以外のものをアクセスする場合は、適切にロックされている必要があります。


5. OS/2 との比較

16 ビット Windows と 16 ビット OS/2 は似ていますが、ハードウェアの違いによりアプローチが異なります。

特徴16 ビット Windows16 ビット OS/2
メモリモデルオーバーレイ方式(強制)ハードウェアによる移動・再ロード可能
管理責任アプリとコンパイラに高い規律が必要システム層がハードウェアを利用し、簡素化
プロローグWindows 特有の「太い」プロローグ必須特別なプロローグ不要(OS が処理)
ロック機構
GlobalLock
/
GlobalUnlock
必須
ハードウェアサポートにより不要
コンパイラ
/Aw
,
/Gw
スイッチ必須
OS 側からサポートされるため簡略化可

OS/2 は 80286 の保護モード機能を最大限に活用し、バグの温床である「手動ロック管理」を排除した点が大きいです。


6. テスティングとツール

通常の環境では潜伏するメモリ管理バグは発見されにくいため、特別なツールによる検証が必要です。

  • Shaker (Windows 1.x/3.0 SDK)

    • 機能: メモリを強制的に「揺さぶり」、セグメントの破棄・移動を強制します。
    • 目的: 正しくロックされていない、または破棄可能な領域へのアクセスなどのバグを引き出すためのストレステストツールです。
  • HeapWalker (Windows 1.x/3.0 SDK)

    • 機能: 現在の割り当て済みセグメントとその所有権を表示します。
    • 用途: 低メモリ状態をシミュレート(全領域を 1K 単位で解放)し、クラッシュ原因を確認します。
  • Stress (Windows 3.1 SDK)

    • Shaker の後継機。リソース不足(内部ヒープ制限、ディスクスペース不足、ファイルハンドル不足)時の動作を検証するために設計されています。

: 8086 ではハードウェアが未割り当てメモリアクセスを検出しないため、専用のツールなしでは誤りを検出できません。


まとめ

16 ビット Windows は、ページング機能を持たない脆弱なハードウェア環境において、ソフトウェア的な高度なオーバーレイ管理を実現していました。

  • コンパイラ設定:
    /Aw
    ,
    /Gw
    の正確な使用。
  • メモリ操作:
    GlobalLock
    /
    GlobalUnlock
    の厳密な運用と、アンロック後のアドレス参照禁止。
  • エクスポート: 適切な関数のエクスポート処理の実装。

これらを遵守しない場合、セグメントの移動に伴うアクセス失敗やバグを引き起こすリスクが極めて高かったのです。

同じ日のほかのニュース

一覧に戻る →

2026/06/08 4:01

Linear がなぜこれほど速いのか?技術的な解説

## Japanese Translation: # リニア革命:ウェブアプリケーションのパフォーマンス革新 リニアは、データベースをブラウザ内に完全に実行する(IndexedDB を使用)ことで、従来の CRUD アプリのデータ読み込み時間である約 300ms を数ミリ秒に短縮します。この「ローカルファースト」アーキテクチャでは、標準的なネットワークループが逆転し、デバイス上で変更を即時適用し、WebSocket を経由で非同期でデルタをプッシュすることで、アップデート待ちの地味な网络待ち時間を排除します。共同創設者のトゥオマス・カンカレは、この自社工程エンジンをゼロから構築することを強く推奨しました。タンスタッククエリや SWR などの一般的な楽観的な更新ライブラリを使用せず、サーバーを単なる同期ターゲットとして厳密に扱うアプローチを採用しています。JavaScript のサイズを最小限に抑えるため(圧縮後の JS は約 21MB にまで削減され、ルートレベルのチャンクに分けられている)、チームはバンドルパイプラインを 4 回も移行しました(Parcel → Rollup → Vite → Rolldown)。これにより、配送されるコード量は約 50% 削減されました。重要資産には、フォント(単一の可変 Inter ファイル)、数百のルートチャンクが含まれており、サービスワーカーと `<head/>` に設定された並行モジュールプレロードリンクを通じて事前キャッシュされます。これにより、オフライン時や繰り返し訪問時でも即座にレンダリングが可能になります。さらに、重要な CSS、JavaScript、認証ロジックは HTML に直接埋め込まれており、認証にはセッショントークンの即時取得ではなく、ローカルストレージ内の存在を確認する方式を採用しています。该系统は、50 件のイシューリストが変更された場合、わずか 50 セルだけを更新するなどの粒度の細かなリレンダを達成します。これは、データをプロパティごとの MobX オブザーバブルに水浸げすることで実現されており、標準的なフレームワークでは追いつけない優れた速度優位性を保証しています。

2026/06/08 3:33

依存症、逮捕、犯罪歴からの再起:ゼロから立ち直すまで

## Japanese Translation: 最も重要な示唆は、技術分野における採用決定において過去の過ちよりも将来の可能性を優先させるべきであるという点です。堅い身元調査は無視できない課題を乗り越えた有能な個人が不当に排除されるためです。著者は自らの驚異的なキャリア回復の実例によってこの主張を具体化しています:未成年の逮捕歴(監禁中に最大保安施設で Schedule II 指定薬物への所有および製造・頒布意図に関する 17 の起訴を含む)から、現代の技術産業における高位な技術職に就くまでです。当初、企業の「前科者なし」という採用ポリシーにより 8 社のオファーが撤回されましたが、彼は後に、ワークリリースインターンシップ期間中やハスラ(現在は PromptQL)で創設者から felony conviction の開示を受け入れた際に再雇用されるなど、採用管理者が大きなリスクを冒して彼を採用したことで成功を果たしました。彼の journey はまた、試行錯誤を通じて学んだ特定の教訓も強調しています:Techtonic での不当な解雇とその後 Slack の履歴を調べて正当化されたことや、Hasura などのコミュニティ主導のツールの発見とその仕事が不可欠になったことなどです。この個人的な物語は、犯罪記録を超えて才能が存在し、支援的なリーダーシップが人生を変えうることを説得力のある証拠として提供します。これからの未来において、業界リーダーは候補者の履歴に基づいて自動的に不合格にするのではなく、実証されたスキルを評価するよりニュアンスのある採用慣行を採用すべきです。功績基準による評価システムへとシフトすることで、技術セクターは見逃された膨大な talent の蓄水池を活用できるほか、更生と労働市場への再統合を積極的に支援し、より包摂的な環境を構築できます。

2026/06/08 3:54

LLM が人間のような属性を持つなら、同じく「エイジ オブ エンパイア2」もそうだ

## Japanese Translation: 本文書(arXiv:2605.31514)は、Adrian de Wynter 氏によるものであり、大規模言語モデル(LLM)に独自の人間のような(擬人化された)特性を帰属させることは欠陥のある仮定であると論じます。その理由は、そのような特性は「Age of Empires II」といったリアルタイム戦略ゲームでトレーニングされたニューラルネットワークを含む、あらゆる十分に複雑なシステムにおいて現れる可能性があるためです。著者は「Age of Empires II」が機能的かつチューリング完全であることを認めつつも、レゴの組み立てやグレート・ボストン地域など他のエンティティを、同様の擬人化的特性を示し得る強力なサブストレートの例として挙げています。基本的な証拠は、根本的な応答特性は一定のままでも、観測される行動の解釈はサブストレートによって変化する可能性があることを示しています。擬人化された属性が存在するかどうかを、サブストレートに依存せずに仮定することは循環的な結論や情報不足の結論につながります。著者は「null(零)」という仮説を提唱しており、明示的な測定がそれを否定するまで、LLM をこれらの特性において非特異であると扱うよう提案しています。この転換は、技術業界に対して、ニューラルネットワークだけでなく他のシステムにも人間のような性質を見出さないよう警戒することを促します。これを裏付けるために、本稿では分野の概要調査、潜在的な異議への言及、そして「null」仮説を適用する具体例について論じています。本研究は、オープンネスとユーザーデータのプライバシーを重視する arXivLabs の取り組みの一部です。