Linux

【PC】Linuxビルドのノウハウ ③make deb-pkgによるカーネルビルドとdebパッケージ作成・インストール

【※ 当記事は2020年7月2日時点の情報です】

ペイヴメント(@pavement1234)です。

エンジニア
エンジニア

Linuxのカーネルビルドの方法を知りたいけど簡単にまとめられた情報源がない

こんな悩みを解決します。

今回やることは3つ。

  1. make-kpkg(古い方式)に関する調査
  2. make deb-pkgによるカーネルビルドとdebパッケージ作成(知識習得、実践)
  3. debパッケージのインストール

バージョン情報

Linux Kernel 3.14

make-kpkgとは何か?

make-kpkgですぐにでもビルドしたかったのですが、make allが一生懸命頑張っているので一旦保留。ビルドを待つ間、make-kpkgについて調べてみました。

Debian 管理者ハンドブック 8.10. カーネルのコンパイルを読むとこんなことが書いてあります。

CULTURE kernel-package の古き良き時代

Linux ビルドシステムに適切な Debian パッケージをビルドする能力がなかった時代、Debian パッケージをビルドするのに推奨されていた方法は kernel-package パッケージに含まれる make-kpkg を使うやり方でした。

Debian界隈では既にmake-kpkgは古いやり方のようです。たしかにこのサイトではmake deb-pkgが使われてますね。Ubuntu(Wikipedia)を見るとUbuntuはDebianから派生したディストリビューションなのでdebパッケージを扱うことが出来るわけで。当然make deb-pkgも行けるハズ。

要するに私はビルドしたkernelをrpmやdebなどのパッケージ管理システムで管理したいと思っていますが、普通にkernelをビルドした場合、つまり、ターゲット指定なしでmakeコマンドを実行した場合、開発者は、distributionが提供するパッケージ管理システムを使うことなく、手動、ないし”make install”を用いてkernelをインストール/アンインストールする必要があります。

管理が面倒などの理由でこれが不満ならmake deb-pkg、あるいはmake rpm-pkgコマンドを使うと、それぞれ.deb形式、.rpm形式のkernelパッケージを作成することができます。

make deb-pkgによるカーネルビルドとdebパッケージ作成(知識習得)

Debian 管理者ハンドブック 8.10. カーネルのコンパイルの「8.10.4. パッケージのコンパイルとビルド」を熟読してみたところ、ake cleanの話と生成されるパッケージの種類が書いてありました。

NOTE 再ビルド前の片付け
既にそのディレクトリの中で 1 回コンパイルした状態で、(たとえばカーネル設定を大幅に変更したなどの理由で) すべてを最初から再ビルドしたい場合、make clean を実行してコンパイル済みファイルを削除しなければいけません。make distclean はさらに生成されたファイルも削除します。この中には .config ファイルも含まれますので、忘れずにバックアップしてください。

カーネル設定の準備が完了したら、make deb-pkg で 5 つの Debian パッケージが生成できます。具体的に言うと、

①カーネルイメージと関連モジュールを含む linux-image-version
②外部モジュールのビルドに必要なヘッダファイルを含む linux-headers-version
③一部のドライバから要求されるファームウェアファイルを含む linux-firmware-image-version (Debian の配布しているカーネルソースからカーネルをビルドする場合、このパッケージは生成されないかもしれません)、
④カーネルイメージとモジュールのデバッグシンボルを含む linux-image-version-dbg
⑤GNU glibc などのユーザ空間ライブラリに関連するヘッダを含む linux-libc-dev

が生成されます。
ここで version は

①上流開発バージョン (Makefile 中の VERSION、PATCHLEVEL、SUBLEVEL、EXTRAVERSION から定義されます)、
②LOCALVERSION 設定パラメータ、
③LOCALVERSION 環境変数

を連結したものです。パッケージバージョンは version に規則正しく増え続けるリビジョン番号 (.version に保存されています) を付け加えたものになります。ただし、

④KDEB_PKGVERSION 環境変数

を使えばパッケージバージョンを上書きすることも可能です。

