60
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

LinuxAdvent Calendar 2019

Day 8

このKernel、どんなKernel?

Last updated at Posted at 2019-12-07

はじめに

この記事は、Linux Advent Calendar 2019 - Qiitaの8日目です。

この記事では、今使っているLinux Kernelがどんなkernelなのか、どんな設定で動いているのかを確認する方法を紹介します。
Linux Kernelは様々な用途に使われています。
そのため、ひとくちにLinux Kernelと言っても、動作はシステムによって全然違います。
現状を把握する手段を知ることで、効率的にKernelのコードリーディングやより良い設定を探す一助になれば幸いです。

また、コメント大歓迎ですので、なんでもお気づきのことがありましたらぜひお気軽にお願いします。

  • この記事で書くこと

    • Linux Kernelの素性を確認する方法
    • Linux Kernelの設定値を確認する方法
  • この記事で書かないこと

    • 各プロセスや各ユーザなどに対する設定(ulimitnicequotactlなど)
    • ユーザ空間の設定(sysconfpathconfなど)
    • ドライバに対する設定(insmod時のparameterなど)
    • 各設定項目の説明

下記6つの項目を紹介しています。
各項目は独立していますので、気になるところからご覧ください。

  1. Kernel Version
  2. CPU Architecture
  3. Kernel Build Config
  4. Command-line Parameters
  5. sysctl
  6. Device Tree

Kernel Version

まずはどのversionのkernelが使われているのかを見てみましょう。
versionを知ることで、特定の機能や修正が入っているかを調査の材料にできます。

Kernel Versionの確認方法 uname -r

uname -rコマンドを実行することで、実行中のKernel Versionを取得できます。
以下は、素のLinux Kernel 5.3.10を使っているときの結果です。

$ uname -r
5.3.10

環境によってはuname -rを実行すると、上記のような3つの数字の後ろにいろいろくっついてるかも知れません。
例えば、以下はUbuntu 19.10での実行結果です。

$ uname  -r
5.3.0-19-generic

この場合、version 5.3.0のLinux kernelをベースに、Ubuntu 19.10用に修正したkernel、ということになります。
どのような修正がされているかは、どこかで公開されているはずです。
Ubuntuの場合は、source codeのパッケージおよびdiffが配布されています。

versionの後ろの文字列(19-generic)の意味は、distributionごとに違います。
Ubuntuの場合はこちらに説明があります。

なお、versionについては他にもたくさん確認方法があり、ここに詳しい記事がありますので、ぜひこちらも見てみてください。
またunameコマンドの中身については、Linux Advent Calendar 2019の1日目で詳しく解説してくれているので、そちらもぜひご覧ください。

Kernel Versionに関する参考ページ

CPU Architecture

続いて、どのCPU向けにビルドされたkernelかを確認してみます。

Linux Kernelの文脈だと、「どのCPU向けか」というのをArchitectureとかarchとか表現することが多いです。
Architectureはいろいろな意味で使われますが、ここではCPUのISA(命令セットアーキテクチャ)のことですね。
例えば、x86_64armなどがあります。

Kernelを意識するときにCPU Architectureがわかってないケースはあまりないかも知れません。
でも、実は意外なArchitecture向けのkernelが動作していることがあります。
念の為に確認しておきましょう。

CPU Architectureの確認方法 uname -m or arch

CPU Architectureを確認するためのコマンドには、uname -marchがあります。
2つのコマンドはどちらもcoreutilsというパッケージで提供されており、実装は共通なので好きな方を使えばいいです。

PC(Ubuntu 18.04)の場合は、だいたいx86_64と表示されると思います。

$ uname -m
x86_64

Raspberry Pi 3 ModelB+(Raspbian)で試してみると、armv7lと表示されます。

$ uname -m
armv7l

実はRaspberry Pi 3 ModelB+には、Cortex-A53という64bitのARM CPUが搭載されています。
だったら64bit用のkernelが動いていると思ってしまいますが、その場合はaarch64と表示されるはずです。
つまり、Raspbianでは32bit用にビルドしたkernelが32bit互換モードで動作している、ということですね。
(推測ですが、おそらくRasPi2やRasPi zeroなど32bit CPUのモデルと共通化するためでしょう)

