モダンオペレーティングシステム 第3章中盤メモ

モダンオペレーティングシステム 第5版 上 第3章中盤(3.3後半〜3.5)。ページテーブルの実装とTLB、ページ置き換えアルゴリズム。 3.3後半 ページテーブルの実装 多段ページテーブル 仮想アドレス空間が64bitの場合、単純なページテーブルは現実的じゃない。 理論上の最大:2^64 = 18,446,744,073,709,551,616 バイト 1ページ = 4KB = 4096バイト ページ数 = 2^64 / 2^12 = 2^52 個 1エントリ = 8バイトとして ページテーブルのサイズ = 2^52 × 8 = 32ペタバイト プロセス1個のページテーブルだけで32PB。話にならない。 実際のx86_64は64bitフルを使わず48bitに妥協している(一部の最新CPUは57bit)。64bitフルを使うと6〜7段の階層が必要になりメモリアクセスのオーバーヘッドが耐えられなくなるから。「理論上の最大は64bitだけど、現実のCPUは48bitに妥協している」という設計判断。 Linuxはこれを多段ページテーブルで解決してる。x86_64では4段構成。 仮想アドレス(48bit有効) ┌──────┬──────┬──────┬──────┬────────────┐ │ PGD │ PUD │ PMD │ PTE │ offset │ │ 9bit │ 9bit │ 9bit │ 9bit │ 12bit │ └──────┴──────┴──────┴──────┴────────────┘ 名前は覚えなくていい。住所の階層構造だと思えばいい。 東京都 → PGD(一番大きい区分) 渋谷区 → PUD 代々木1丁目 → PMD 1番地 → PTE 101号室 → offset(ページ内の位置) ポイントは使っていない部分のテーブルを作らないこと。 ...

May 10, 2026 · 4 min

モダンオペレーティングシステム 第3章後半メモ

モダンオペレーティングシステム 第5版 上 第3章後半(3.6〜3.8)。実装上の課題、セグメンテーション、そしてCPU脆弱性とOSの関係。 3.6 実装上の課題 OSがページングに関わるタイミング ページング処理はいつ動くか。 【プロセス生成時】 → 新しいページテーブルを作る → ディスクからプログラムをロードする準備 【プロセス実行時】 → TLBミス → ページテーブルをたどる → ページフォルト → ページを物理メモリに載せる 【プロセス終了時】 → ページテーブルを解放 → 物理フレームを解放 → ディスク上のスワップ領域を解放 【コンテキストスイッチ時】 → ページテーブルの切り替え → TLBフラッシュ or ASID切り替え k3sでPodが終了した時: コンテナプロセス終了 → カーネルがページテーブル解放 → 物理フレーム解放 → 次のPodがそのフレームを使える 命令の再実行問題 ページフォルトが起きた時、フォルトした命令を再実行する。でもここに厄介な問題がある。 メモリのコピー命令(100番地→200番地にコピー) 実行途中の150番地でページフォルト ↓ 100〜149はすでにコピー済み ↓ 命令を再実行 ↓ 100〜149を二重にコピー → 壊れる 対処法 方法1: 命令実行前に全ページが存在するか先読みチェック → なければ先にページフォルトを起こしておく → 全部揃ってから命令実行(x86寄りの方式) 方法2: CPUが内部状態を保存 → どこまで実行したか記録 → 再開時はその続きから(一部のRISCの方式) ページのロック(ピン留め) I/O処理中のページは絶対に追い出せない。 DMA転送中 → デバイスが直接メモリに書き込んでる → このページを追い出したら → デバイスが存在しないメモリに書き込む → 最悪システムクラッシュ だからI/O中のページには**ロック(ピン留め)**をかける。置き換えアルゴリズムの対象外になり、I/O完了後にロック解除。 ...

May 10, 2026 · 3 min

モダンオペレーティングシステム 第3章前半メモ

