前回までは設定をいじってましたが、今回はLinuxカーネルを差し替えます。

こちらの記事で最新のRaspbianのカーネルを差し替えてますので、そちらも参考までに。

◆VirtualBox上でクロスコンパイルする

特にカーネルを差し替える必要はないのかもしれませんが、自己満足コンフィグレーションの最適化のため、何となく自家製Myカーネルに差し替えたいと思います。愛着もわきますし。

Raspberry Piのターゲット上でカーネルをビルドをすることも可能ですが、カーネルビルドするにはRaspberry Piでは非力で、とても時間がかかってしまうため、VirtualBox上に構築したLinux Mintでクロスコンパイルしようと思います。

VirtualBox上にLinux Mintの構築方法はこちらを参照。

以下はVirtualBox上のLinux Mintでの操作です。

◆Raspberry Pi用クロスコンパイラを取得する
コンパイラもカーネルソースもgitで公開されています。

  GitHub (https://github.com/raspberrypi/)

gitがインストールされていない場合は、インストールしておきます。
$ sudo apt-get install git

まずはTerminalを起動し、適当な作業ディレクトリを作成します。
以降、作業ディレクトリを ${HOME}/raspi/ として記述します。
$ cd ${HOME}/raspi/
$ git clone https://github.com/raspberrypi/tools.git

取得してきた中を見ると tools/arm-bcm2708/ に以下の4つのツールチェーンがあります。
- arm-bcm2708-linux-gnueabi
- arm-bcm2708hardfp-linux-gnueabi
- gcc-linaro-arm-linux-gnueabihf-raspbian
- gcc-linaro-arm-linux-gnueabihf-raspbian-x64

どれを使うべきか、、、
自分は 32bit版 Linux Mint をインストールしているので 64bit版はそもそも実行できません。
これで残りの候補は3つ。(64bitマシンならこれ一択でいい気もします)
それぞれの gcc で --version を実行した結果が下記です。
$ arm-bcm2708-linux-gnueabi/bin/arm-bcm2708-linux-gnueabi-gcc --version
arm-bcm2708-linux-gnueabi-gcc (crosstool-NG 1.15.2) 4.7.1 20120402 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi-gcc --version
arm-bcm2708hardfp-linux-gnueabi-gcc (crosstool-NG 1.15.2) 4.7.1 20120402 (prerelease)
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

$ gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-gcc --version
arm-linux-gnueabihf-gcc (crosstool-NG linaro-1.13.1-4.8-2014.01 - Linaro GCC 2013.11) 4.8.3 20140106 (prerelease)
Copyright (C) 2013 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
gcc-linaro-arm-linux-gnueabihf-raspbian がGCCバージョン4.8.3 で一番新しいので、これを使うことにします。
(安易ですが、OSSは最新であれば最新ほどいい、が自分のモットーですので。)

.bashrc の最後に
PATH=${HOME}/raspi/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin:${PATH}
とでも付け加えて、パスを通しておきます。

# 新しいTerminalを使うか ". ~/.bashrc" と実行しないと、まだ反映はされていませんので注意。

◆Raspberry Pi用Linuxカーネルのソースコードを取得
$ cd ${HOME}/raspi/
$ git clone https://github.com/raspberrypi/linux.git
それなりに時間かかると思います。

◆Raspberry Pi用のカーネルコンフィグを最適化する
カーネルコンフィグは Raspberry Pi 上で
$ sudo zcat /proc/config.gz > raspi.config
とすれば、今 Raspberry Pi で動いているLinuxカーネルのカーネルコンフィグが取得できます。

今回は自分がデフォルトのコンフィグをベースに変更を加えたこのコンフィグを使用します。
カーネルバージョンは 3.12.31 です。

# もっと最新のカーネルがいいという方はもっと↓を参照。

変更点は下記の通りです。
- デバッグ関連のコンフィグを無効化
(ダンプ 吐かれても別に原因解析する気ないなー、と思ったのでCOREDUMP、GDB、KPROBEとか全部切ってます。)
- プロファイル関連のコンフィグを無効化
- IPv6 の無効化(元々ローダブルモジュールだったのでそのままでもいいのですが)
- ROOT_NFS の無効化
- カーネル圧縮形式をlz4に変更
- ログバッファサイズとかその他、何点か微修正

カーネルイメージの省サイズ化、及びオーバーヘッド軽減を目的としたものなので、デフォルトのコンフィグから機能的には変わっていないはずです。(nfs root を使用する場合を除き)

上記コンフィグを.config として linux ディレクトリ直下に置きます。
$ curl http://sstea.blog.jp/raspi/kbuild/kconfig-rpi-3.12.31 > .config

◆Raspberry Pi用のカーネルをビルド(再構築)する
今回、カーネル圧縮形式をlz4にしているので下記をインストールしておきます。
$ sudo apt-get install liblz4-tool

ついでに、カーネルビルドに必要なパッケージが未インストールなら入れておきます。
$ sudo apt-get install build-essential libncurses5-dev

環境変数を設定します。
$ export ARCH=arm
$ export CROSS_COMPILE=arm-linux-gnueabihf-
カーネルコンフィグを変えたい場合は変えます。(変えないならmenuconfigは不要です。)
$ cd ${HOME}/raspi/linux/
$ make menuconfig
カーネルをビルドします。
$ make
カーネルモジュールのビルド&インストールを行います。(インストール先はテンポラリなのでどこでもいいです。../mod-3.12.31である必要はありません。)
$ make modules
$ make modules_install INSTALL_MOD_PATH=../mod-3.12.31/  INSTALL_MOD_STRIP=--strip-unneeded

ビルドが完了したなら、linux/arch/arm/boot/zImage がカーネルイメージになります。
これを kernel.img とリネームし、Raspberry Pi の /boot/kernel.img と置換することで新カーネルに差し替えることが出来ます。

ちなみに、元のカーネルイメージはgzip圧縮形式で 3.3MBほどでした。
今回のカーネルイメージはlz4圧縮形式で3.0MBほどで、シュリンク出来ています。
(同じgizp圧縮形式なら2.6MBまで縮みましたが、展開速度が高速なlz4を採用しています。)

あと、${HOME}/raspi/mod-3.12.31/ というのが出来ているはずなのでこの中の lib/modules/3.12.31+/ をRaspberry Pi の /lib/modules/ 以下に置いてあげるのも忘れずに。

ちなみに、../mod-3.12.31/lib/firmware/ にもいろいろファイルが出来ているのですが、
Raspberry Pi の /lib/firmware/ にあるファイルと一つも被っていないのが謎。
たぶんなくてもいい気もしますが、念のために ../mod-3.12.31/lib/firmware/ 以下のファイルも Raspberry Pi の /lib/firmware/ 以下にコピーしておくことにします。

◆Raspberry Pi用のチャキチャキの最近カーネルを使用する
と、ここで終わってもいいのですが、せっかくビルドまでして自家製カーネルを使うのだから、最新バージョンのカーネルを使用したいと思います。
現在の最新バージョンは 3.17.2 ですので、3.17.2 のカーネルにしましょう。

git でLinuxカーネルのソースを取得する所までは同じです。
(または、3.12.31 のツリーが不要なら make distclean してそれを流用しましょう。)
$ cd ${HOME}/raspi/linux/
$ git checkout -b rpi-3.17.y remotes/origin/rpi-3.17.y

3.17.2 のカーネルコンフィグにはこのコンフィグを使用します。
デフォルトコンフィグからの変更点は 3.12.31 の時と同様です。

ビルド方法等は 3.12.31 と同様です。

カーネルイメージを差し替えて、リブート後 Raspberry Pi 上で
$ uname -r
とし、3.17.2 と表示されていれば成功です。

◆カーネルイメージのファイル名を変更する
今回はカーネルを自家製カーネルに置き換えました。
ファームアップデート等を行なうと、本家のカーネルイメージに置き換えられてしまいます。

カーネルイメージのファイル名をデフォルト(kernel.img) から変更することで、これを回避することができます。

/boot/config.txt に下記を追記します。
$ kernel=kernel-3.17.img

カーネルイメージのファイル名をリネームします。
$ sudo mv /boot/kernel.img /boot/kernel-3.17.img

◆おまけ1
カーネルのオーバーヘッド軽減を目指してカーネルコンフィグをいじりましたが、効果は出ているのでしょうか?

printk で表示できる時間はカーネル起動からの時間のため、カーネル省サイズ下によるブートローダーの展開時間削減の効果は残念ながら正確な確認方法がありません。(体感できるレベルでなければ)

カーネル起動後であればカーネルログで確認できますので、今回の確認方法としてはカーネルログのrootfsのマウントまでの時間("EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)"とういうログの出力時間)を指標としたいと思います。
以前の記事でquietの効果確認を行なったのと同じ方法です。)

