『Linuxカーネルの教科書』

  • LinuxのカーネルはOSS, 27M+ lines

§1 Linuxカーネルの基礎

  • Linuxカーネルはいろいろな機能がある(モノリシックカーネル)
    • <=> マイクロカーネル
  • UNIXシステムコールとほぼ互換(POSIXなどの共通インターフェース)
  • カーネルはイベント駆動
    • ハードウェア(外部)割り込み
    • ソフトウェア割り込み/例外(内部))
  • システムコール(syscall)

スレッド

  • Linuxでプログラムを実行すると「プロセス」または「スレッド」が生成
    • スレッドは他のプロセスやスレッドとメモリを共有
  • $ ps aux で出る
    • [hoge] はカーネルスレッド
  • 時分割処理している(CFS; Completely Fair Scheduler)

メモリ管理

  • 仮想アドレスでメモリ管理
    • ページング方式 (size: 4KB)
    • 多段テーブル max5段
  • ページテーブルで変換(TLBキャッシュ)
  • デマンドページング: ページフォルトが起きたら割当する

image- (LRU; Least Recently Used) でページ回収

  • メモリが足りなくなると OOM Killerで空き物理ページを確保

デバイス管理

  • ほぼすべてのデバイスを「ファイル」として抽象化
    • デバイスファイルは /dev/ にある
  • キャラクター型: シリアル回線や端末のようにデータを1byteづつ入出力
  • ブロック型: ハードディスクのようにブロック単位で入出力, バッファされる

ファイルシステム

  • 記憶装置を抽象化、仮想化
    • VFS層があるので色々なファイルシステムをサポート(e.g. ext4, exfat, ntfs)

image

ネットワーク

  • ネットワークはプロトコルが複雑なので「ソケット」インターフェースでさまざまなプロトコルに対応
  • Netfilter で特定のパケットの送受信の制限等の処理をできる

カーネルの読み込みから稼働まで

  • 電源を入れると BIOSUEFI などのファームウェアが稼働
    • ブートローダーが起動しカーネルのイメージ(/boot/vmlinux) をメモリにのせて実行
    • カーネルが初期化と起動処理
    • rootファイルシステムをマウントして /sbin/init が起動, 以降はこのプロセスが親プロセスに

§2 Linuxカーネルのモジュール管理

  • 機能を「モジュールファイル」として組み込んだり切り離したりできる
    • カーネルイメージの小型化や拡張性、デバイスドライバなど
    • 全て(470MB) -> Ubuntu 20.04(12MB)
    • lib/modules/<release-number> にモジュールある
      • lsmod で確認できる

§3 Linuxカーネルのビルド方法

$ sudo apt update
$ sudo apt install build-essential bc bison flex libelf-dev libssl-dev libncurse5-dev
$ tar xvf linux-5.4.74.tar.xz
$ cd linux-5.4.74
$ touce .config

§4 タスクスケジューラの仕組み

初期のスケジューラ

  • nice値(-20~19の優先度(小さいほど優先))に応じてタイムスライスを割り当てる
    • 最も残りタイムスライスが多いものが選ばれる

O(1)スケジューラ

  • タスクを線形探索するので遅い
    • nice値とタスクのスリープ時間により優先度が算出される

CFS; Completely Fair Scheduler

  • 対話型タスクに実行優先度ボーナスを与える処理が複雑
  • 各タスクが実際にCPUで実行される時間を均一にすることをポリシーに
  • vruntime(CPUで実行された累計時間)を Red-Black Tree で管理
  • タイムスライス = レイテンシ * 重み / キュー内の実行可能タスクの重みの合計
    • policy: SCHED_FIFO, SCHED_RR, SCHED_OTHER

§5 仮想メモリを管理する仕組み

  • 64bit x86では4Kバイトだと4段
    • 有効バイト数は64bitよりも少ない -> 52bit = 4PB (0x0000000000000000 ± N/2 bit)
  • Linuxはこのリニアアドレスを素直に使う
    • ~0xFFFFFFFFFFFFFFFF: カーネル用
    • 0x0000000000000000~: プロセス用
  • PTI: カーネルモード時のみカーネル用の領域を割り当てる仕組み
    • 物理メモリを連続で対応付ける仕組みもある.
dev > sudo cat /proc/1/maps
00200000-0024d000 r--p 00000000 00:14 3659174698859071                   /init
0024d000-00326000 r-xp 0004c000 00:14 3659174698859071                   /init
00326000-00334000 rw-p 00124000 00:14 3659174698859071                   /init
00334000-00336000 rw-p 00131000 00:14 3659174698859071                   /init
00336000-0033d000 rw-p 00000000 00:00 0
00ac8000-00acf000 rw-p 00000000 00:00 0                                  [heap]
7f36ee3a6000-7f36ee3a7000 ---p 00000000 00:00 0
7f36ee3a7000-7f36ee3bc000 rw-p 00000000 00:00 0
7f36ee3bc000-7f36ee3bd000 ---p 00000000 00:00 0
7f36ee3bd000-7f36ee3d2000 rw-p 00000000 00:00 0
7ffd3f0d2000-7ffd3f0f3000 rw-p 00000000 00:00 0                          [stack]
7ffd3f186000-7ffd3f189000 r--p 00000000 00:00 0                          [vvar]
7ffd3f189000-7ffd3f18b000 r-xp 00000000 00:00 0                          [vdso]

image

§6 コンテキストスイッチ

image

  • PTI: Page TAble Isolation
  • ユーザーモード時はカーネル領域は何もマッピングしない

§7 物理メモリー管理の仕組み

  • ページングで仮想メモリを管理できるから物理メモリの管理は不要かというとそうではない
    • 連続的に確保したり, DMAというCPUを介さないデータ転送の仕組みがあるから
  • 内部断片化: ページの中で余る
  • 外部断片化: ページ同士がバラバラで連続した大きな空きがなくなる

バディーシステム

  • 2^10 ページのブロックをリストで管理し

スラブアロケータ

  • ページサイズより小さなデータ領域の確保/開放が出来る

§8 ファイルシステム

  • ext4というファイルシステム