モダンオペレーティングシステム 第5版 上 第3章前半(3.1〜3.3)。メモリ管理の基本。 第3章前半 メモリ管理の基礎 ベースレジスタとリミットレジスタ CPUのハードウェアレジスタを使ったシンプルなメモリ保護の仕組み。 プログラムがメモリにロードされる ↓ ベースレジスタ ← プログラムの開始物理アドレス リミットレジスタ ← プログラムの長さ ↓ プロセスがメモリにアクセスするたびに ↓ CPUがアクセスアドレス + ベース値を自動加算 ↓ リミットを超えてないか同時チェック ↓ 超えてたらフォールト(アクセス中断) これによってプロセスAはプロセスBのメモリに触れない。CPUレベルで物理的にブロックしてる。 ProxmoxのVM(KVM)との絡み KVMの場合はベース/リミットだけじゃなく、**EPT(Extended Page Table)**というIntel VT-xの機能でさらに一段上の分離をしてる。 ゲストの仮想アドレス ↓ ゲストのページテーブル ↓ ゲストの物理アドレス(実はまだ仮想) ↓ EPT(ここがKVMの追加レイヤー) ↓ 本当の物理アドレス これをネストされたページングと呼ぶ。ゲストOSごとに完全に別の物理アドレス空間にマッピングされるので、VM同士はお互いのメモリに絶対アクセスできない。 EPTはソフトウェアじゃなくてCPUがハードウェアで変換するので速い。 KVMに必要なCPU機能 KVMは以下のCPU機能が必須: CPU 仮想化支援機能 Intel VT-x AMD AMD-V(SVM) ARM(ラズパイ4等) ARMv8 Virtualization Extensions BIOSでVT-xが無効になってるとKVMが動かない。ProxmoxでVM作れないエラーの原因の大半がこれ。 MacのDockerが遅かった理由もここ Intel Mac時代 → HyperKitでソフトウェア仮想化 → 遅い・重い Apple Silicon(M1〜) → ARMのVirtualization Extensions使える → ハードウェア仮想化 → 速い M1でDockerが速くなったのはApple SiliconがARMの仮想化支援機能をちゃんと使えるようになったから。ただしARMなのでx86イメージは--platform=linux/amd64でエミュレーションになり遅い。 ...

May 2, 2026 · 2 min

モダンオペレーティングシステム 第1章・第2章メモ

モダンオペレーティングシステム 第5版 上 読んでる。 読み進めた記事というよりは範囲の中でわからんとこを生成AIと相談しながら文章課題とか出してもらいつつ進めたものをまとめた記事。 第1章 序論 OSの2つの役割 OSには2つの顔がある。 1. 拡張マシン(Extended Machine) ハードウェアの複雑さを隠蔽する。アプリ開発者はディスクの物理構造を知らなくてもファイルを読み書きできる。Docker APIがLinuxの複雑さを隠すのと同じ発想。 2. リソースマネージャー(Resource Manager) CPU・メモリ・ディスク・ネットワークを複数プロセスに割り当てる。k3s schedulerがNodeのリソースをPodに割り当てるのと同じ役割。 k3s scheduler → OSのリソースマネージャーと同じ役割 Docker API → OSの拡張マシンと同じ役割 OSがやってることをk3sやDockerが上位レイヤーで再現している。 ユーザー空間とカーネル空間 ┌─────────────────────────────┐ │ ユーザー空間 │ │ Docker、アプリ、k3s、etc... │ │ → 直接ハードウェアは触れない │ └──────────┬──────────────────┘ │ システムコール(唯一の通路) ┌──────────▼──────────────────┐ │ カーネル空間 │ │ スケジューラ │ │ メモリ管理 │ │ ファイルシステム │ │ デバイスドライバ │ │ → ハードウェアを直接触れる │ └─────────────────────────────┘ アプリが直接ハードウェアを触れたら危険。カーネルが仲介することで安全性を保証する。 システムコール docker runした時の実際の流れ: docker run ubuntu ↓ Dockerデーモン ↓ clone() ← プロセス生成 unshare() ← namespaceを作る mount() ← ファイルシステムをマウント ↓ Linuxカーネル Dockerはシステムコールの集合体。魔法じゃなくてLinuxのシステムコールをうまく組み合わせてるだけ。 ...

April 26, 2026 · 3 min

LinuxのCFSとEEVDFを整理する - スケジューラはなぜ赤黒木を使うのか