オリジナルconfig
raspberrypi kernel: [   13.000215] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
変更config
raspberrypi kernel: [   12.670055] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)

一応オリジナルより早くなってそうです。体感できないと思いますが。
小さなことからコツコツと!!

◆おまけ2

initramfsを使用してみます。

# 結局使用しない構成に戻しましたが。

現状のブートシーケンスはbootloader→kernel→rootfsの順に起動しますが、
bootloader→kernel→initramfs→rootfsの順に起動させる事ができます。

このinitramfsで先に起動させておきたいデーモン等やモジュールのロード等を済ませれば、rootfsの起動スクリプトの処理が遅かろうが関係ありません。
$ sudo apt-get install initramfs-tools
$ sudo update-initramfs -c -k $(uname -r)
で /boot/ 以下に initrd.img-3.17.2+ というファイルが出来ているはずです。

/boot/config.txt に以下を追記すれば、initramfsを使用したブートシーケンスになります。
initramfs initrd.img-3.17.2+
ただこのままだと、純粋にinitramfsを経由するようになった分だけ起動時間が少し遅くなっただけでメリットはありません。

initramfsの中身をいじくるためにこのファイルを VirtualBox の Linux Mint 上に持っていきます。
以下は ${WORKDIR} にファイルを持ってきたとして記載します。

ファイル名を変更し、ディレクトリを作成しておきます。
$ mv initrd.img-3.17.2+ initrd.img-3.17.2+.orig.gz
$ mkdir -p ${WORKDIR}/initramfs/
展開します。
$ cd ${WORKDIR}/initramfs/
$ gunzip -c -9 ../initrd.img-3.17.2+.orig.gz | cpio -i -d -H newc
お好きにいじくってください。

満足したら、再びinitramfsイメージにします。
$ cd ${WORKDIR}/initramfs/
$ find . | cpio -o -H newc | gzip > ../initrd.img-3.17.2.+
これを raspberry pi の /boot/ 以下においてあげてください。


次回はいったんこの環境をバックアップしようと思います。