$ make deb-pkg LOCALVERSION=-falcot KDEB_PKGVERSION=$(make kernelversion)-1
[…] $ ls ../*.deb
../linux-headers-3.16.7-ckt4-falcot_3.16.7-1_amd64.deb 
../linux-image-3.16.7-ckt4-falcot_3.16.7-1_amd64.deb 
../linux-image-3.16.7-ckt4-falcot-dbg_3.16.7-1_amd64.deb 
../linux-libc-dev_3.16.7-1_amd64.deb 
(③は無かった)

①上流開発バージョン (Makefile 中の VERSION、PATCHLEVEL、SUBLEVEL、EXTRAVERSION から定義されます)、

/usr/src/linux-3.14.79/Makefileの先頭5行にバージョンが記載されていました。これをつなげて3.14.79となります。

VERSION = 3
PATCHLEVEL = 14
SUBLEVEL = 79
EXTRAVERSION =
NAME = Remembering Coco

②LOCALVERSION 設定パラメータ

上記のmake deb-pkgコマンド例にある、LOCALVERSION=-falcot、のことです。

③LOCALVERSION 環境変数

自分のPCでecho $LOCALVERSIONと打ってみましたが何も表示されなかったので環境変数は設定されていないことが分かりました。

④KDEB_PKGVERSION 環境変数

上記のmake deb-pkgコマンド例にある、KDEB_PKGVERSION=$(make kernelversion)-1、のことです。Makefileが置いてある/usr/src/linux-3.14.79でmake lernelversionを打ち込むと3.14.79と出力されますが、それに-1を付加した3.14.79-1をKDEB_PKGVERSIONに代入しています。

カーネルバージョンについて、もうちょっと詳しく知りたい場合[Linux] kernel version stringが参考になります。

make deb-pkgによるカーネルビルドとdebパッケージ作成(実践)

一通り予備知識を得たので真似してやってみました。

$ make deb-pkg LOCALVERSION=-pavement1234 KDEB_PKGVERSION=$(make kernelversion)-1

(building…)

INSTALL include/asm (64 files)
dpkg-deb: ../linux-firmware-image-3.14.79-pavement1234_3.14.79-1_amd64.deb' にパッケージ linux-firmware-image-3.14.79-pavement1234′ を構築しています。
dpkg-deb: エラー: ../linux-firmware-image-3.14.79-pavement1234_3.14.79-1_amd64.deb' を作成できませ ん: 許可がありません
make[ 1 ]: *** [deb-pkg] エラー 2
make: *** [deb-pkg] エラー 2

とりあえずエラー発生。許可がない系エラーと判断しsudoを付けて再び実行。

$ sudo make deb-pkg LOCALVERSION=-pavement1234 KDEB_PKGVERSION=$(make kernelversion)-1

(building…)

INSTALL include/uapi (0 file)
INSTALL include/asm (64 files)
dpkg-deb:
../linux-firmware-image-3.14.79-pavement1234_3.14.79-1_amd64.deb’ にパッケージ linux-firmware-image-3.14.79-pavement1234' を構築しています。
dpkg-deb:
../linux-headers-3.14.79-pavement1234_3.14.79-1_amd64.deb’ にパッケージ linux-headers-3.14.79-pavement1234' を構築しています。
dpkg-deb:
../linux-libc-dev_3.14.79-1_amd64.deb’ にパッケージ linux-libc-dev' を構築しています。
dpkg-deb:
../linux-image-3.14.79-pavement1234_3.14.79-1_amd64.deb’ にパッケージ linux-image-3.14.79-pavement1234' を構築しています。
dpkg-deb:
../linux-image-3.14.79-pavement1234-dbg_3.14.79-1_amd64.deb’ にパッケージ `linux-image-3.14.79-pavement1234-dbg’ を構築しています。

$ ls ..
linux-3.14.79
linux-firmware-image-3.14.79-pavement1234_3.14.79-1_amd64.deb ③
linux-headers-3.13.0-165
linux-headers-3.13.0-165-generic
linux-headers-3.13.0-24
linux-headers-3.13.0-24-generic
linux-headers-3.14.79-pavement1234_3.14.79-1_amd64.deb ②
linux-image-3.14.79-pavement1234-dbg_3.14.79-1_amd64.deb ④
linux-image-3.14.79-pavement1234_3.14.79-1_amd64.deb ①
linux-libc-dev_3.14.79-1_amd64.deb ⑤
linux-source-3.13.0
linux-source-3.13.0.tar.bz2

無事5つのdebパッケージが生成されました。

debパッケージのインストール

8.11. カーネルのインストールを読みました。まずはカーネルパッケージ(image)の中身に関する解説です。

8.11.1. Debian カーネルパッケージの特徴

Debian カーネルパッケージはカーネルイメージ (vmlinuz-version) をインストールします。カーネルの設定 (config-version) とシンボルテーブル (System.map-version) は /boot/ に置かれます。シンボルテーブルは開発者がカーネルエラーメッセージの意味を理解する際の手助けになります。それどころかシンボルテーブルがなければ、カーネルの「oops」(「oops」とはカーネル空間で起こるユーザ空間プログラムのセグメンテーション違反に相当するエラーです。言い換えれば、不正なポインタを参照して値を取得したことで生成されるメッセージです) に含まれる情報は、数字で表したメモリアドレスだけになります。アドレスとシンボルや関数名を対応付けるテーブルがなければ、この情報は役に立ちません。モジュールは /lib/modules/version/ ディレクトリにインストールされます。
カーネルパッケージの設定スクリプトは自動的に initrd イメージを生成します。initrd はブートローダによってメモリに読み込まれる小さなシステムで (このため「init RAM ディスク」と名付けられています)、Linux カーネルは initrd を使って完全な Debian システムを含むデバイス (たとえば SATA ディスクのドライバ) にアクセスするために必要なモジュールを読み込みます。最後に、カーネルパッケージの post-installation スクリプトが /vmlinuz、/vmlinuz.old、/initrd.img、/initrd.img.old のシンボリックリンクを更新します。これらはそれぞれインストールされた最新の 2 つのカーネルとカーネルに対応する initrd イメージを指します。
これらの作業のほとんどは /etc/kernel/*.d/ ディレクトリの中にあるフックスクリプトが担っています。たとえば、grub との統合は、カーネルがインストールまたは削除された際に update-grub を呼び出す /etc/kernel/postinst.d/zz-update-grub と /etc/kernel/postrm.d/zz-update-grub が担っています。

次にdpkgによるパッケージインストールの解説です。

8.11.2. dpkg を使ったインストール

apt はとても便利なので、簡単に低レベルツールについて忘れてしまいます。しかし、コンパイルされたカーネルをインストールする最も簡単な方法は dpkg -i package.deb などのコマンドを使うやり方です。ここで package.deb は linux-image パッケージの名前で、たとえば linux-image-3.16.7-ckt4-falcot_1_amd64.deb です。
この章で説明されている設定手順は基本であり、サーバシステムにもワークステーションにも適用でき、半自動化された方法で広く適用できます。しかしながら、この設定手順だけで完全に設定されたシステムを十分に提供することは不可能です。「Unix サービス」として知られている低レベルプログラムを初めとする、いくつかの要素に対する設定がまだ必要です。

とにかくimageをdpkgでインストールすればいいんですね。

$ sudo dpkg -i linux-image-3.14.79-pavement1234_3.14.79-1_amd64.deb

以前に未選択のパッケージ linux-image-3.14.79-pavement1234 を選択しています。
(データベースを読み込んでいます … 現在 253096 個のファイルとディレクトリがインストールされています。)
linux-image-3.14.79-pavement1234_3.14.79-1_amd64.deb を展開する準備をしています …
linux-image-3.14.79-pavement1234 (3.14.79-1) を展開しています…
linux-image-3.14.79-pavement1234 (3.14.79-1) を設定しています …
update-initramfs: Generating /boot/initrd.img-3.14.79-pavement1234
Generating grub configuration file …
Warning: Setting GRUB_TIMEOUT to a non-zero value when GRUB_HIDDEN_TIMEOUT is set is no longer supported.
Linux イメージを見つけました: /boot/vmlinuz-3.14.79-pavement1234
Found initrd image: /boot/initrd.img-3.14.79-pavement1234
Linux イメージを見つけました: /boot/vmlinuz-3.13.0-165-generic
Found initrd image: /boot/initrd.img-3.13.0-165-generic
Linux イメージを見つけました: /boot/vmlinuz-3.13.0-24-generic
Found initrd image: /boot/initrd.img-3.13.0-24-generic
Adding boot menu entry for EFI firmware configuration
完了

インストールがうまくいった感じ。リブートしてみました。

$ sudo reboot

リブート後、無線LANがうまく認識されないみたいなのでTeraTermで接続できず。もう1度リブートしたら繋がりました。謎です…。

というわけでuname -aを打ち込んだところ、無事カーネル3.14に更新されていることが確認できました。

$ uname -a
Linux pavement1234 3.14.79-pavement1234 #2 SMP Tue Jul 9 06:38:10 JST 2019 x86_64 x86_64 x86_64 GNU/Linux

/bootを見てみると、いい感じにインストールされてます。

$ ls /boot
System.map-3.13.0-165-generic initrd.img-3.13.0-24-generic
System.map-3.13.0-24-generic initrd.img-3.14.79-pavement1234
System.map-3.14.79-pavement1234 lost+found
abi-3.13.0-165-generic memtest86+.bin
abi-3.13.0-24-generic memtest86+.elf
config-3.13.0-165-generic memtest86+_multiboot.bin
config-3.13.0-24-generic retpoline-3.13.0-165-generic
config-3.14.79-pavement1234 vmlinuz-3.13.0-165-generic
efi vmlinuz-3.13.0-165-generic.efi.signed
grub vmlinuz-3.13.0-24-generic
initrd.img-3.13.0-165-generic vmlinuz-3.14.79-pavement1234

/lib/modules/3.14.79-pavement1234/も見てみました。いい感じです。

$ ls /lib/modules/3.14.79-pavement1234/
kernel modules.builtin modules.dep.bin modules.softdep
modules.alias modules.builtin.bin modules.devname modules.symbols
modules.alias.bin modules.dep modules.order modules.symbols.bin

さて、インストールまではうまく行きました。

まとめ

とりあえずインストールまで駆け足で実施しました。いろいろ宿題が残ってるので、残課題編をまとめます。

ABOUT ME
ペイヴメント
ペイヴメントのエンジニア塾(当ブログ)では20年以上の経験から得られたプログラミング系ノウハウについてベテランにも満足して頂けるような内容の濃いコンテンツを初心者にも分かりやすい形で日々発信しています。【経歴】ベンチャーのソフトハウスで4年勤務後、精密機器メーカーのソフト開発部門に勤務し今に至ります。