はじめに 動かしながらゼロから学ぶLinuxカーネルの教科書 第2版 上記の技術書を読んでいてスケジューラ周りの理解が曖昧だったので、生成AIや公式ドキュメントを使って整理した。 CFS (Completely Fair Scheduler) とは Linux 2.6.23から導入されたプロセススケジューラ。「全プロセスに公平にCPU時間を与える」という思想で設計されている。 vruntime(仮想実行時間) vruntimeは「実際の実行時間をNICE値で補正した値」で、CFSの核心となる指標。 vruntime += 実際のCPU時間 × (1024 / プロセスの重み) NICE値が低い(優先度高)→ 重みが大きい → vruntimeの増加が遅い → より長くCPUを使える NICE値が高い(優先度低)→ 重みが小さい → vruntimeの増加が速い → すぐ交代させられる CFSは「vruntimeが最も小さいプロセスを次に実行する」というルールで動く。後ろ向きの指標(過去の使用量の累積)であることがEEVDFとの本質的な差になる。 NICE値と重み NICE値は -20(最高優先度)〜 +19(最低優先度)の範囲で、内部的に重みに変換される。 NICE 0 → weight 1024 NICE -1 → weight 1277(約1.25倍) NICE +1 → weight 820(約0.8倍) NICE -20 → weight 88761 NICE +19 → weight 15 1段階変わるごとに約10%のCPU時間が変化する設計になっている。 タイムスライスとスケジューリングレイテンシ スケジューリングレイテンシは「全プロセスが最低1回実行されるべき目標周期」。デフォルト約6〜24ms(プロセス数による)。 タイムスライスはその比例配分: タイムスライス = スケジューリングレイテンシ × (タスクの重み / キュー内の全タスクの重みの合計) 具体例: ...

March 1, 2026 · 2 min

Linuxの起動フローを整理する - UEFI/BIOSからinitまで

はじめに [https://info.nikkeibp.co.jp/media/LIN/atcl/books/070900046/:embed:cite] 上記の技術書を読んでいて、ブートローダとLinuxの初期スタート時の役割とか順番がいまいち掴めなかったので生成AIや他の記事など別軸から調べ直してまとめた。 起動フロー全体像 UEFI/BIOS ↓ POST(ハードウェア初期化)、ブートデバイス選択 ブートローダー(GRUB等) ↓ /boot/vmlinuz(カーネルイメージ)をメモリに展開 ↓ /boot/initramfs をメモリに展開 カーネル起動 ↓ initramfsを一時的な / としてマウント ↓ ドライバ読み込み、本物のrootデバイスを認識 ↓ 本物のroot FSをマウント(switch_root) /sbin/init(systemd)に移譲 各フェーズの詳細 1. UEFI/BIOS 起動の最初はUEFI(または旧来のBIOS)が担う。 POST(Power-On Self Test): メモリ、CPU、周辺デバイスの初期化 ブートデバイスの選択(NVMe, SSD, PXEなど) UEFIの場合はEFIパーティション(ESP)から .efi ファイルを直接実行できる UEFIとBIOSの大きな違いとして、UEFIはGPTディスクのネイティブサポートや、セキュアブートの仕組みを持つ。 2. ブートローダー(GRUB2等) UEFI/BIOSからブートローダーに制御が渡る。 代表的なものはGRUB2で、設定ファイルは /boot/grub/grub.cfg にある。 ブートローダーの役割はシンプルで、以下の2点だけ: カーネルイメージ(vmlinuz)をメモリに展開する initramfs(initramfs-*.img)をメモリに展開する # /boot 以下の典型的な構成 $ ls /boot/ grub/ initramfs-6.1.0-28-amd64.img vmlinuz-6.1.0-28-amd64 ブートローダー自身はルートFSのマウントをしない。あくまでカーネルとinitramfsをメモリに置いて制御を渡すだけ。 3. カーネル起動とinitramfs ここが一番誤解されやすいフェーズ。 カーネルが起動すると、まず**initramfs(Initial RAM Filesystem)**を一時的なルート(/)としてマウントする。 なぜinitramfsが必要か? カーネル本体はコンパクトに保つ設計になっており、NVMeやLVMやLUKS(暗号化)といった本物のディスクにアクセスするためのドライバを、起動時に動的にロードする必要がある。 initramfsはそのためのミニマルな環境を提供する。 initramfs の中身(概略) /init → 起動スクリプト /lib/modules → カーネルモジュール(ドライバ) /bin, /sbin → busybox等の最低限のコマンド群 処理の流れ: ...

February 28, 2026 · 1 min