Kernelのarchディレクトリ

CPU Architectureを確認できたので、今度は自分が使っているCPU Architectureの処理が、
Kernelソースコード上でどこに置かれているかを見てみましょう。

Kernelのソースコードを見てみると、arch/ディレクトリ以下に
CPU Architectureごとのディレクトリがあることがわかります。

$ ls -F arch/
Kconfig  arm/	 csky/	   ia64/	mips/	openrisc/  riscv/  sparc/      x86/
alpha/	 arm64/  h8300/    m68k/	nds32/	parisc/    s390/   um/	       xtensa/
arc/	 c6x/	 hexagon/  microblaze/	nios2/	powerpc/   sh/	   unicore32/

先程紹介したuname -mコマンドの結果をちょっとだけ置換したものがディレクトリ名になります。
置換はscripts/subarch.includeというMakefileで行われます。
例えば、x86_64向けのkernelをビルドするときは、x86/ディレクトリが使われるわけですね。
同じように、Raspbianのarmv7lの場合は、arm/ディレクトリが使われます。

scripts/subarch.include
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
				  -e s/sun4u/sparc64/ \
				  -e s/arm.*/arm/ -e s/sa110/arm/ \
				  -e s/s390x/s390/ -e s/parisc64/parisc/ \
				  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
				  -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
				  -e s/riscv.*/riscv/)

CPU Architectureの参考ページ

Kernel Build Config

続いては、Kernel Build Configです。
人によってはコンパイルスイッチとか#ifdefとか言ったほうがしっくりくるかも知れません。
単にKernel Configとか言ったときは、これのことを指す場合が多いです。

先にも述べたようにLinux Kernelは幅広い用途で使われていますが、なんでもできる万能Kernelを作ると、イメージサイズがいたずらに大きくなってしまいます。
そのため、使わない機能をコンパイル対象外にすることで、サイズを節約しています。

デバッグ機能もkernel build configでOFF/ONする場合が多いので、どのようなデバッグ機能が使えるかを知ることもできます。

Kernel Build Configの確認方法 1. /boot/config-X.Y.Z

Linux distributionによっては、/boot以下にkernel imageと一緒に残してくれています。
例えば、Ubuntu19.10では、/boot/config-5.3.0-19-genericというファイルに保存されています。
CentOS 8, Debian 10でも同等のファイルがあります。

$ head /boot/config-$(uname -r)
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86 5.3.0-19-generic Kernel Configuration
#

#
# Compiler: gcc (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008
#
CONFIG_CC_IS_GCC=y
CONFIG_GCC_VERSION=90201

Kernel Build Configの確認方法 2. /proc/config.gz

CONFIG_IKCONFIG_PROCを有効にしているkernelでは、
Kernel Build Configを/proc/config.gzで確認することができます。

残念ながらあまり使える環境はないです。
ただ動作中のkernelに教えてもらうため、より確実な確認方法です。
使える環境では積極的に使いましょう。

例えば、Androidで使用されているLinux Kernelではこの方法が使えることが多いです。
以下は、Android 10のEmulatorで確認したときの結果です。
(gzip圧縮されているのでzcatコマンドを使っていることに注意してください)
Samsung Galaxy Note8 (Android 9)でも、/proc/config.gzが有効になっていました。

$ zcat /proc/config.gz | head
#
# Automatically generated file; DO NOT EDIT.
# Linux/x86_64 4.14.112 Kernel Configuration
#
CONFIG_64BIT=y
CONFIG_X86_64=y
CONFIG_X86=y
CONFIG_INSTRUCTION_DECODER=y
CONFIG_OUTPUT_FORMAT="elf64-x86-64"
CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"

Kernel Build Configにはどんな項目があるの?

Kernelのソースコードに含まれる、Kconfigというファイルで定義されています。
依存関係や各項目の説明などもそこに記載されています。

またmake xconfigを実行すると、各設定項目の説明を見ながら一覧できるので、おすすめです。

Screenshot from 2019-12-06 12-07-55.png

Ctrl-fで検索もできます。

Screenshot from 2019-12-06 12-10-53.png

