独学Linux　カーネルビルドをやってみよう

# はじめに
本記事はこれからLinuxカーネルをビルドするするための入門記事になります。

Linuxカーネルを学ぶことにより得られる恩恵は大きいものです。
Linuxカーネルをビルドすることで以下のようなことが実現できます。

- 不要なモジュールを外すことでメモリの消費を抑えてシステムを最適化
- デフォルトではカーネルに組み込まれていない機能や最新機能を有効化
- カーネルがサポートしていないデバイスが利用可能
- ソースコードを変更してカスタマイズも自由自在

LinuxカーネルのビルドはVirtualBoxなど仮想環境を使用することで、簡単に検証することができます。

本記事ではVirtualBoxを使用してカーネルのバージョンをアップ手順について記載しています。

## 環境
本記事の環境について以下に記載します。

前提としてVirtualBoxで仮想マシンを作成し、Ubuntuをインストールします。
仮想マシンの作成やUbuntuのインストールについては割愛します。

- ホストOS：macOS Big Sur 11.2.3
- 仮想環境：VirtualBox　6.1.18
- ゲストOS：Ubuntu 20.04.2 LTS

**留意事項**
ホストの環境やビルド設定により、`make`の時間に影響します。
また、仮想マシンのディスク容量が少なくて、取り込むモジュールが多い場合、容量不足によりビルドが止まってしまうことがあります。検証する場合は以下について考慮すると良いと思います。

- ビルド実行時はホストOSをスリープしない設定にする
- 仮想マシンのディスクはlvmなど拡張しやすい設計にする

もし、ビルド実行時にディスク容量が不足した場合、lvm環境であればディスクを拡張することでビルドの継続ができます。

- `lvextend`コマンドで論理ボリュームを追加（例：20GB）   
`# lvextend -L +20g /dev/ubuntu-vg/ubuntu-lv `

```console
Size of logical volume ubuntu-vg/ubuntu-lv changed from 28.00 GiB (7168 extents) to 48.00 GiB (12288 extents).
  Logical volume ubuntu-vg/ubuntu-lv successfully resized.
```

- ファイルシステムの再作成  
`# resize2fs /dev/ubuntu-vg/ubuntu-lv `

```console
Filesystem at /dev/ubuntu-vg/ubuntu-lv is mounted on /; on-line resizing required
old_desc_blocks = 4, new_desc_blocks = 6
The filesystem on /dev/ubuntu-vg/ubuntu-lv is now 12582912 (4k) blocks long.
```

## カーネルビルド手順
本記事ではカーネルのソースアーカイブから最新のソースを取得してビルドします。

コマンドの実行はrootユーザーで行っています。

カーネルビルド実行前のバージョンは以下になります。

- バージョン確認  
`# uname -r`

```console
5.4.0-72-generic
```

### パッケージのインストール
はじめにカーネルビルドに必要なパッケージをインストールします。
LinuxカーネルのソースコードはC言語でほとんど書かれているため、Cコンパイラが必要です。

- パッケージのインストール   
`# apt-get update && apt install -y build-essential bc bison flex libelf-dev libssl-dev libncurses5-dev`

`build-essential`はカーネルビルドに必要なパッケージ一式がインストールされます。`build-essential`をインストールすることで`gcc`などのコンパイラがインストールされます。

また、カーネルのビルドに必要なパッケージの要件については、ダウンロードしたソースアーカイブの`Documentation/process/changes.rst`に記載があります。

