Linux カーネルの探究: Kconfig の秘密

Linux カーネルの探究: Kconfig の秘密

Linux 構成/ビルド システムがどのように機能するかを深く理解します。

Linux カーネル構成/ビルド システム (Kconfig/kBuild とも呼ばれます) は、Linux カーネル コードが Git に移行されて以来、長い間存在しています。しかし、サポート インフラストラクチャとしてはあまり注目されておらず、日常業務でそれを使用しているカーネル開発者でさえ、実際にはそれについて考えたことがありません。

Linux カーネルがどのようにコンパイルされるかを探るために、この記事では Kconfig/kBuild の内部を詳しく調べ、.config ファイルと vmlinux/bzImage ファイルがどのように生成されるかを説明し、依存関係を追跡するためのスマートなトリックを紹介します。

設定

カーネルを構築する最初のステップは常に構成です。 Kconfig は、Linux カーネルを高度にモジュール化およびカスタマイズ可能にするのに役立ちます。 Kconfig は、ユーザーにいくつかの構成ターゲットを提供します。

設定行指向プログラムを使用して現在の構成を更新する
設定ncursesメニューベースのプログラムを使用して現在の設定を更新する
メニュー構成メニューベースの手順を使用して現在の構成を更新します
xconfig Qtベースのフロントエンドを使用して現在の構成を更新する
gconfig GTK+ベースのフロントエンドを使用して現在の設定を更新する
古い設定提供された.configをベースとして現在の構成を更新します
ローカルmodconfigロードされていない現在の構成の無効化されたモジュールを更新します
ローカルyesconfig現在の構成を更新し、ローカルモッドをコアモッドに変換します
定義設定Archが提供するDefconfigからデフォルトの新しい設定を取得します
保存されたdefconfig現在の設定を ./defconfig (最小設定) として保存します。
すべてnoconfigすべてのオプションに「いいえ」と答える新しい構成
アリエスコンフィグすべてのオプションが「はい」で受け入れられる新しい構成
すべてのmodconfig可能な場合は新しい構成モジュールを選択する
すべて定義設定すべてのシンボルがデフォルト値に設定された新しい構成
ランドコンフィグすべてのオプションにランダムに回答する新しい構成
新しい設定のリスト新しいオプションを一覧表示する
古い定義構成oldconfigと同じですが、プロンプトなしで新しいシンボルをデフォルトとして設定します。
kvmconfig kvmクライアントカーネルサポートの追加オプションを有効にする
設定Xen dom0とゲストカーネルでサポートされているその他のオプションを有効にする
タイニーコンフィグ可能な限り小さなカーネルを構成する

これらのターゲットの中では menuconfig が最も人気があると思います。ターゲットは、カーネルによって提供され、カーネル ビルド プロセス中に生成されるさまざまなホスト プログラムによって処理されます。一部のターゲットには GUI (ユーザーの利便性のため) がありますが、ほとんどのターゲットには GUI がありません。 kconfig 関連のツールとソース コードは、主にカーネル ソース コードの scripts/kconfig/ にあります。 scripts/kconfig/makefile から開始できます。CONF、mconf、nconf など、いくつかのホスト プログラムがあります。 conf を除いて、それぞれが GUI ベースの構成ターゲットの 1 つを担当しているため、conf がそれらのほとんどを処理します。

論理的には、Kconfig インフラストラクチャには 2 つの部分があります。1 つは構成項目を定義するための新しい言語を実装する部分 (カーネル ソース コードの Kconfig ファイルを参照)、もう 1 つは Kconfig 言語を解析して構成操作を処理する部分です。

ほとんどの構成ターゲットの内部フローはほぼ同じです (以下を参照)。

すべての構成項目にはデフォルト値があることに注意してください。

最初のステップでは、ソース ルートの下の Kconfig ファイルを読み取って初期構成データベースを構築し、次に既存の構成ファイルを読み取って、この優先順位に従って初期データベースを更新します。

  • .config
  • /lib/Module/$(shell,uname -r)/.config
  • /etc/カーネル設定
  • /boot/config-$(シェル、uname -r)
  • ARCH_DEFCONFIG
  • ARCH/$(ARCH)/Defconfig

menuconfig またはコマンド ライン ベースの構成 (oldconfig) を介して GUI ベースの構成を実行している場合は、データベースがカスタマイズ内容で更新されます。最後に、構成データベースを .config ファイルにダンプします。