ごくごく一部ですが、いくつかよく使う項目を紹介します。

  • CONFIG_IKCONFIG_PROC
    Kernel Build Configを/proc/config.gz経由で取得できるようにします。
    Kernel Build Configを色々いじるときにはまず有効にしておくとわかりやすいです。

  • CONFIG_DEBUG_INFO
    デバッグシンボルを付与したカーネルイメージを生成するようになります。
    gdbを使ったKernelデバッグをするときに必要です。

  • CONFIG_FTRACE
    FtraceというKernelの動作を解析するための機能を有効にします。
    LinuxにFtraceを導入する - Qiitaに詳しく解説してくれています。

  • CONFIG_KPROBE
    動作中のKernelにバイナリパッチを当てるための仕組みである、Kprobeを有効にします。
    Kprobeをもう少し使いやすくしたツール、SystemTapを使うためにも必要です。

Kernel Build Configの変更方法

Kernel Build Configを変更するためには、Kernelの再ビルドが必要です。

上述の方法で取得した、現状の.configをベースにmake menuconfigmake xconfig等で編集し、Kernelを再ビルドするのが一般的な変更方法です。

ただし、.configはビルド時に生成されるファイルなので、ソースコード管理の対象外にすべきです。
make menuconfigで目的の.configが作れたら、make savedefconfigで出力されるdefconfigをコミットするようにしましょう。
make savedefconfigは、その時点の.configを再生成するための最小限のconfigを出力してくれます。

例えば、defconfigarch/x86/configs/mytest_defconfigにコミットしておけば、次からはmake mytest_defconfigとすることで、開発メンバみんなが編集後の.configを生成することができます。


$ make savedefconfig
$ mv defconfig arch/x86/configs/mytest_defconfig
$ mv .config .config.back
$ make mytest_defconfig
#
# configuration written to .config
#
$ diff .config .config.back

Kernel Build Configの参考ページ

Command-line Parameters

続いて、Command-line Parametersを紹介します。
Command-line Parametersとは、Kernelの起動時に引数として指定するオプションのことです。
そのためbootloaderが指定するのが一般的です。

なお、ここまでに紹介した、Kernel Version・CPU architecture・Kernel Build Configは、3つともkernelコンパイル時に決定するものでした。
対してここから紹介するのは、Kernel自体を変更することなく動作を変える仕組みです。

Command-line Parametersの確認方法 1. /proc/cmdline

/proc/cmdlineでCommand-line Parametersを確認することができます。

$ cat /proc/cmdline
BOOT_IMAGE=/boot/vmlinuz-5.3.7+ root=UUID=71b4dc0a-e498-41f4-9cd2-a81e8c03cda8 ro quiet splash vt.handoff=1

Command-line Parametersの確認方法 2. dmesg

dmesgにも出力されるので、ログしか残っていない状況でも確認可能です。

$ dmesg | grep "Kernel command line"
[    0.104775] Kernel command line: BOOT_IMAGE=/boot/vmlinuz-5.3.7+ root=UUID=71b4dc0a-e498-41f4-9cd2-a81e8c03cda8 ro quiet splash vt.handoff=1

Command-line Parametersにはどんな項目があるの?

Command-line Parametersの設定項目については、ドキュメントがあります。

ごくごく一部ですが、参考にいくつか紹介します。
他にもたくさんあるので、ぜひドキュメントも読んでみてください。

  • loglevel
    コンソールに出力するkernel logのレベルを変更できます。
    起動直後の不具合解析時に変更することがよくあります。

  • log_buf_len
    kernel logのリングバッファサイズを変更できます。
    ログが流れてしまうときに、サイズを大きく取ることでログを見ることができるようになります。
    デフォルト値はCONFIG_LOG_CPU_MAX_BUF_SHIFTで変更可能ですが、一時的に増やす場合はこちらを使います。

  • mem
    kernelが使うメモリサイズを指定することができます。
    メモリが少ない状況をシミュレートするときや、メモリマップが怪しいときなどに使います。

  • ftrace
    FtraceというKernelの動作を解析するための機能を、起動直後から有効にします。
    起動直後のkernelの振る舞いを解析するときに使います。

Command-line Parametersの参考ページ

sysctl