### カーネルのダウンロード
[The Linux Kernel Archives](https://www.kernel.org/)より、最新のLinuxカーネルをダウンロードします。

本記事時ではlongtermの5.4.109のソースを使用しています。

![スクリーンショット 2021-04-10 22.55.52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/bacccade-af6c-e408-585e-54c82a0ff360.png)

longtermはUpstreamで取り込まれたバグフィックスや、コミュニティによるメンテナンスが行われています。カーネルリリースのカテゴリの説明については、[Active kernel releases](https://www.kernel.org/category/releases.html)を参照。

Linuxカーネルをダウンロード後、scpなどを使用して作成した仮想マシンに資材を転送します。
資材転送後、仮想マシンにログインを行い資材を展開します。

- 資材の転送   
`# scp linux-5.4.109.tar.xz ubuntu@＜IPアドレス＞:/tmp`   
- 資材の展開    
`# cd /usr/local/src/`  
`# mv /tmp/linux-5.4.109.tar.xz ./`  
`# tar xvf linux-5.4.109.tar.xz && cd linux-5.4.109/`  

tarballをダウンロードすると、拡張子は[**xz**](https://ja.wikipedia.org/wiki/Xz_(%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%83%E3%83%88))になっています。xz はLZMA/LZMA2圧縮アルゴリズムを利用していて、gzipなどと比べると圧縮率が高いです。

### ビルド設定
カーネル機能の有効化や、モジュールの取り込みについては、`make`コマンドでビルドの設定を行います。

ビルド設定には`.config`ファイルが必要です。
本記事ではビルドを簡単に行うために、`/boot`配下のconfigファイルをコピーして作成します。

- configファイルの作成
`# cp /boot/config-5.4.0-72-generic .config`

- config設定
`CONFIG_SYSTEM_TRUSTED_KEYS`の値が入っているとエラーで`make`が失敗するため、値を削除します。

`# vi .config`

```config
# 変更前
CONFIG_SYSTEM_TRUSTED_KEYS="debian/canonical-certs.pem"

# 変更後
CONFIG_SYSTEM_TRUSTED_KEYS=""
```

- 参考  
`CONFIG_SYSTEM_TRUSTED_KEYS`に値が入っている場合、エラーでmakeが止まり以下の様なメッセージが出力されます。

```console
make[1]: *** No rule to make target 'debian/canonical-certs.pem', needed by 'certs/x509_certificate_list'.  Stop.
make: *** [Makefile:1810: certs] Error 2
```

`make`コマンドのビルド設定には他にもやり方があります。
例えば、`make menuconfig`というターゲットを実行すると、以下の様な様な画面が出力されます。

![スクリーンショット 2021-04-18 19.15.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/5f1000cf-eb98-6d71-2842-e11a67121bef.png)

このターゲットはカテゴライズされた設定項目毎に、メニュー形式でビルド設定を行うことができます。

### make
`make`コマンドはLinuxのソフトウェアなどをインストールするために必要なコマンドです。

`make`コマンドを実行するとソースコードファイルをコンパイルし、オブジェクトファイルに変換します。コンパイルしたオブジェクトファイルとライブラリを、リンカでリンクすることで実行形式ファイルに変換します。

`make`を使用する準備をするには、プログラム内のファイル間の関係と状態を記述したMakefileというファイルを作成する必要があります。ダウンロードしたLinuxカーネルのソースには、最初からMakefileが存在するため、新規の作成は不要です。

はじめに`make`を実行するためのコンパイル設定を行います。
本記事では簡単に設定を行うために、現在ロードしているモジュールをベースにするために`make localmodconfig`を指定します。`make localmodconfig`を指定すると、ロード済みのモジュールに不要なオプションは無効にします。よって、ロードされていないモジュールを取り込まない様にするため、カーネル自体が小さくなりコンパイル時間を短縮できます。

- コンパイル設定  
`# make localmodconfig`

コマンドを実行すると、一部について対話的に入力が求められます。デフォルト設定の場合は`Enter`を押します。

```console
 HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.[ch]
  HOSTCC  scripts/kconfig/lexer.lex.o
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTLD  scripts/kconfig/conf
using config: '.config'
module vboxguest did not have configs CONFIG_VBOXGUEST
*
* Restart config...
*
*
* PCI support
*
PCI support (PCI) [Y/n/?] y
  PCI Express Port Bus support (PCIEPORTBUS) [Y/n/?] y
    PCI Express Hotplug driver (HOTPLUG_PCI_PCIE) [Y/n/?] y
    PCI Express Advanced Error Reporting support (PCIEAER) [Y/n/?] y
      PCI Express error injection support (PCIEAER_INJECT) [N/m/y/?] n
      PCI Express ECRC settings control (PCIE_ECRC) [N/y/?] n
    PCI Express ASPM control (PCIEASPM) [Y/n/?] y
      Debug PCI Express ASPM (PCIEASPM_DEBUG) [N/y/?] (NEW) 
      Default ASPM policy
      > 1. BIOS default (PCIEASPM_DEFAULT)
        2. Powersave (PCIEASPM_POWERSAVE)
        3. Power Supersave (PCIEASPM_POWER_SUPERSAVE)
        4. Performance (PCIEASPM_PERFORMANCE)
      choice[1-4?]: 1
    PCI Express Downstream Port Containment support (PCIE_DPC) [Y/n/?] y
    PCI Express Precision Time Measurement support (PCIE_PTM) [Y/n/?] y
    PCI Express Bandwidth Change Notification (PCIE_BW) [N/y/?] n
  Message Signaled Interrupts (MSI and MSI-X) (PCI_MSI) [Y/?] y
  Enable PCI quirk workarounds (PCI_QUIRKS) [Y/n/?] y
  PCI Debugging (PCI_DEBUG) [N/y/?] n
  Enable PCI resource re-allocation detection (PCI_REALLOC_ENABLE_AUTO) [Y/n/?] y
  PCI Stub driver (PCI_STUB) [N/m/y/?] n
  PCI PF Stub driver (PCI_PF_STUB) [N/m/y/?] n
  Xen PCI Frontend (XEN_PCIDEV_FRONTEND) [N/m/y/?] n
  PCI IOV support (PCI_IOV) [Y/n/?] y
  PCI PRI support (PCI_PRI) [Y/?] y
  PCI PASID support (PCI_PASID) [Y/?] y
  PCI peer-to-peer transfer support (PCI_P2PDMA) [N/y/?] n
*
* PCI GPIO expanders
*
AMD 8111 GPIO driver (GPIO_AMD8111) [N/m/y/?] n
BT8XX GPIO abuser (GPIO_BT8XX) [N/m/y/?] (NEW) 
OKI SEMICONDUCTOR ML7213 IOH GPIO support (GPIO_ML_IOH) [N/m/y/?] n
ACCES PCI-IDIO-16 GPIO support (GPIO_PCI_IDIO_16) [N/m/y/?] n
ACCES PCIe-IDIO-24 GPIO support (GPIO_PCIE_IDIO_24) [N/m/y/?] n
RDC R-321x GPIO support (GPIO_RDC321X) [N/m/y/?] n
*
* PCI sound devices
*
PCI sound devices (SND_PCI) [Y/n/?] y
  Analog Devices AD1889 (SND_AD1889) [N/m/?] n
  Avance Logic ALS300/ALS300+ (SND_ALS300) [N/m/?] n
  Avance Logic ALS4000 (SND_ALS4000) [N/m/?] n
  ALi M5451 PCI Audio Controller (SND_ALI5451) [N/m/?] n
  AudioScience ASIxxxx (SND_ASIHPI) [N/m/?] n
  ATI IXP AC97 Controller (SND_ATIIXP) [N/m/?] n
  ATI IXP Modem (SND_ATIIXP_MODEM) [N/m/?] n
  Aureal Advantage (SND_AU8810) [N/m/?] n
  Aureal Vortex (SND_AU8820) [N/m/?] n
  Aureal Vortex 2 (SND_AU8830) [N/m/?] n
  Emagic Audiowerk 2 (SND_AW2) [N/m/?] n
  Aztech AZF3328 / PCI168 (SND_AZT3328) [N/m/?] n
  Bt87x Audio Capture (SND_BT87X) [N/m/?] n
  SB Audigy LS / Live 24bit (SND_CA0106) [N/m/?] n
  C-Media 8338, 8738, 8768, 8770 (SND_CMIPCI) [N/m/?] n
  C-Media 8786, 8787, 8788 (Oxygen) (SND_OXYGEN) [N/m/?] n
  Cirrus Logic (Sound Fusion) CS4281 (SND_CS4281) [N/m/?] n
  Cirrus Logic (Sound Fusion) CS4280/CS461x/CS462x/CS463x (SND_CS46XX) [N/m/?] n
  Creative Sound Blaster X-Fi (SND_CTXFI) [N/m/?] n
  (Echoaudio) Darla20 (SND_DARLA20) [N/m/?] n
  (Echoaudio) Gina20 (SND_GINA20) [N/m/?] n
  (Echoaudio) Layla20 (SND_LAYLA20) [N/m/?] n
  (Echoaudio) Darla24 (SND_DARLA24) [N/m/?] n
  (Echoaudio) Gina24 (SND_GINA24) [N/m/?] n
  (Echoaudio) Layla24 (SND_LAYLA24) [N/m/?] n
  (Echoaudio) Mona (SND_MONA) [N/m/?] n
  (Echoaudio) Mia (SND_MIA) [N/m/?] n
  (Echoaudio) 3G cards (SND_ECHO3G) [N/m/?] n
  (Echoaudio) Indigo (SND_INDIGO) [N/m/?] n
  (Echoaudio) Indigo IO (SND_INDIGOIO) [N/m/?] n
  (Echoaudio) Indigo DJ (SND_INDIGODJ) [N/m/?] n
  (Echoaudio) Indigo IOx (SND_INDIGOIOX) [N/m/?] n
  (Echoaudio) Indigo DJx (SND_INDIGODJX) [N/m/?] n
  Emu10k1 (SB Live!, Audigy, E-mu APS) (SND_EMU10K1) [N/m/?] n
  Emu10k1X (Dell OEM Version) (SND_EMU10K1X) [N/m/?] n
  (Creative) Ensoniq AudioPCI 1370 (SND_ENS1370) [N/m/?] n
  (Creative) Ensoniq AudioPCI 1371/1373 (SND_ENS1371) [N/m/?] n
  ESS ES1938/1946/1969 (Solo-1) (SND_ES1938) [N/m/?] n
  ESS ES1968/1978 (Maestro-1/2/2E) (SND_ES1968) [N/m/?] n
  ForteMedia FM801 (SND_FM801) [N/m/?] n
  RME Hammerfall DSP Audio (SND_HDSP) [N/m/?] n
  RME Hammerfall DSP MADI/RayDAT/AIO (SND_HDSPM) [N/m/?] n
  ICEnsemble ICE1712 (Envy24) (SND_ICE1712) [N/m/?] n
  ICE/VT1724/1720 (Envy24HT/PT) (SND_ICE1724) [N/m/?] n
  Intel/SiS/nVidia/AMD/ALi AC97 Controller (SND_INTEL8X0) [M/n/?] m
  Intel/SiS/nVidia/AMD MC97 Modem (SND_INTEL8X0M) [N/m/?] n
  Korg 1212 IO (SND_KORG1212) [N/m/?] n
  Digigram Lola (SND_LOLA) [N/m/?] n
  Digigram LX6464ES (SND_LX6464ES) [N/m/?] n
  ESS Allegro/Maestro3 (SND_MAESTRO3) [N/m/?] n
  Digigram miXart (SND_MIXART) [N/m/?] n
  NeoMagic NM256AV/ZX (SND_NM256) [N/m/?] n
  Digigram PCXHR (SND_PCXHR) [N/m/?] n
  Conexant Riptide (SND_RIPTIDE) [N/m/?] n
  RME Digi32, 32/8, 32 PRO (SND_RME32) [N/m/?] n
  RME Digi96, 96/8, 96/8 PRO (SND_RME96) [N/m/?] n
  RME Digi9652 (Hammerfall) (SND_RME9652) [N/m/?] n
  Studio Evolution SE6X (SND_SE6X) [N/m/?] (NEW) 
  S3 SonicVibes (SND_SONICVIBES) [N/m/?] n
  Trident 4D-Wave DX/NX; SiS 7018 (SND_TRIDENT) [N/m/?] n
  VIA 82C686A/B, 8233/8235 AC97 Controller (SND_VIA82XX) [N/m/?] n
  VIA 82C686A/B, 8233 based Modems (SND_VIA82XX_MODEM) [N/m/?] n
  Asus Virtuoso 66/100/200 (Xonar) (SND_VIRTUOSO) [N/m/?] n
  Digigram VX222 (SND_VX222) [N/m/?] n
  Yamaha YMF724/740/744/754 (SND_YMFPCI) [N/m/?] n
#
# configuration written to .config
#
```

コンパイル設定が正常終了したら次にコンパイルを実行し、モジュールなどのインストールを行います。

- コンパイル  
`# make`  
`# make modules_install`  
`# make install`

### 動作確認
OS再起動後、以下のコマンドを実行して、カーネルのバージョンが上がったことを確認します。

- カーネルのバージョン確認   
`# uname -r`

```console
5.4.109
```

無事にカーネルのバージョンが上がっていれば成功です。:penguin:

- vmlinuzの確認  
`# ls -l /boot/`

```console
total 170432
drwxr-xr-x  4 root root     4096 Apr 17 15:16 ./
drwxr-xr-x 20 root root     4096 Apr 17 13:59 ../
-rw-r--r--  1 root root   237851 Apr 12 15:12 config-5.4.0-72-generic
-rw-r--r--  1 root root   148982 Apr 17 15:16 config-5.4.109
drwxr-xr-x  4 root root     4096 Apr 17 15:16 grub/
lrwxrwxrwx  1 root root       27 Apr 17 13:59 initrd.img -> initrd.img-5.4.0-72-generic
-rw-r--r--  1 root root 82810789 Apr 17 14:00 initrd.img-5.4.0-72-generic
-rw-r--r--  1 root root 58762340 Apr 17 15:16 initrd.img-5.4.109
lrwxrwxrwx  1 root root       27 Apr 17 13:59 initrd.img.old -> initrd.img-5.4.0-72-generic
drwx------  2 root root    16384 Apr 17 13:57 lost+found/
-rw-------  1 root root  4750202 Apr 12 15:12 System.map-5.4.0-72-generic
-rw-r--r--  1 root root  4565739 Apr 17 15:16 System.map-5.4.109
lrwxrwxrwx  1 root root       15 Apr 17 15:16 vmlinuz -> vmlinuz-5.4.109
-rw-------  1 root root 11760384 Apr 12 16:56 vmlinuz-5.4.0-72-generic
-rw-r--r--  1 root root 11438976 Apr 17 15:16 vmlinuz-5.4.109
lrwxrwxrwx  1 root root       24 Apr 17 13:59 vmlinuz.old -> vmlinuz-5.4.0-72-generic
```

vmlinuzのシンボリックリンクのリンク先が、`vmlinuz-5.4.109`に変わったことも確認できます。


# おわりに
Linuxはエンジニアとして市場価値を高めるために必要なスキルです。
現在のOSは最適化されていますが、カーネルの世界を覗いてみるのも面白いと思います。

## 参考
- [Documentation extracted from the Linux kernel and mirrored on the web where Google can find it:](https://www.kernel.org/doc/)
- [The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/)
- [BuildADebianKernelPackage](https://wiki.debian.org/BuildADebianKernelPackage)
- [カーネルのコンパイル](https://debian-handbook.info/browse/ja-JP/stable/sect.kernel-compilation.html)
- [Linux kernel Development](https://www.youtube.com/watch?v=598Xe7OsPuU)