Linuxカーネルプログラミング初心者に対して、ソースコードのビルドから始め、カーネルソースコードの修正、モジュールの作成方法を説明します。またパフォーマンス向上のために必要なメモリの割り当て、スケジューラ、同期といった複雑なテクニックを説明し、実際にどのようにプログラミングして活用すればよいのか、その要点と、つまずきやすいポイントなども教えてくれます。Linuxカーネルに関する知識を広く学ぶことが可能です。
Linuxカーネルプログラミング 第2版
Kaiwan N. Billimoria 著、武内 覚、大岩 尚宏 訳
![[cover photo]](https://www.oreilly.co.jp/books/images/picture_large978-4-8144-0110-9.jpeg)
- TOPICS
- Linux
- 発行年月日
- 2025年07月
- PRINT LENGTH
- 492
- ISBN
- 978-4-8144-0110-9
- 原書
- Linux Kernel Programming, 2nd Edition
- FORMAT
- Print PDF EPUB
目次
推薦の言葉 はじめに Ⅰ部 基礎知識 1章 Linuxカーネルプログラミング入門 1.1 開発環境の構築 1.1.1 システム要件 1.1.2 Linuxディストリビューションとカーネルの選択 1.1.3 仮想マシンへのLinuxのインストール 1.1.4 インストール済みのUbuntu仮想マシンイメージ 1.1.5 OSBoxes Ubuntu 22.04のセットアップ 1.1.5.1 ステップ1:VirtualBox Guest Additionsのインストール 1.1.5.2 ステップ2:新しいユーザでセットアップ 1.1.5.3 ステップ3:必要なパッケージのインストール 1.1.6 仮想マシンに関するその他のtips 1.2 便利なプロジェクト 1.2.1 Linux manページ 1.2.2 tldr 1.2.3 Linuxカーネルドキュメント 1.2.4 Linuxカーネルの静的解析ツール 1.2.5 LTTng(Linux Trace Toolkit next generation) 1.2.6 procmap 1.2.7 eBPF 2章 Linuxカーネルのビルド 2.1 カーネルビルドの事前準備 2.1.1 バージョン名の命名規則 2.1.2 開発の流れ 2.1.2.1 コマンドラインによる確認 2.1.2.2 GitHubページでカーネルのGit logを確認する 2.1.3 サポート期間 2.1.3.1 LTSカーネルの方向性 2.1.3.2 どのカーネルを選択すべきか? 2.2 カーネルソースのビルド手順 2.2.1 ステップ1:Linuxカーネルソースツリーの取得 2.2.1.1 特定のカーネルをダウンロードする 2.2.1.2 Gitツリーのclone 2.2.2 ステップ2:カーネルソースコードの展開 2.2.3 ステップ3:Linuxカーネルのビルド設定 2.2.3.1 Kconfig/Kbuildビルドシステムの概要 2.2.3.2 デフォルトのビルド設定 2.2.3.3 make localmodconfigを使う 2.2.3.4 make menuconfigを用いたビルド設定 2.2.4 ステップ4:カーネルイメージとモジュールのビルド 2.2.5 ステップ5:カーネルモジュールのインストール 2.2.5.1 カーネルソース内にあるカーネルモジュール 2.2.5.2 カーネルモジュールのインストール 2.2.6 ステップ6:initramfsイメージの作成とブートローダの設定 2.2.7 ステップ7:GRUBブートローダのカスタマイズ 2.2.7.1 GRUBのカスタマイズ 2.2.7.2 デフォルトでブートするカーネルを設定する 2.2.7.3 GNU GRUBブートローダを起動する 2.2.7.4 GRUBプロンプトでカーネルパラメータを変更 2.3 カーネルビルドにおいて知っておくべきこと 2.3.1 ビルドに必要なツールの最小バージョン 2.3.2 ビルドしたカーネルの配布 2.3.3 カーネルビルドの様子を監視する 2.4 まとめ 3章 カーネルモジュールの作成 3.1 カーネルの構造を理解する 3.1.1 ユーザ空間とカーネル空間 3.1.2 ライブラリとシステムコールAPI 3.1.3 カーネルサブシステム 3.2 モジュール 3.2.1 モジュールサブシステム 3.2.2 カーネルソースツリー内のモジュール 3.3 カーネルモジュールを書く 3.3.1 カーネルヘッダ 3.3.2 モジュール用のマクロ 3.3.3 初期化関数と終了関数 3.4 カーネルモジュールの操作 3.4.1 カーネルモジュールのビルド 3.4.2 カーネルモジュールの実行 3.4.3 printk() 3.4.4 起動中のカーネルモジュール一覧 3.4.5 モジュールのアンロード 3.4.6 lkmスクリプト 3.5 カーネルログとprintk 3.5.1 カーネルリングバッファの使用 3.5.2 カーネルログとsystemdのjournalctl 3.5.3 printkのログレベル 3.5.3.1 pr_マクロ 3.5.3.2 コンソールへの書き込み 3.5.3.3 コンソールに出力する 3.5.3.4 デバッグレベルのカーネルメッセージを有効にする 3.5.4 printkのレートリミット(割合制限) 3.5.5 ユーザ空間からカーネルメッセージを生成する 3.5.6 pr_fmtマクロによるprintk出力の標準化 3.5.7 printkフォーマット指定子と移植性 3.5.8 新しいprintkインデックス機能 3.6 カーネルモジュールのMakefileの基本 3.7 Makefileのテンプレート 3.8 LinuxカーネルのABI互換について 3.9 システム情報の収集 3.10 カーネルモジュールのライセンス 3.10.1 カーネル本体のライセンス 3.10.2 アウトオブツリーカーネルモジュールのライセンス 3.11 カーネルモジュールのライブラリに相当する機能 3.11.1 ソースファイルのリンク 3.11.2 カーネルモジュール内変数と関数のスコープ 3.11.3 モジュールスタッキング 3.12 カーネルモジュールのパラメータ 3.12.1 モジュールパラメータの宣言と使用 3.12.2 モジュールパラメータをロード後に変更 3.12.3 モジュールのデータ型と検証 3.12.3.1 モジュールパラメータの検証 3.12.3.2 モジュールパラメータ名の上書き 3.12.3.3 ハードウェア関連のパラメータ 3.13 カーネルにおける浮動小数点演算 3.14 起動時にモジュールを自動ロードする 3.15 カーネルモジュールとセキュリティ 3.15.1 システムログに関するprocファイルシステム 3.15.1.1 dmesg_restrict 3.15.1.2 kptr_restrict 3.15.2 カーネルモジュールの署名 3.15.3 カーネルモジュール自体を無効にする 3.15.4 カーネルにおけるLSMのロックダウン 3.16 カーネル開発におけるコーディングスタイルガイドライン 3.17 メインラインカーネルへの貢献 3.18 まとめ Ⅱ部 主要機能 4章 プロセスとスレッド 4.1 プロセスコンテキストと割り込みコンテキスト 4.2 プロセスの仮想アドレス空間の基本を理解する 4.3 プロセスとスレッドとスタックの管理 4.3.1 実行中のプロセスとスレッドの数を確認する 4.3.2 ユーザ空間 4.3.3 カーネル空間 4.3.4 ユーザとカーネルのスタックを見る 4.3.4.1 伝統的な方法 4.3.4.2 eBPFを使う方法 4.3.5 プロセス仮想アドレス空間の全体像 4.3.6 タスク構造 4.3.7 currentを使ったタスク構造へのアクセス 4.3.8 コンテキストの判定 4.4 currentを使用したタスク構造の操作 4.4.1 カーネルのヘルパーメソッド 4.4.2 プロセスコンテキストの情報を表示 4.4.3 Linuxがモノリシックカーネルであることの確認 4.4.4 セキュリティを意識したコーディング 4.5 タスクリストの反復処理 4.5.1 すべてのプロセスを表示 4.5.2 すべてのスレッドを表示 4.5.3 コード 4.6 まとめ 5章 メモリ管理 5.1 仮想記憶 5.1.1 Hello worldプログラムの内部を覗く 5.1.2 仮想アドレスとアドレス変換 5.1.3 64ビットLinuxシステムでのVM分割 5.2 プロセス仮想アドレス空間 5.2.1 ユーザ仮想アドレス空間 5.2.1.1 /proc/PID/mapsを直接読み出す方法 5.2.1.2 プロセスのメモリマップを表示するツール 5.2.2 VMAの基本 5.3 仮想アドレス空間の詳細 5.3.1 カーネル仮想アドレス空間の情報を表示する 5.3.2 カーネル仮想アドレス空間のレイアウトを表すマクロと変数 5.3.3 メモリレイアウトに関するカーネルのドキュメント 5.4 メモリレイアウトのランダム化:KASLR 5.4.1 ASLRによるユーザメモリアドレスのランダム化 5.4.2 KASLRによるカーネルメモリレイアウトのランダム化 5.4.3 bashスクリプトでASLRとKASLRの状態を表示する 5.5 物理メモリの管理 5.5.1 ノードとNUMA 5.5.2 ゾーン 5.6 物理メモリをダイレクトマップした領域 5.7 まとめ 6章 カーネルメモリ確保 6.1 カーネルメモリアロケータの概要 6.2 ページアロケータ 6.2.1 ページアロケータの基本 6.2.1.1 フリーリストの構造 6.2.1.2 ページアロケータの動作 6.2.1.3 ケーススタディ 6.2.1.4 ページアロケータの詳細 6.2.2 ページアロケータAPI 6.2.2.1 GFPフラグ 6.2.2.2 ページアロケータにおけるページの解放 6.2.2.3 カーネルメモリの確保と解放におけるガイドライン 6.2.2.4 ページアロケータAPIを使用したモジュール 6.2.2.5 lowlevel_mem_lkmのインストール 6.2.3 GFPフラグの詳細 6.2.3.1 スリープできない状態 6.2.3.2 ページアロケータの長所、短所 6.3 スラブアロケータ 6.3.1 オブジェクトキャッシュ 6.3.2 スラブアロケータAPI 6.3.2.1 スラブメモリの確保 6.3.2.2 スラブメモリの解放 6.3.2.3 kmallocで使用するスラブキャッシュ 6.3.2.4 モジュールでスラブAPIを使う 6.4 kmalloc APIのサイズ範囲 6.5 スラブアロケータの詳細 6.5.1 リソース管理版のAPI 6.5.2 スラブヘルパーAPI 6.5.3 cgroupsとメモリ 6.6 スラブアロケータの使用における注意点 6.6.1 背景とまとめ 6.6.2 ksize()でスラブアロケータをテスト 6.6.3 カーネル内のフラグメンテーションの検出 6.6.3.1 slabinfoで簡単に確認 6.6.3.2 alloc_traces 6.6.4 スラブのメリット、デメリット 6.6.5 カーネルのスラブ実装 6.7 カスタムスラブキャッシュ 6.7.1 カスタムスラブキャッシュの作成と使用 6.7.1.1 ステップ1:カスタムスラブキャッシュの作成 6.7.1.2 ステップ2:カスタムスラブキャッシュの使用 6.7.1.3 ステップ3:カスタムキャッシュの削除 6.7.2 カスタムスラブのサンプルモジュール 6.7.3 スラブシュリンカ 6.8 メモリ問題のデバッグ 6.9 vmalloc() API 6.9.1 vmalloc() APIの使用 6.9.2 ユーザ空間でのメモリ確保とデマンドページング 6.9.3 vmalloc()関連のAPI 6.9.3.1 vmalloc()で確保されたメモリか確認する 6.9.3.2 kvmalloc() 6.9.3.3 ヒュージページvmalloc 6.9.4 kmalloc()とvmalloc()の比較 6.10 メモリ確保APIを使うタイミング 6.10.1 メモリ確保APIの可視化 6.10.2 メモリ確保APIの適切な選択 6.10.3 DMAとCMA 6.11 メモリ回収 6.11.1 ゾーンウォータマークとkswapd 6.11.2 MGLRU(multi-generational LRU)リスト 6.11.3 DAMON(Data Access Monitoring) 6.12 OOMキラー 6.12.1 OOMキラーを意図的に動作させる 6.12.1.1 Magic SysRqキーによりOOMキラーを動作させる 6.12.1.2 メモリ負荷によりOOMキラーを動作させる 6.12.2 overcommit_memory 6.12.2.1 __vm_enough_memory() によるオーバーコミットの確認 6.12.2.2 ケース1:overcommit_memory == 0(OVERCOMMIT_GUESS) 6.12.2.3 ケース2:overcommit_memory == 2(OVERCOMMIT_NEVER)overcommit_ratio == 50 6.12.3 デマンドページングとOOM 6.12.4 OOMスコア 6.12.5 OOMキラーとcgroups 6.13 まとめ 7章 CPUスケジューラ 7.1 基礎知識 7.1.1 KSE 7.1.2 プロセスのステートマシン 7.1.3 POSIXスケジューリングポリシー 7.2 フローの可視化 7.2.1 gnome-system-monitorを使った可視化 7.2.2 perfを使った可視化 7.2.3 他の可視化手法 7.3 スケジューリングクラス 7.3.1 スケジューリングの流れ 7.3.2 次の実行対象の選択 7.3.3 フェアクラス 7.3.4 CPUスケジューラに関する統計情報 7.4 スレッドのスケジューリングポリシーと優先度を取得する 7.5 CPUスケジューリングの内部構造 7.5.1 プリエンプティブカーネル 7.5.2 誰がスケジューラコードを実行するのか 7.5.3 schedule()を呼び出す契機 7.5.3.1 thread_info構造体 7.5.3.2 タイマ割り込みの延長でのTIF_NEED_RESCHEDの設定 7.5.3.3 TIF_NEED_RESCHEDのチェック 7.5.3.4 コアスケジューラコードの概要 7.6 CPUアフィニティマスク 7.6.1 ユーザプロセスのCPUアフィニティマスクを読み書きする 7.6.2 カーネルスレッドのCPUアフィニティマスクを設定する 7.7 スレッドのスケジューリングポリシーと優先度の読み書き 7.8 cgroups 7.8.1 cgroupsのコントローラ 7.8.2 cgroups v2の階層 7.8.2.1 コントローラの有効化、無効化 7.8.2.2 階層内のcgroups 7.8.2.3 systemdとcgroups 7.8.2.4 cgroupsを操作する自作スクリプト 7.8.3 cgroupsによるCPUリソースの制約 7.8.3.1 systemdを活用する方法 7.8.3.2 cgroupsを直接使う 7.9 まとめ Ⅲ部 同期機能 8章 カーネルの同期 8.1 クリティカルセクションと排他実行 8.1.1 クリティカルセクション 8.1.2 グローバル変数のケース 8.1.3 ロックの概念 8.1.4 データ競合の正式な定義 8.2 Linuxカーネル内における並列処理の懸念 8.2.1 SMPとデータ競合 8.2.2 プリエンプティブカーネル、ブロックI/O、データ競合 8.2.3 ハードウェア割り込みとデータ競合 8.2.4 ロックのガイドラインとデッドロック 8.3 mutexとスピンロックのどちらを使用するか 8.3.1 理論上のロックの選定方法 8.3.2 実際のロックの選定方法 8.4 mutex 8.4.1 mutexの初期化 8.4.2 mutexのロックとアンロックのAPI 8.4.3 mutexの使用 8.4.3.1 mutexロックと割り込み許可、割り込み不可スリープ 8.4.3.2 mutexに関するその他のAPI 8.4.3.3 セマフォとmutex 8.4.3.4 優先度逆転とRT-mutex 8.4.3.5 mutexの内部設計 8.5 スピンロック 8.5.1 スピンロックの簡単な使い方 8.5.2 サンプルドライバにおけるスピンロック 8.5.3 アトミックコンテキストでスリープさせた場合の動作確認 8.6 ロックと割り込み 8.6.1 readの途中に割り込みが発生するケース 8.6.2 一部の割り込みを禁止する場合 8.6.3 Linuxにおける割り込みハンドラの要点 8.7 atomic_tとrefcount_t 8.7.1 新しいrefcount_tと古いatomic_t 8.7.2 インタフェース 8.7.3 64ビットのアトミック整数操作 8.7.4 内部の実装 8.8 アトミックRMW操作 8.8.1 ビット操作 8.8.2 ビットマスクの検索 8.9 reader-writerスピンロック 8.9.1 reader-writerスピンロックのインタフェース 8.9.2 reader-writerスピンロックを使う 8.9.3 reader-writerスピンロックの性能 8.9.4 その他の同期メカニズム 8.10 CPUキャッシュとフォルスシェアリング 8.10.1 CPUキャッシュ 8.10.2 キャッシュのリスク 8.10.2.1 キャッシュコヒーレンシ 8.10.2.2 フォルスシェアリング 8.11 ロックフリープログラミング 8.11.1 Per-CPU変数 8.11.2 RCU 8.11.2.1 RCUの仕組み 8.11.2.2 RCUの使用 8.11.2.3 カーネルにおけるRCUの使用状況 8.12 カーネル内におけるロックのデバッグ 8.12.1 ロックデバッグ用のビルド設定 8.12.2 lockdep 8.12.3 lockstat 8.13 メモリバリア 8.14 まとめ 索 引