FreeBSDカーネル開発シリーズ
| Part1 ビルド | Part2 モジュール | Part3 ドライバ | Part4 システムコール | Part5 DTrace |
|---|---|---|---|---|
| 👈 Now | - | - | - | - |
はじめに
Linuxのカーネルビルドはやったことある人多いと思う。
でもFreeBSDのカーネルビルドはやったことある?
実はFreeBSDの方がLinuxよりカーネル開発に入りやすいんだよ。理由:
- ソースツリーがシンプル
- ドキュメントが充実
- カーネルとユーザーランドが一体で開発されてる
今回はカーネルのビルドから改造まで、ハンズオン形式で解説する。
ソースコードの取得
Gitでクローン
pkg install git
# 安定版(14.2-RELEASE)
git clone --branch releng/14.2 --depth 1 https://git.FreeBSD.org/src.git /usr/src
# 開発版(main)
git clone --depth 1 https://git.FreeBSD.org/src.git /usr/src
ソースツリーの構造
/usr/src/
├── bin/ # 基本コマンド (/bin/ls等)
├── sbin/ # システムコマンド (/sbin/mount等)
├── usr.bin/ # ユーザーコマンド (/usr/bin/*)
├── usr.sbin/ # システム管理コマンド
├── lib/ # ライブラリ
├── sys/ # ★カーネルソース★
│ ├── amd64/ # x86_64アーキテクチャ依存
│ ├── arm64/ # ARM64アーキテクチャ依存
│ ├── conf/ # カーネル設定ファイル
│ ├── dev/ # デバイスドライバ
│ ├── fs/ # ファイルシステム
│ ├── kern/ # カーネルコア
│ ├── net/ # ネットワーク
│ ├── vm/ # 仮想メモリ
│ └── ...
├── share/ # 共有データ
├── contrib/ # サードパーティソース
└── Makefile # トップレベルMakefile
Linuxと比べてシンプル。全部が1つのリポジトリに入ってる。
カーネルの設定
設定ファイル
cd /usr/src/sys/amd64/conf/
ls
# GENERIC GENERIC.hints MINIMAL NOTES
-
GENERIC: 標準的なカーネル設定 -
MINIMAL: 最小構成 -
NOTES: 全オプションの説明
カスタム設定の作成
cp GENERIC MYKERNEL
vi MYKERNEL
# カーネル名を変更
ident MYKERNEL
# 不要なドライバを削除してスリム化
nodevice fdc # フロッピーディスク
nodevice ata # IDE/ATA
nodevice isci # Intel SAS
# デバッグオプション追加
options KDB # カーネルデバッガ
options DDB # 対話型デバッガ
options GDB # リモートGDB
options INVARIANTS # 追加のアサーション
options INVARIANT_SUPPORT
# DTrace有効化
options KDTRACE_HOOKS
options DDB_CTF
# パフォーマンスオプション
options SCHED_ULE # ULEスケジューラ(デフォルト)
options PREEMPTION # プリエンプション
# メモリ制限を増加
options MAXMEM=(16*1024) # 16GBまで
# 特定のファイルシステム無効化
nooptions MSDOSFS # FAT
nooptions CD9660 # ISO9660
設定オプションの確認
# 全オプションの説明
less /usr/src/sys/amd64/conf/NOTES
less /usr/src/sys/conf/NOTES
カーネルのビルド
ビルドコマンド
cd /usr/src
# 設定ファイルを指定してビルド
make buildkernel KERNCONF=MYKERNEL
初回は1時間くらいかかる。コーヒーでも飲んでて。
--------------------------------------------------------------
>>> Kernel build for MYKERNEL started on Mon Jan 1 12:00:00 JST 2024
--------------------------------------------------------------
>>> stage 1: configuring the kernel
config MYKERNEL
Kernel build directory is /usr/obj/usr/src/amd64.amd64/sys/MYKERNEL
>>> stage 2: building everything
make -C /usr/src/sys/MYKERNEL all
cc -c -O2 -pipe -fno-strict-aliasing ...
...(大量の出力)...
--------------------------------------------------------------
>>> Kernel build for MYKERNEL completed on Mon Jan 1 13:00:00 JST 2024
--------------------------------------------------------------
並列ビルド
# CPUコア数に合わせて並列化
make -j$(sysctl -n hw.ncpu) buildkernel KERNCONF=MYKERNEL
インストール
make installkernel KERNCONF=MYKERNEL
--------------------------------------------------------------
>>> Installing kernel MYKERNEL
--------------------------------------------------------------
install -o root -g wheel -m 555 /usr/obj/.../kernel /boot/kernel/kernel
install -o root -g wheel -m 555 /usr/obj/.../kernel.debug /usr/lib/debug/boot/kernel/kernel.debug
...
再起動
reboot
確認
uname -a
# FreeBSD hostname 14.2-RELEASE FreeBSD 14.2-RELEASE ... MYKERNEL amd64
MYKERNELが表示されてれば成功!
ワールド(ユーザーランド)のビルド
カーネルだけじゃなく、ユーザーランド全体もビルドできる。
cd /usr/src
# ワールドをビルド
make -j$(sysctl -n hw.ncpu) buildworld
# インストール
make installworld
# 設定ファイルを更新
mergemaster -Ui
# または etcupdate
etcupdate
簡単なカーネル改造
起動メッセージを変更
vi /usr/src/sys/kern/init_main.c
// 424行目あたり
static void
print_caddr_t(void *data)
{
printf("%s", (char *)data);
}
// この下にカスタムメッセージを追加
static void
print_welcome_message(void *data)
{
printf("\n");
printf("===================================\n");
printf(" Welcome to My Custom FreeBSD! \n");
printf(" Built by Aqua \n");
printf("===================================\n");
printf("\n");
}
SYSINIT(announce, SI_SUB_COPYRIGHT, SI_ORDER_FIRST, print_welcome_message, NULL);
再ビルド&インストール
cd /usr/src
make buildkernel KERNCONF=MYKERNEL
make installkernel KERNCONF=MYKERNEL
reboot
起動時にカスタムメッセージが表示される!
カーネルソースの読み方
重要なファイル
# プロセス管理
/usr/src/sys/kern/kern_proc.c
/usr/src/sys/kern/kern_fork.c
# システムコール
/usr/src/sys/kern/syscalls.master # システムコール定義
/usr/src/sys/kern/kern_descrip.c # ファイルディスクリプタ
# メモリ管理
/usr/src/sys/vm/vm_page.c
/usr/src/sys/vm/vm_fault.c
# ファイルシステム
/usr/src/sys/kern/vfs_syscalls.c
/usr/src/sys/ufs/ffs/ffs_vfsops.c
# ネットワーク
/usr/src/sys/netinet/tcp_input.c
/usr/src/sys/netinet/tcp_output.c
コードを追いかけるコツ
# grep + cscope が便利
cd /usr/src/sys
# cscopeデータベース作成
find . -name "*.[ch]" > cscope.files
cscope -b -q
# cscope起動
cscope -d
# 's' でシンボル検索
# 'g' で定義を検索
# 'c' で呼び出し元を検索
デバッグ方法
DDBカーネルデバッガ
# カーネル設定に追加(前述)
options KDB
options DDB
# 起動時にDDBに入る
# /boot/loader.conf に追加
debug.debugger_on_panic="1"
パニック時やCtrl+Alt+Escで対話型デバッガに入る。
Stopped at breakpoint_here+0x4: ret
db> trace # スタックトレース
db> ps # プロセス一覧
db> show pcpu # CPU情報
db> cont # 続行
printfデバッグ
カーネルではprintfが使える。
#include <sys/param.h>
#include <sys/systm.h>
void my_function(void) {
printf("DEBUG: my_function called\n");
}
KASSERT(アサーション)
#include <sys/param.h>
#include <sys/systm.h>
void my_function(struct proc *p) {
KASSERT(p != NULL, ("my_function: p is NULL"));
// ...
}
INVARIANTSオプションを有効にしてビルドすると、KASSERTが有効になる。
まとめ
FreeBSDカーネル開発の入り口:
- ソースツリーがシンプルで理解しやすい
-
GENERICをコピーしてカスタム設定を作る -
make buildkernelでビルド -
make installkernelでインストール - DDBやprintfでデバッグ
Linuxカーネルより入りやすいと思う。構造がシンプルで、ドキュメントも充実してる。
次回予告
Part2: カーネルモジュールを作って動的にロードする
毎回カーネルを再ビルドするのは面倒。カーネルモジュールを作れば、動的にロード/アンロードできる。「Hello, World」モジュールから実用的なモジュールまで作ってみよう。
この記事が役に立ったら、いいね・ストックしてもらえると嬉しいです!