Linux kernelのMakefileを雰囲気で読んだ話
2020/04/28この記事は,「カーネルビルドでも進捗が見たい!」に入り切らなかった蛇足です.
kbuild-progressの実装を初めた当初は,kernelのMakefileを改造することで,プログレスバーを表示しようとしていました.Linux kernelのビルドシステムであるKbuildはmakeで書かれています.実際にビルドを行っているmakeなら,ビルド全体のタスク数や,残りのタスク数などを知っているはずだと考えたのです.
Makefileを読んでみた
Linux kernelには 63,135行のMakefile が含まれ(linux-5.6.7で計測), なまら複雑です. 私はmakeについて全く詳しくないので(予防線),雰囲気で読んでいくしかありません.がんばります.
vmlinux
ルール
プログレスバーを表示するにあたって,まず,ビルド対象ファイルの一覧的なものがほしいです.もっとも典型的なビルドコマンドであるmake vmlinux
の挙動について考えます.vmlinux
のルールはlinux-5.6.7/Makefile
の
vmlinux: scripts/link-vmlinux.sh autoksyms_recursive $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)
ですね.vmlinux:
がターゲットで,その後に以下のような依存が続いています.
scripts/link-vmlinux.sh
autoksyms_recursive
$(vmlinux-deps)
FORCE
scripts/link-vmlinux.sh
は…なんかvmlinuxをリンクするやつですね(雑).たぶん今回は見なくてもいいでしょう.FORCE
は,このルールの実行を強制する何かです.これも関係なし.
autoksyms_recursive
は怪しい名前ですね.ルールを見ると,
ifdef CONFIG_TRIM_UNUSED_KSYMS
autoksyms_recursive: ...
endif
CONFIG_TRIM_UNUSED_KSYMS
が宣言されていないときは何もしないそうです.CONFIG_TRIM_UNUSED_KSYMS
とは?
このオプションにより、未使用のエクスポートされたシンボルをビルドから削除できます。 https://cateee.net/lkddb/web-lkddb/TRIM_UNUSED_KSYMS.html
なるほど,kernelをダイエットするやつですね.今回は関係ナシ!
vmlinux-deps
変数
ということで$(vmlinux-deps)
があやしい.この変数は…
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_OBJS) $(KBUILD_VMLINUX_LIBS)
ここで作られてますね.おお!ビルド対象の一覧っぽい感じがします!さっそく変数の中身を見てみましょう!
init/built-in.a
usr/built-in.a
arch/x86/built-in.a
...(その他20個のファイル)...
あれ…?なんだか数が少ないですね…トップレベルのオブジェクトファイルしかリストに入っていないようです.じゃあこれらのオブジェクトを生成するルールは…
# The actual objects are generated when descending,
# make sure no implicit rule kicks in
$(sort $(vmlinux-deps)): descend ;
「実際のオブジェクトは下降時に生成されます」,なるほど.たぶんディレクトリを再帰してビルドしていくんですね.
descend
ルール
descendのルールは…
descend: $(build-dirs)
$(build-dirs): prepare
$(Q)$(MAKE) $(build)=$@ \
single-build=$(if $(filter-out $@/, $(filter $@/%, $(single-no-ko))),1) \
need-builtin=1 need-modorder=1
ありました.依存はbuild-dirs
で,3行のコマンドがあります.build-dirs
の中身は…
init usr arch/x86 kernel certs mm fs ipc security crypto block drivers sound net lib arch/x86/lib virt
ほーん.トップディレクトリのリストですね.
コマンドのほうを見てみます.3行ありますが,下2行は変数に何か代入しているだけなので,1行目に注目します.
$(Q)$(MAKE) $(build)=$@ \
いろいろ変数を展開していますね.Q
とMAKE
変数の中身をそれぞれ見てみます.
Q = @
MAKE = make
なるほど,@
コマンドはmakeのログを抑制するやつなので,Q
は静かにしたいときに使う変数です.MAKE
はまんまmake
ですね.つまり,このコマンドは別のmakeコマンドを実行しています.build
変数の定義は…?
linux-5.6.7$ grep -r "build :="
scripts/Kbuild.include:build := -f $(srctree)/scripts/Makefile.build obj
linux-5.6.7/scripts/Kbuild.include:build
にありました.-f $(srctree)/scripts/Makefile.build obj
です.つまり先程のコマンドは…
@make -f $(srctree)/scripts/Makefile.build obj=$@ \
となります.makeの-f
オプションはMakefileを明示的に指定するやつですね.obj変数に$@
をセットしてlinux-5.6.7/scripts/Makefile.build
を実行しています.@
変数はビルドターゲットを表す特殊な変数で,先程のbuild-dirs
変数の中身が入ってきます.つまり
make -f $(srctree)/scripts/Makefile.build obj=init
make -f $(srctree)/scripts/Makefile.build obj=usr
make -f $(srctree)/scripts/Makefile.build obj=arch/x86
...(その他20行のコマンド)...
というコマンドが実行されていきます.
scripts/Makefile.build
というわけで,各トップディレクトリの処理はscripts/Makefile.build
が担当しているようです.このMakefileはどんな処理をしているのでしょうか.ちょっと先頭から読んでみます.
src := $(obj)
...
# Init all relevant variables used in kbuild files so
# 1) they have correct type
# 2) they do not inherit any value from the environment
obj-y :=
obj-m :=
おっ,各トップディレクトリが obj
変数にセットされていますね.そして,続くコメントでは「kbuildファイルで使用されるすべての関連変数を初期化」…
kernelやkernel moduleを開発したことのある方なら,obj-y
やobj-m
という変数を見てニヤリとするはずです.これらは,Linux kernelに自分の書いたプログラムを登録するときに使う変数ですね.kernelの各ディレクトリには それぞれのMakefileがあり,ビルドしたいファイルをこれらの変数に登録しているのです.
obj-y
を調べれば,ビルド対象のオブジェクトがリストできるかも!
obj-y
ではobj-y
はどのように利用されているのでしょうか.Makefile.build
の中にはそれらしいヤツがなかったのですが,
linux-5.6.7$ grep -r "obj-y"
...
scripts/Makefile.lib:real-obj-y := $(foreach m, $(obj-y), $(if $(strip $($(m:.o=-objs)) $($(m:.o=-y)) $($(m:.o=-))),$($(m:.o=-objs)) $($(m:.o=-y)),$(m)))
scripts/Makefile.lib
に,ぽいのがありました.obj
がarch/x86
の時のreal-obj-y
を覗いてみましょう.
arch/x86/entry/built-in.a
arch/x86/events/built-in.a
arch/x86/realmode/built-in.a
arch/x86/kernel/built-in.a
arch/x86/mm/built-in.a
arch/x86/crypto/built-in.a
arch/x86/ia32/built-in.a
arch/x86/platform/built-in.a
arch/x86/net/built-in.a
お!ビルド対象のオブジェクトですね!でも数が少ない…
kbuildにおいてbuilt-in.a
は,各ディレクトリのオブジェクトファイルをまとめたやつです.今回は,arch/x86
の,各サブディレクトリのbuilt-in.a
しかリストされていません.各サブディレクトリのビルドは誰がやるのでしょうか…?
再帰処理
完全に勘 ですが,Makefile.build
は再帰的に呼び出されるのでしょう.試しに「build」とかでMakefile.build
を検索してみると…
# Descending
# ---------------------------------------------------------------------------
PHONY += $(subdir-ym)
$(subdir-ym):
$(Q)$(MAKE) $(build)=$@ \
ほらでた. subdir-ym
とか,名前からして再帰処理関係です.obj
がarch/x86
のときのsubdir-ym
の中身を見てみると,
arch/x86/crypto arch/x86/entry arch/x86/events arch/x86/ia32 arch/x86/kernel arch/x86/mm arch/x86/net arch/x86/platform arch/x86/realmode
あーね.arch/x86
のサブディレクトリでしょう.念のため,arch/x86
以下のディレクトリを確認します.
linux-5.6.7/arch/x86$ find . -type f
./crypto
...
./events
...
./entry
...
うん,ありますね.subdir-ym
は1つ下のディレクトリのリストです.そして,それらに対して自分自身を呼び出していますね.どうやらMakefile.build
は,各ディレクトリに対して呼び出され,そこのobj-y
やobj-m
をビルドしていくということで間違い無さそうです.
結局…
薄々勘づいていたのですが, ビルド対象ファイルの一覧など,どこにもありません. 各ディレクトリのビルド対象ファイルを,各ディレクトリに対して呼び出されたMakefile.build
が知っているだけで, だれも全体を把握していないのです…
Makefileを改造するのは筋が悪いということで,方針を考え直した結果,makeをdry-runするという方法を思いつきました.
他に調べたこと
stackoverflow
こんな質問を見つけました
Kernel compilation progress [closed]
linux kernelのコンパイルの進捗を表示したいんだけど,なんかいい方法ない? https://stackoverflow.com/questions/12251949/kernel-compilation-progress
これに対して,「Makefileを改造すれば?」的な返信がありますが,まぁ面倒ですよね.みんな困ってるんすね〜.
help-make
help-makeのメーリングリストに,似たようなことをやろうとしている人がいました.
Monitoring make progression
makeの進行状況を監視する方法はありますか?
https://lists.gnu.org/archive/html/help-make/2006-09/msg00025.html
に対しての返信では,
Re: Monitoring make progression
問題は,いくつのステップが実行されるかを知らないことです.Makeは,実行される可能性のあるステップの合計数さえ知りません.
そうそう.
最初に「make -n」を使用して,makeにステップを生成させる人のことを聞いたことがあります.それらを実行することで,ステップをカウントし,実際のmakeを実行します.
お!私と同じアプローチですね!
ただし,この方法結構不安定です.Makefileによっては,全てのルールが出力されない場合がありますし,出力されたルールが実際には実行されない場合もあります.また,プロジェクトによっては,ドライランは非常に重いでしょう.
でも,ドライランが今のところ最良の方法です.https://lists.gnu.org/archive/html/help-make/2006-09/msg00026.html
はー,そうなんですね.makeは奥が深そうです.
GOLEM
先程のメーリングリストによれば,makeのドライランは決して完璧な方法ではないようです.
A Robust Approach for Variability Extraction from the Linux Build Systemによれば,Linux kernelのMakefileを解析するGOLEMというツールを開発したそうです(GOLEMは,リンク先のundertakerというツールのサブセットです).
これを使えばより確実にビルド対象ファイルをリストできると考えたのですが,私の環境ではGOLEMがうまく動きませんでした.ツールの使い方が悪いだけかも知れませんが…
まとめ
結局,Makefileをここまで読む必要はなかったのですが,まぁためになったのでよしです.
あーつかれた