しかし、.config ファイルはカーネル ビルドの最終製品ではありません。そのため、syncconfig ターゲットが存在します。 syncconfig は以前は silentoldconfig というファイルでしたが、古い名前どおりの動作をしなかったため、名前が変更されました。また、これは内部使用用であり、ユーザー向けではないため、リストから削除されました。

syncconfig が実行する処理の例を次に示します。

syncconfig は .config を入力として受け取り、次の 3 つのカテゴリに分類される他のいくつかのファイルを出力します。

auto.conf と tristate.conf は、テキスト処理用のファイルを生成するために使用されます。たとえば、コンポーネントの makefile に次のようなステートメントがある場合があります。

obj-$(CONFIG_GENERIC_CALIBRATE_DELAY) += calibrate.o

autoconf.h は C 言語のソース ファイルで使用されます。

空のヘッダー ファイル include/config/ は、以下で説明する kbuild 中の構成依存関係の追跡に使用されます。

プロファイリング後、どのファイルとコード セグメントがコンパイルされていないかがわかります。

Kビルド

再帰的な make と呼ばれるコンポーネント単位の構築は、GNU では一般的なアプローチです。大規模なプロジェクトを作成および管理します。 KBuild は再帰的な make の良い例です。ソース ファイルを異なるモジュール/コンポーネントに分割することで、各コンポーネントは独自の Makefile によって管理されます。ビルドを開始すると、最上位の Makefile が各コンポーネントの makefile を正しい順序で呼び出し、コンポーネントをビルドして、最終的な実行可能ファイルに収集します。

KBuild はさまざまな種類の makefile を参照します。

  • Makefile はソース ルートの最上位の Makefile にあります。
  • .config はカーネル構成ファイルです。
  • ARCH/$(ARCH)/Makefile は、トップの Makefile を補完する arch Makefile です。
  • scripts/Makefile* は、すべての kbuild makefile に共通するルールを記述します。
  • 最終的に、Kbuildmakefile は約 500 個になります。

最上位の makefile には archmakefile が含まれており、これは .config ファイルを読み取り、サブディレクトリに移動し、make を呼び出して、そこに定義されているルーチンの助けを借りて各コンポーネント makefile を実装します。 scripts/Makefile* は、各中間オブジェクトを構築し、すべての中間オブジェクトを vmlinux にリンクします。コア ファイル Documentation/kbuild/makefiles.txt には、これらの makefile のすべての側面が記述されています。

たとえば、x86-64 上で vmlinux を起動する方法を見てみましょう。

(イラストは Richard Y. Steven のブログに基づいています。著者の許可を得て更新および使用しています。)

vmlinux に入るすべての .o ファイルは、まず変数によって表される独自の組み込み .a に入ります。 KBUILD_VMLINUX_INIT、KBUILD_VMLINUX_Main、KBUILD_VMLINUX_LIBS を収集し、それらを vmlinux ファイルに収集します。

簡略化された Makefile コードを使用して、再帰的な make が Linux カーネルでどのように実装されているかを見てみましょう。

# トップのMakefile内
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps)
        +$(if_changed、link-vmlinux を呼び出す)
# 変数の割り当て
vmlinux 依存関係 := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS)
エクスポート KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
エクスポート KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y)
エクスポート KBUILD_VMLINUX_LIBS := $(libs-y1)
KBUILD_LDS をエクスポート:= arch/$(SRCARCH)/kernel/vmlinux.lds
init-y := init/
ドライバ-y := ドライバ/ サウンド/ ファームウェア/
ネット-y := ネット/
ライブラリ-y := lib/
コアy := usr/
virt-y := virt/
# 対応する組み込み.aに変換する
init-y := $(patsubst %/, %/built-in.a, $(init-y))
core-y := $(patsubst %/, %/built-in.a, $(core-y))
ドライバーy := $(patsubst %/, %/built-in.a, $(drivers-y))
net-y := $(patsubst %/, %/built-in.a, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.a, $(filter-out %.a, $(libs-y)))
virt-y := $(patsubst %/, %/built-in.a, $(virt-y))
# 依存関係を設定します。vmlinux-depsはすべて中間オブジェクトで、vmlinux-dirs
# は偽のターゲットなので、このルールが適用されるたびに、vmlinux-dirsのレシピ
# が実行されます。`info make` の「4.6 Phony Targets」を参照してください。
$(ソート $(vmlinux-deps)): $(vmlinux-dirs) ;
# 変数 vmlinux-dirs は各組み込みのディレクトリ部分です。
vmlinux-dirs := $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \
           $(コア-y) $(コア-m) $(ドライバー-y) $(ドライバー-m) \
           $(net-y) $(net-m) $(libs-y) $(libs-m) $(virt-y)))