次に紹介するsysctlは、Kernel Parameterを実行中に変更する仕組みです。
Command-line parametersのように起動時だけではなく、いつでも変更できます。

sysctlの確認方法 1. sysctl

sysctl -aを実行することで、すべてのparameterの現在値を確認することができます。
いくつかparameterを参照するためにはroot権限が必要です。

$ sudo sysctl -a | head
abi.vsyscall32 = 1
debug.exception-trace = 1
debug.kprobes-optimization = 1
dev.cdrom.autoclose = 1
dev.cdrom.autoeject = 0
dev.cdrom.check_media = 0
dev.cdrom.debug = 0
dev.cdrom.info = CD-ROM information, Id: cdrom.c 3.20 2003/12/17
dev.cdrom.info = 
dev.cdrom.info = drive name:	

sysctlの確認方法 2. /proc/sys/

sysctlで設定できるparameterは、全て/proc/sys以下にファイルとして公開されています。
このファイルを読み出すことでparamter値の確認が、書き込むことで変更ができます。
というか、sysctlコマンドも内部では、/proc/sys経由で確認・変更しています。

$ find /proc/sys -type f | head | xargs -I@ sh -c 'echo "@ = $(cat @ | head -1)"'
/proc/sys/abi/vsyscall32 = 1
/proc/sys/debug/exception-trace = 1
/proc/sys/debug/kprobes-optimization = 1
/proc/sys/dev/cdrom/autoclose = 1
/proc/sys/dev/cdrom/autoeject = 0
/proc/sys/dev/cdrom/check_media = 0
/proc/sys/dev/cdrom/debug = 0
/proc/sys/dev/cdrom/info = CD-ROM information, Id: cdrom.c 3.20 2003/12/17
/proc/sys/dev/cdrom/lock = 1
/proc/sys/dev/hpet/max-user-freq = 64

変更可能なparameterなのか、root権限は必要なのか、を事前に知りたいときは、
/proc/sys以下のファイルのパーミッションを見ることで確認できます。

下記を見ると、fs.file-maxは書き込み可能だけど、
fs.file-nrは書き込み不可だということがわかります。
また、fs.file-maxを変更するためにはroot権限が必要だ、ということもわかりますね。

$ ls -l /proc/sys/fs/file-*
-rw-r--r-- 1 root root 0 12月  6 14:01 /proc/sys/fs/file-max
-r--r--r-- 1 root root 0 12月  6 14:01 /proc/sys/fs/file-nr

sysctlにはどんな項目があるの?

sysctlで設定できる項目についても、ドキュメントがあります。

こちらもごくごく一部ですが、参考にどんな項目があるか紹介します。

  • fs.file-max
    システム全体で開くことのできるファイル数を、確認・変更することができます。

  • fs.file-nr
    現在開いているファイル数と、システム全体で開くことのできるファイル数を確認することができます。
    先述のとおり、read-onlyです。
    例えば、15840 0 3231322となっていた場合は、
    現在開いているファイル数が15840で、システム全体で開くことのできるファイル数が3231322です。
    真ん中の0は、互換性のために残っているだけで常に0なので、気にしなくていいです。
    システムとしてfd leakが発生していないか確認するときなどに使います。

  • fs.nr_open
    1プロセスあたりでopenできるファイル数です。
    ただし、実際にはプロセスごとに設定されるulimitでより厳しく制限されていることが多いです。
    ulimitによる制限は、/proc/${PID}/limitsで確認することができます。

  • kernel.print-fatal-signals
    この値を1にすると、プロセスが異常終了したときにプロセス名とレジスタ値をkernel logに残します。

  • kernel.core_pattern
    プロセスが異常終了したときのCoredumpを残す方法を設定できます。
    Coredumpのファイル名を変更するだけではなく、特定のプログラムにリダイレクトすることもできたりします。
    Linux Effective Coredump - Qiitaにて詳しく解説してくれています。

  • vm.drop_caches
    値を書き込んだときにkernelのキャッシュを解放します。
    この項目がちょっと変わっているのは、読み出した値に意味はなく、書き込んだときに処理が走る点です。
    Linuxのdrop_cachesにwriteした時の動きを追う - Qiitaにて詳しく解説してくれています。
    この項目のファイルパーミッションはread-writeになっていますが、本当はwrite-onlyであるべきですね。

