試験運用中なLinux備忘録・旧記事

はてなダイアリーで公開していた2007年5月-2015年3月の記事を保存しています。

Linuxカーネル(2.6系)のビルドの流れ

(2014/9/22)本記事の手順はカーネル3.x系についても同様となる。ブートローダについてはGRUB Legacy(GRUB 1)についてを扱っているが、GRUB 2ではカーネルは自動検出されて起動するための項目群は自動的に生成される。「ヘルパースクリプトを使用した場合のGRUB 2(1.96)の設定方法(全般とカーネル関係)」も参照。

  1. ソースの入手・展開
  2. 必要に応じてパッチ当て
  3. 必要に応じてバージョン番号(EXTRAVERSION)を付ける
  4. ビルド設定のコピー
  5. ビルドの設定
  6. 組み込みとモジュール
  7. 設定のヒント
  8. ビルドとインストール
  9. ビルド後のソースツリー

ソースの入手・展開

確実に、ディストリのパッケージになっている。Debian,Ubuntuでは「linux-source」、Gentoo Linuxでは種類によりパッケージ名が異なるが、通常は「gentoo-sources」。
展開は、自動的にされるディストリとされないディストリとがあるが、Debian,Ubuntuでは、ソースのtar.bz2ファイルが置かれるようなので、これを手動で展開する。
展開場所は/usr/src/以下が一般的。更に、

$ ls -l /usr/src/
合計 36
lrwxrwxrwx  1 root root     21 2007-07-17 22:23 linux -> linux-2.6.22-suspend2
drwxr-xr-x 20 root root   4096 2007-07-14 21:40 linux-2.6.22-gentoo-r1
drwxr-xr-x 20 root root   4096 2007-07-21 20:38 linux-2.6.22-suspend2

のように、新しく使用するソースツリーにリンクを張る。Gentoo Linuxでは展開までを行い、「symlink」のUSEフラグを付けると、/usr/src/linuxシンボリックリンクまで作ってくれる。

必要に応じてパッチ当て

ディストリのカーネルソースパッケージは色々な修正が入っているのだが、その中に入っていないパッチを当てたい場合、ここで当てる。
パッチを当てる際には、対象ファイルのある階層が重要なので、注意する。パッチファイルの先頭が

--- linux-2.6.22/Documentation/kernel-parameters.txt    2007-07-11 22:18:38.000000000 +1000
+++ suspend2-2.2.10-for-2.6.22/Documentation/kernel-parameters.txt      2007-07-11 22:25:42.000000000 +1000

のようになっている場合、1番目のディレクトリ(suspend2-2.2.10-for-2.6.22/)を省く。ディレクトリ階層を省くには、patchコマンドの-p[省略したい階層数]オプションを使う。下は、bzip2圧縮されたパッチで1階層削った例。/path/to/filename.patch.bz2は、実際のファイル名に合わせる。

# cd /usr/src/linux
# bzcat /path/to/filename.patch.bz2 | patch -p1

gzip圧縮されている場合はzcatで.patch.gzファイルを指定し、圧縮されていないファイルの場合、

# patch -p1 < /path/to/filename.patch

のようにして直接読み込む。
なお、パッチファイルの拡張子は.patchであることが多いが、.diffとなっている場合もある。中身は同じ形式。

処理が開始されて

patching file [ファイル名]

という行だけが出て終了した場合は成功。

Hunk #2 succeeded at 1215 (offset -2 lines).

というようなメッセージが出る場合も、適用場所が何行目なのかがずれていたりするだけで、適用自体は問題なく済んでいる。*1

Hunk #1 FAILED at 28.
Hunk #2 succeeded at 3312 (offset 186 lines).
1 out of 2 hunks FAILED -- saving rejects to file block/ll_rw_blk.c.rej

のように、「FAILED」が出ている場合は失敗で、.rejファイルに、適用の失敗した領域(Hunk)が書き出される。

必要に応じてバージョン番号(EXTRAVERSION)を付ける

/usr/src/linux/Makefileの先頭に、

VERSION = 2
PATCHLEVEL = 6
SUBLEVEL = 22
EXTRAVERSION = -suspend2

のようなバージョン表記があり、unameコマンドで出力されるバージョン情報や、カーネルモジュールの場所(/lib/modules/[バージョン])、/boot/以下の

  • System.map-[バージョン]
  • config-[バージョン]
  • vmlinuz-[バージョン]

といったファイル/ディレクトリ名の一部にも使われる。
同じバージョンのカーネルを使って、設定を別のものにしてビルドする場合、上記のEXTRAVERSIONの部分を変えることで、異なるバージョン表記にすることができる。これは設定の試行錯誤をするときなどに便利。

ビルド設定のコピー

カーネルビルドの設定は、/usr/src/linux/.configというファイルに書き出され、使用される。
ディストリのカーネルを使っている場合、設定ファイル/boot/config(-[バージョン])/usr/src/.configとしてコピーすると、そのカーネルと同じ設定から始められる。

# cp /boot/config /usr/src/config

もしくは、

General setup  --->
 <*> Kernel .config support                                           
 [*]   Enable access to .config through /proc/config.gz

というように設定しておくことで、動作中のカーネルの設定を

# zcat /proc/config.gz > .config

として出力させることもできる。

ビルドの設定

下は、設定ファイル/usr/src/linux/.configを含めて、ビルド時に生成された全てのファイルを消す。ディストリのカーネルソースパッケージでは、この作業が必要な場合がある。

# make HOSTCXX="ccache g++" CC="ccache gcc" mrproper