# 再帰的なmakeのエントリ
$(vmlinux-ディレクトリ):
        $(Q)$(MAKE) $(ビルド)=$@ 組み込みが必要=1

再帰的なレシピの展開、例:

make -f scripts/Makefile.build obj=init need-builtin=1

これは、make が scripts/Makefile.build に入り、各built-in.a のビルドを続行することを意味します。 scripts/link-vmlinux.sh の助けを借りて、vmlinux ファイルは最終的にソース ルートの下に配置されます。

vmlinux と bzImage を理解する

多くの Linux カーネル開発者は、vmlinux と bzImage の関係を明確に理解していない可能性があります。たとえば、x86-64 では次のようになります。

ソース ルート vmlinux はストリップされ、圧縮されて、piggy.S に配置され、その後、他のピア オブジェクトが arch/x86/boot/compressed/vmlinux にリンクされます。同時に、arch/x86/boot に setup.bin というファイルが生成されます。 config_x86_RELOCS に応じて、再配置情報を含むオプションの 3 番目のファイルが存在する場合があります。

カーネルによって提供される build というコマンドは、これらの 2 つ (または 3 つ) の部分を最終的な bzImage ファイルにビルドします。

依存関係の追跡

KBuild は 3 種類の依存関係を追跡します。

  1. すべての前提条件ファイル (*.c および *.h)
  2. すべての前提条件ファイルで使用されるCONFIG_オプション
  3. ターゲットをコンパイルするためのコマンドライン依存関係。

最初のものはわかりやすいですが、2番目と3番目はどうでしょうか?カーネル開発者は次のようなコード スニペットをよく目にします。

#ifdef CONFIG_SMP
__boot_cpu_id = CPU;
#終了

CONFIG_SMP が変更された場合は、このコードを再コンパイルする必要があります。ソース ファイルをコンパイルするために使用するコマンド ラインも重要です。コマンド ラインが異なると、オブジェクト ファイルも異なる可能性があるためです。

.C ファイルが #include ディレクティブを介してインクルードされる場合、次のようなルールを記述する必要があります。

main.o: 定義.h
レシピ...

大規模なプロジェクトを管理する場合、このようなルールが多数必要になりますが、どれも面倒になる可能性があります。幸いなことに、最近の C コンパイラのほとんどは、ソース ファイル内の #include 行を調べることで判別できます。 GNUコンパイラコレクション(GCC)の場合は、コマンドライン引数を追加するだけです:-MD depfile

# scripts/Makefile.lib内
c_flags = -Wp、-MD、$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
         -include $(srctree)/include/linux/compiler_types.h \
         $(__c_flags) $(modkern_cflags) \
         $(ベース名フラグ) $(モッド名フラグ)

これにより、次の内容の .D ファイルが生成されます。

init_task.o: init/init_task.c include/linux/kconfig.h \
include/generated/autoconf.h include/linux/init_task.h \
インクルード/linux/rcupdate.h インクルード/linux/types.h \
...

ホスト プログラム fixdep は、他の 2 つの依存関係を取得して処理します。 depfile はコマンド ラインを入力として受け取り、ターゲットのコマンド ラインとすべての前提条件 (構成を含む) を記録する Makefile 構文の .cmd ファイルを出力します。次のようになります:

# ターゲットをコンパイルするために使用するコマンドライン
cmd_init/init_task.o := gcc -Wp,-MD,init/.init_task.od -nostdinc ...
...
# 依存ファイル
deps_init/init_task.o := \
$(ワイルドカード include/config/posix/timers.h) \
$(ワイルドカード include/config/arch/task/struct/on/stack.h) \
$(ワイルドカード include/config/thread/info/in/task.h) \
...
 include/uapi/linux/types.h \
 arch/x86/include/uapi/asm/types.h \
 include/uapi/asm-generic/types.h \
...

.cmd ファイルは再帰ビルド プロセスに含まれ、すべての依存関係情報を提供し、ターゲットを再構築するかどうかを決定するのに役立ちます。

その秘密は、Fixdep が depfile (.d ファイル) を解析し、その中のすべての依存関係ファイルを解析し、すべての config_strings のテキストを検索し、それらを対応する空のヘッダー ファイルに変換し、それらをターゲットの前提条件に追加することです。構成が変更されるたびに、対応する空のヘッダー ファイルも更新されるため、kbuild はその変更を検出し、それに依存するターゲットを再構築できます。コマンドラインも記録されるため、前回のコンパイルパラメータと現在のコンパイルパラメータを簡単に比較できます。