sysctlに設定した値はいつまで有効なの?

基本的にsysctlによる設定は、kernelのグローバル変数に保存されるだけなので、システムを再起動したら忘れてしまいます。

永続的に設定するための仕組みはディストリビューションによって異なりますが、多くのディストリビューションでは、/etc/sysctl.confを起動時にデフォルト値として読み込みます。
これは、システム起動時にinitスクリプトからsysctl --systemを実行することで実現されています。

Androidの場合は、起動時の処理を記述するinitrcスクリプトから直接/proc/sysに書き込みます。

/init.rc
on early-init
    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

sysctlの参考ページ

Device Tree

最後は、Device Treeを確認してみます。

Device Treeとは、ハードウェア構成情報です。
ARMのCPUを使った組込系のシステムでよく使われています。

組込みシステムではデバイスのメモリアドレスや割り込み番号などがシステムごとに異なります。
それらをデバイスドライバのソースコードに埋め込んでしまうと、読みにくくメンテナンスしにくくなってしまいます。
そのためこれらのハードウェア構成情報は、ソースコードから独立したDevice Tree Source(.dtsファイル)に記述する方法が一般的です。

試しに5.3.10.dtsファイルの数のarchごとのランキングを見てみると、armがダントツですね。
次がpowerpcになっています。
Device Treeは、もともとpowerpc向けに開発されたらしいので、そのためでしょうね。

$ find arch/ -name *.dts | cut -d '/' -f 2 | uniq -c | sort -n -r
   1176 arm
    265 arm64
    200 powerpc
     50 mips
     17 arc
      7 xtensa
      5 c6x
      3 h8300
      2 openrisc
      2 nios2
      1 x86
      1 sh
      1 riscv
      1 nds32
      1 microblaze

ではPCの世界ではどうしているかというと、UEFI BIOSからACPI規格に従ってハードウェアの情報をもらうらしいですが、よく知りません。
ごめんなさい。

Device Treeの確認方法 /sys/firmware/fdt

Device Treeを使うシステムの場合、
/sys/firmware/fdtにFDT形式に変換されたDevice Treeがあります。
FDTとは、Flattend Device Treeの略で、DTB(Device Tree Blob)とも言います。
dtc(Device Tree Compiler)というコマンドで、FDTを.dtsファイルに変換することができます。

組込みシステムの場合/sys/firmware/fdtをPCに転送してから、PCで.dtsに変換することも可能です。
/proc/device-tree以下に、ファイルシステムに展開された状態のDevice Treeもありますが、私は/sys/firmware/fdtの方が取り回しやすいので便利だと思います。

以下は、Raspberry Pi 3 ModelB+ (Raspbian)でのDevice Treeの確認結果です。

$ sudo dtc /sys/firmware/fdt 2> /dev/null | head
/dts-v1/;

/memreserve/	0x0000000000000000 0x0000000000001000;
/ {
	memreserve = < 0x3b400000 0x4c00000 >;
	serial-number = "00000000c97f6276";
	compatible = "raspberrypi,3-model-b\0brcm,bcm2837";
	model = "Raspberry Pi 3 Model B Rev 1.2";
	interrupt-parent = < 0x01 >;
	#address-cells = < 0x01 >;

Device Treeにはどんな項目があるの?

Device Treeの仕様書はこちらに公開されています。

ただし、実際Device Treeに書かれた設定値がどのような意味をもつかは、対象のハードウェア仕様およびデバイスドライバの仕様に大きく依存します。
意外な使われ方がされていることも結構あるので、Device Treeを見るときはできるだけその値を使う実装も確認しましょう。

Device Treeの参考ページ

おわりに

最後まで読んでくださり、ありがとうございます。

設定は、直接的にKernelの振る舞いを変えることができるので、Kernelのコードリーディングの取っ掛かりとしてとても良いと思っています。
設定項目名でgrepしたらすぐに関連実装が見つかりますし、なにより設定値を変えて動作を見てみるのが簡単です。

この記事がどなたかの参考になれば、とてもうれしいです。

60
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
60
48

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?