設定ファイルはそのままで、生成されたファイルだけ消すには

# make HOSTCXX="ccache g++" CC="ccache gcc" clean

を実行。その他に、distcleanというターゲットもあるが、通常は、上の2つで事足りる。

下は、既存の設定/usr/src/linux/.configを読み込んで、新しく追加された設定項目のみ設定する方法。一度設定を作ってしまえば、カーネルのバージョンが新しくなったときにこれを指定することで、追加された機能を設定しつつ、その他の設定は保持できる。

# make HOSTCXX="ccache g++" CC="ccache gcc" oldconfig

下は、端末上で動作する設定ツールで設定する方法。ncursesライブラリ(開発ファイル含む)が必要。

# make HOSTCXX="ccache g++" CC="ccache gcc" menuconfig

Xを使っている場合のみ、GTK+ 2やQt 3といったGUIツールキットライブラリ(開発ファイル含む)を使用したGUI設定ツールで設定ができる。それぞれ

# make HOSTCXX="ccache g++" CC="ccache gcc" gconfig

# make HOSTCXX="ccache g++" CC="ccache gcc" xconfig

となる。

組み込みとモジュール

幾つかの項目は、有効/無効の二択以外に、有効/モジュール/無効という三択の設定ができる。モジュールというのは、使用されていないときには読み込まれず、その機能を使いたいときに読み込んで使う形式。有効にしたものは、カーネルに組み込まれ、常にその機能が使える。
menuconfigでは「< >」というかっこになっているものがモジュールにできるもの(「<M>」がモジュール、「<*>」が組み込み)。xconfigではチェックボックスを何回かクリックして、四角の中に丸(サイコロの1のような形)になるものがそう。

設定のヒント

カスタムカーネルを作成する場合、初期RAMディスクイメージ(initrd)は使わないで済むようにできる(下のインストール手順でも触れていない)。その際に注意するのが、

といった辺りで、ディストリのカーネルの設定をそのまま使用しようとすると、ext3パーティションがマウントできない、などの理由でカーネルパニックになってしまったりすることがある。*2

その他、環境に合った設定を目指すためのヒントになるかもしれないことを幾つかメモ。

  • 使用していないデバイスのドライバは、モジュールにしていても、ビルド時間・電力・ディスク領域を無駄にするだけなので、なるべく減らすのがよい。
  • デバイスドライバに関しては、確実に「必要ない」と言えるものに関しては無効にして、よく分からないものに関してはモジュールにしておく(モジュールにできないものは有効にする)。
  • 新しいカーネルで動作に支障が無ければ、更に絞っていくことを繰り返す。ただし、一度に沢山の項目を無効にしすぎないで、段階的に減らしていく。
  • どのデバイスドライバが必要かよく分からないとき、ディストリのカーネル*3を使用していれば、lsmodで、使用されているモジュール名を見て、設定の参考にする。
  • 設定ファイルは、こまめにファイルに書き出す。バージョン(「2.6.xx」の「xx」部分)が上がったときも、古いバージョン向けのファイルとは別に新しく設定ファイルを書き出す。

ビルドとインストール

# make HOSTCXX="ccache g++" CC="ccache gcc"

ビルドしているカーネルと同一バージョンのモジュールが/lib/modules/以下にある場合、削除してからモジュールをインストールする。そうでなければこれはする必要がない。

# rm /lib/modules/[バージョン]/ -fr
# make HOSTCXX="ccache g++" CC="ccache gcc" modules_install

installターゲットは、/boot/以下に

  • System.map-[バージョン]
  • config-[バージョン]
  • vmlinuz-[バージョン]

をインストールし、更に

  • /boot/System.map
  • /boot/config
  • /boot/vmlinuz

シンボリックリンクを最新のファイルに張る。既に同名のファイルがあるものは[元のファイル名].oldと名前変更される。もう一度installをしてしまうと、.oldファイルだったものは上書きされて無くなってしまう。

# make HOSTCXX="ccache g++" CC="ccache gcc" install

カーネル自身を手動でコピーしたい場合、

# cp arch/[i386やx86_64など]/boot/bzImage /boot/vmlinuz-2.6.xx

のようにする。

いずれの方法でカーネルをインストールしても、grubブートローダの設定は行われないため、設定ファイル/boot/grub/menu.lstのkernel行で

title  Linux new
root (hd0,x)
kernel /vmlinuz root=...
boot

title  Linux old
root (hd0,x)
kernel /vmlinuz.old root=...
boot

のように、シンボリックリンクに対して設定しておけば、直前のカーネル(/boot/vmlinuz.old)でも起動できるようにしつつ、最新カーネル(/boot/vmlinuz)も使え、カーネルの更新ごとにkernel行を書き換えたりする必要もなくなる。

ビルド後のソースツリー

追加カーネルモジュールをビルドしたりするときに使用されることがあるため、使用しているバージョンのカーネルソースツリーは残しておくのがよい。また、その際、ビルドを通した状態にしておく(mrproperターゲットでファイルを消してある状態や、.configだけ置いてある状態では、追加モジュールのビルドがうまくいかない)。

*1:diffコマンドでパッチを作るとき、行番号だけでなく、周辺の行も含めて取る差分形式を使用しているため、多少ずれても、周辺の行が同じであれば大丈夫

*2:自分が初めてカーネルビルドに挑戦したときにこれをやって失敗し、しばらく諦めてしまった

*3:KNOPPIXUbuntuなどのCD起動LinuxなどでもOK