将来に向けて

Kconfig/kbuild は、2017 年初頭に新しいメンテナーである山田正弘が参加するまで長い間変更されていませんでしたが、現在 KBuild は再び活発に開発されています。この記事の内容と異なるものがすぐに見つかったとしても驚かないでください。

要約する

以上がこの記事の全内容です。この記事の内容が皆様の勉強や仕事に何らかの参考学習価値をもたらすことを願います。123WORDPRESS.COM をご愛顧いただき、誠にありがとうございます。これについてもっと知りたい場合は、次のリンクをご覧ください。

以下もご興味があるかもしれません:
  • Linuxカーネルのメモリ管理アーキテクチャの詳細な説明
  • Linuxカーネル空間とユーザー空間の実装と分析
  • Linuxカーネルプロセススケジューリング関数schedule()のトリガーと実行タイミングの詳細な説明
  • LinuxはSysctlコマンドを使用してカーネルパラメータを調整します
  • Linuxカーネルパラメータ調整方法
  • Linuxカーネルのブートパラメータの詳細な説明
  • Linuxカーネルタイマーについての簡単な説明
  • Linux のカーネルリンクリストの例の詳細な説明
  • ORACLEをインストールするときにLinuxでカーネルパラメータを設定することの意味についての簡単な説明
  • Linux カーネル デバイス ドライバー Linux カーネル 基本メモの概要

<<:  jsのイベントオブジェクトを深く理解しましょう

>>:  MySQL データベースを最適化する 8 つの方法の詳細な説明 (必読の定番)

推薦する

MySQL が my.cnf を読み込む順序の詳細

目次MySQLがmy.cnfを読み込む順序1. mysql.server の起動方法2. mysql...

Mysql 5.7.17 をインストールした後、MySQL にログインするチュートリアル

mysql-5.7.17 のインストールについては記事の下部で紹介されているので、参考にしてください...

Javascript 仮想 DOM の詳細な説明

目次仮想DOMとは何ですか?なぜ仮想DOMが必要なのでしょうか?仮想 DOM はどのようにして実際の...

Linux (CentOS7) に Tomcat をインストールし、Tomcat をスタートアップ項目として設定します (tomcat8 を例に挙げます)

目次TomcatをインストールするTomcat 圧縮パッケージをダウンロードTomcatには3つの主...

Vue は PDF ファイルのオンライン プレビューを実装します (pdf.js/iframe/embed を使用)

序文現在、私はコースウェア PPT のオンライン プレビューを必要とする高品質のコースに取り組んでい...

Win10 に Tomcat サーバーをインストールし、環境変数を構成する詳細なチュートリアル (画像とテキスト)

目次JDKをダウンロードしてインストールするTomcat 圧縮パッケージをダウンロードTomcatの...

Linux システムの busybox に mkfs.vfat コマンドを移植する

オーディオおよびビデオ ファイルを保存するためのディスク寿命を延ばすには、ディスクをフォーマットする...

Linux のような環境で jdk1.8 をインストールし、環境変数を設定する方法の詳細な説明

設定は非常にシンプルですが、毎回確認しないといけないので、記録だけ残しておきます。 1. インストー...

Vueはソースコード付きのリファレンスライブラリのメソッドを使用します

monaco-editor-vueの公式ソースコードは次のとおりです。インデックス 'mon...

CentOS 8 に MariaDB をインストールするための詳細なチュートリアル

MariaDB データベース管理システムは MySQL のブランチであり、主にオープンソース コミュ...

js オブザーバーモードの紹介と使用

目次定義2. 使用シナリオ3. 例を挙げる4. コーディング定義オブザーバー パターンは 1 対多の...

Linux で Golang をインストールする方法

Go は、シンプルで信頼性が高く、効率的なソフトウェアを簡単に構築できるオープンソース プログラミン...

CentOS 6.5 インストール mysql5.7 チュートリアル

1. 新機能MySQL 5.7 はエキサイティングなマイルストーンです。デフォルトの InnoDB ...

MySQLインデックスを追加する3つの原則を簡単に理解する

1. インデックスの重要性インデックスは、列に特定の値を持つ行をすばやく見つけるために使用されます。...

VMware 仮想マシンの NAT モードを構成する方法

この記事では、VMware仮想マシンのNAT構成プロセスを詳しく説明します。具体的な内容は次のとおり...