【入門】Linux カーネルをビルドしてみた
目次
こんにちは
システムソリューション部のインフラわっしょいマンです。
前回のブログでは実験的に文字サイズをでかくしてみたのですが、やりすぎたので反省して今回は普通サイズで書きます。
カーネルってご存じですか
コンピュータにおいて OS というものがハードウェアとソフトウェアの橋渡しをしてくれています。
この OS は単一ソフトウェアというよりも複数のソフトウェアから構成されており、その中でもコアとなる部分を担当しているものがカーネルという存在です。
私のカーネルに対する前提知識はこの辺りまでで、なんとなく聞いたことがある存在という感じでした。
その後、日々の業務に忙殺されながらも漠然と正体不明の「カーネル」さんにひっそりと思いを馳せておりましたが、
そういった業務の端々にもカーネルの影がありました。
「swap の使用具合を調整するためにカーネルの swappiness を調整する」
「net.ipv4.tcp_max_syn_backlog でコネクションについてチューニングする」
「fstab を書き損じるとカーネルパニックが発生するよ」
インフラエンジニアの端くれとして、たまに廊下ですれ違うこの存在と、ぜひお近づきになりたいと感じるとともに、皆さんも同じように感じているであろう感情が私にもいつしか生まれていました。
カーネルわかるのってなんかかっこよくね??
そのように全俺が憧れる OS の奥地には一体何があるのか?
その謎を解明するため、我々調査隊は OS の奥地へと向かった――。
ビルドってなんでしょうか
ところで、素晴らしいことにカーネルはオープンソースです。
コードを見ることができます。最高ですね。
ただC言語で書かれています(※)ので、コードのままでは使用できません。
バイナリに変換して使う必要があります。
これをビルド、あるいはコンパイルといいます。
ビルドする際に、例えばもともとカーネルに入っていない機能を追加したり、反対にいらない機能を削ってメモリ消費を小さくしたり、ソースコード自体をいじって改造するなど「僕の考えた最強のカーネル」にすることが可能です。
最強っていいですよね。世にある Linux ディストリビューションたちもそんな感覚で作られていってるはずです。たぶん(もちろん、いろいろあった末に生まれたディストリビューションたちの存在もありますが......)
(※)最近カーネル開発第2言語として、「所有権」の概念で有名なプログラミング言語「Rust」が取り入れられる流れが多く支持されているようなので、今後 C ではないパッケージも増えるかもしれません。
さて、カーネルってどんなもんよ?ってな理解を深めるためにもとりあえずビルドしてみます。
タイトル回収です。実践あるのみですね。
ビルドの下準備をしましょう
ビルドにはいくつか方法がありますが今回は CentOS7 上で、パッケージ管理システムを利用せずビルドしていきたいと思います。
どの世界でも、ソースをインストールしてきて あれこれやるのは管理の面も含めて修羅の道ですが、好きなバージョンを入れられる&作業時に特定のディストリビューションに依存した問題が起こりにくいというメリットもある為、今回はこの方法を選択しております。
また、以降 記載している作業はVagrant の Bento Box を利用した仮想環境で実施しています。
まずはカーネルのソースコードをダウンロードしましょう
カーネルは下記のリンク(もしくはこのサイトのミラーサイト)で公開されています。
※本記事では、longterm:5.15.58 を利用します。
The Linux Kernel Archives
https://www.kernel.org/
# tar ファイルをダウンロード [root@localhost]# cd /tmp [root@localhost tmp]# curl -O https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.58.tar.xz
# 解凍する [root@localhost tmp]# tar xvf linux-5.15.58.tar.xz
解凍したディレクトリにはたくさんの関連ファイルが詰まっています。
構造については下記 URL をご参考ください。
https://linuxjf.osdn.jp/JFdocs/The-Linux-Kernel-15.html
次に必要なものを環境に入れていきます。
必要なものはいくつかありますが、代表的なものは下記の通りです
- コンパイラ「GCC」
- ビルドツール「make」
- カーネル周辺のツールをビルドする際に使用されるライブラリ用として必要な「ヘッダーファイル」
- その他ライブラリ
これら含めコマンドでインストールしていきます。
# ソースビルドのためのツール一式をインストール [root@localhost ~]# yum groupinstall "Development tools"
コマンドを実行すると、たくさんツールがインストールされます。
「Development tools」というグループパッケージの中に「GCC」などの重要なツールが入っており、「groupinstall」オプションによってまとめて導入されます。
ただ、これだけでは十分ではないのでいくつか追加で、インストールしていきます。
なお必要なパッケージは先ほど解凍したディレクトリ(以後ソースディレクトリと呼びます)の Documentation/process/changes.rst
に書かれています。
# 必要パッケージのインストール [root@localhost ~]# yum install clang squashfs-tools pcmciautils quota ppp nfs-utils procps-ng kernel-devel udev mcelog python-sphinx openssl-devel dwarves elfutils-libelf-devel ncurses-devel
「ncurses-devel」はコンソールの画面制御に使用するもので、この後に行う「make menuconfig」コマンドの実施の際に必要なので入れております。
ビルド設定を変更してみましょう
ツールの準備ができたので、カーネルの設定をいじっていきたいのですが、設定変更にも複数の方法があります。
直接「.config」ファイルを編集することでも変更可能なのですが、大変なので今回はメニューから選択することで変更していける「menuconfig」という設定方法を利用していきます。
それでは、ソースディレクトリに移動してから必要なコマンドをぶちかましましょう。
[root@localhost linux-5.15.58]# make menuconfig ~~~ *** Compiler is too old. *** Your GCC version: 4.8.5 *** Minimum GCC version: 5.1.0
怒られました。ごめんて。
GCC のバージョン古いって。
なるほど、現在の GCC をアップデートするのはめんどくさい難しいので、softwarecollections という Red Hat 社が用意してくれている仕組みを使って、新しめな GCC を使います。
# パッケージインストール [root@localhost linux-5.15.58]# yum install scl-utils centos-release-scl # gcc の入っているツールセットのインストール [root@localhost linux-5.15.58]# yum install devtoolset-8 # 現在のシェルで gcc8 を使えるように設定 [root@localhost linux-5.15.58]# source scl_source enable devtoolset-8 # バージョン確認 [root@localhost linux-5.15.58]# gcc -v
これでようやく menuconfig を使えそうなので、実行してみます。
[root@localhost linux-5.15.58]# make menuconfig
きたー
なんか 設定ウィンドウみたいなのが出ました。
画面上部にも書いているのですが、操作方法や画面の説明をまとめると
- Select で選択 Exit で戻る Select か Exit かなどの状態は Tab キーで切り替え
- Y キーで機能を有効化 N キーで機能を無効化 M キーで機能をモジュール化(必要な時にのみ呼び出される状態) Space キーでこれらの状態を順に遷移させる
- 有効化された機能の横には「<*>」 無効化された機能の横には「<>」 モジュール化された機能の横には「<M>」と表示される
設定画面までたどり着いたので設定していきたいのですが、設定可能なカーネル変数はなんと2万弱くらいあるそうです。
膨大すぎるよ。
オープンソースってすごい。
たくさんありすぎるので、項目については思い切って割愛します。(今後 学習してブログに書けたらいいな。いや書くよ、書くってば)
ドキュメンテーションはあります。修業はまだ始まったばかりなのです。
さて 言い訳が終わったところで、「Exit」を選択して「make menuconfig」の画面から抜けましょう。
保存するか聞かれるので、保存して終了すると、ソースディレクトリの第一階層に「.config」というファイルが作成されていると思います。
中を見てみるとわかるのですが、こちらがカーネルビルド設定のファイルです。
上の方でも書きましたが、「menuconfig」や他のツールを使わず、このファイルを直接編集しても大丈夫です。
ビルドしてみましょう
ここまで来たらあとは簡単です。
ソースディレクトリへ移動して、カーネルをビルドしてみましょう
スペックによってはかなり時間がかかるので、並列処理で実行すると良いです。
# プロセッサ数を確認する [root@localhost linux-5.15.58]# nproc
私の仮想環境では結果は「2」でした
# nproc コマンドで確認したプロセッサ数をオプションの引数にとって、make コマンドでカーネルを並列処理ビルドする [root@localhost linux-5.15.58]# make -j2
コマンドを打つとなにやらゴリゴリと処理が始まります。
けっこう長いので、おやつでも食べて待ちましょう。
数時間待った後、完了しました。
途中、夏の車のボンネットくらい PC があつあつになってました。
CPU ベンチマークかな?
ソースディレクトリを確認すると「vmlinux」や「modules.builtin」など、いろいろな関連ファイルができていることが確認できます。
ここでようやくビルド完了です。
ビルドしたカーネルをインストールしてみましょう
せっかくなのでインストールまでやってみます。
その前に、いろいろと調査していると「/usr/src/」配下に linux のソースディレクトリを置くお作法があるらしいということを知りました。(LPIC でも出題されるようです。あれ? Level1 は持ってるんだけどな???)
なので いそいそと、ディレクトリを移動させることにします。
この配置場所に関してはディストリビューションによって違いがあるので、いろいろ調べてみると面白そうです。
# ディレクトリ移動 [root@localhost]# mv /tmp/linux-5.15.58 /usr/src/ # シンボリックリンク作成 [root@localhost]# ln -s /usr/src/linux-5.15.58 /usr/src/linux
コンプラ遵守完了!
では、いよいよカーネルをインストールしていきましょう。
# モジュールファイルをインストール [root@localhost linux]# make modules_install # 先に現在のカーネルバージョンを確認 [root@localhost linux]# uname -r 3.10.0-1160.71.1.el7.x86_64 # カーネル&初期化ディスクイメージをインストール [root@localhost linux]# make install
それでは1回 再起動してみますね。
[root@localhost linux]# reboot
で、改めて VirtualBox を立ち上げてみると
/sbin/mount.vboxsf: mounting failed with the error: No such device
ええ......
立ち上がんねえ......
なんで怒られているのかわかりませんが、なにか怒られていることだけは分かります。
リアルでもそういうことありますよね。
どうやらカーネルのバージョン変化の影響で、VirtualBox 側で管理してるバージョンと差が生まれて共有フォルダのマウントに失敗しているみたいです。
仮想環境あるある「仮想環境依存の問題でやりたい検証が進まない」
なんかプラグイン入れたらそのあたり上手いことやってくれるみたいです。
先駆者様々です、ほんとに。
# プラグインインストール vagrant plugin install vagrant-vbguest # 実行 vagrant vbguest # いろいろ終わったらリロード vagrant reload
ssh してバージョン確認してみます。
[root@localhost ~]# uname -r 3.10.0-1160.71.1.el7.x86_64
うーん?
変わってないな
OS を起動するときに選択可能なカーネルバージョンを確認してみます。
[root@localhost ~]# sudo awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg 0 : CentOS Linux (5.15.58) 7 (Core) 1 : CentOS Linux 7 Rescue f9788dcfa7cdc542bad786c12fab4a3c (3.10.0-1160.71.1.el7.x86_64) 2 : CentOS Linux (3.10.0-1160.71.1.el7.x86_64) 7 (Core) 3 : CentOS Linux (3.10.0-1160.66.1.el7.x86_64) 7 (Core) 4 : CentOS Linux (0-rescue-62851673ae88124499bf281ce5a57918) 7 (Core)
あるやん
「0 : CentOS Linux (5.15.58) 7 (Core)」←これですね、ビルドしたやつ
自動で切り替わってなかったみたいです。これも VirtualBox の仕様??(疑心暗鬼)
とりあえず「grub2-set-default」コマンドでリスト「0」番での起動がデフォルトとなるように設定してみます。
[root@localhost ~]# grub2-set-default 0
で、また再起動して ssh してみます。
[root@localhost ~]# uname -r 5.15.58
ウオアアアアア
ちゃんと今回ビルドしたバージョンになってます!
よかったー
感想
Linux って奥深いですね。
知らないことばかりで、トラブルシューティング含めてとても勉強になりました。
おそらく自分で Linux ディストリビューションを作るとなれば、
カーネルに加えて、パッケージとかも自分で入れていくっていう感じになるのでしょうか。
大変そうだけど面白そう!
なんだか自分の適当さがだいぶ文章に出ていた気がしますが、
少しだけ Linux の事を知ることができたので、満足です。
ありがとうございました!