Raspbian でffmpegをセルフビルドしてみた

Rapberry Pi 2 のRapbian jessieでffmpegをセルフビルドしてみました。

準備

$ sudo apt-get update
$ sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev \
  libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev \
  libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev
$ sudo apt-get install yasm
$ sudo apt-get install libx264-dev

fdk-aacのビルド

$ mkdir $HOME/work/ffmpeg
$ cd $HOME/work/ffmpeg/
$ wget -O fdk-aac.tar.gz https://github.com/mstorsjo/fdk-aac/tarball/master
$ tar xvzf fdk-aac.tar.gz 
$ cd mstorsjo-fdk-*/
$ autoreconf -fiv
$ ./configure 
$ make -j4
$ sudo make install

ffmpegのビルド

今回はx265は省略。

$ cd $HOME/work/ffmpeg/
$ wget https://www.ffmpeg.org/releases/ffmpeg-2.7.2.tar.xz
$ tar xf ffmpeg-2.7.2.tar.xz 
$ cd ffmpeg-2.7.2/
$ vi myconfig.sh
$ sh myconfig.sh 
$ time make -j4 2>&1 |tee make.log
$ sudo make install

myconfig.shの内容は

./configure \
  --enable-gpl \
  --enable-libass \
  --enable-libfdk-aac \
  --enable-libfreetype \
  --enable-libtheora \
  --enable-libvorbis \
  --enable-libx264 \
  --enable-nonfree

ビルドには一時間くらいかかりました。
できたffmpegを実行すると、libfdk-acc.so がみつからないというエラーになってしまいました。これは/usr/local/libに確かにあるのになんでだろう?と少し悩みましたが、これを実行しないと反映されないことを忘れていました。

$ sudo /sbin/ldconfig

これで問題なく起動できるようになりました。

$ ffmpeg -version
ffmpeg version 2.7.2 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 4.9.2 (Raspbian 4.9.2-10)
configuration: --enable-gpl --enable-libass --enable-libfdk-aac --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-libx264 --enable-nonfree
libavutil      54. 27.100 / 54. 27.100
libavcodec     56. 41.100 / 56. 41.100
libavformat    56. 36.100 / 56. 36.100
libavdevice    56.  4.100 / 56.  4.100
libavfilter     5. 16.101 /  5. 16.101
libswscale      3.  1.101 /  3.  1.101
libswresample   1.  2.100 /  1.  2.100
libpostproc    53.  3.100 / 53.  3.100

ロスコンパイルする方法はこちら。
CompilationGuide/RaspberryPi

PCMのデータを自分でいじってデジタルオーディオを理解する

私はいままで音を扱ったことはなかったので、PCMのことは聞いたことがあるという程度でした。仕事でオーディオのデバイスドライバを触ることになったので、その前提知識としてPCMのことを調べていたのですが、実際にいろいろといじってみたらとてもよく理解できたので紹介します。

PCMとは

高校の物理で習ったとおり、音は空気の振動の波です。マイクの音はアナログの波形として入力されますが、AUDIO CODECと呼ばれるチップでデジタルの波形に変換されてコンピュータに入ってきます。逆に音を出すときにはデジタルの波形をAUDIO CODECに流すと、そこでアナログの波形に変換されてスピーカーを鳴らします。
このAUDIO CODECとやりとりするデジタルの波形のフォーマットがPCMです。

PCM(Pluse Code Modulation)についてはこちらの記事がわかりやすいです。
‚o‚b‚l‚ÌŠî‘b’mŽ¯

ここでは主に量子化ビット数:16, サンプリングレート48kHzのPCMデータを扱います。

ffmpegでPCMデータを扱う

本題はPCMデータの加工なので、その前後の面倒なところはffmpegにやってもらいます。

音声ファイルをPCMデータに変換する

$ ffmpeg -i src.ogg -f s16le -ar 48k -ac 2 d.pcm

ar はサンプリングレート。ac はチャネル数。ステレオなら2, モノラルなら1

PCMデータを音声ファイルに変換する

$ ffmpeg -f s16le -ar 48k -ac 2 -i d.pcm d.wav

PCMデータを再生する

$ ffplay -f s16le -ar 48k -ac 2 d.pcm 

あるいは、alsaのutilityコマンドを使って

$ aplay -f dat d.pcm

-f dat は -f S16_LE -c2 -r48000 の省略形。

PCMデータのサンプリングレートを変換する

$ ffmpeg -f s16le -ar 48k -ac 2 -i d.pcm -f s16le -ar 44.1k -ac 2 d_441.pcm

PCMデータの中を見てみる

まずは手元にあるPCMのデータをダンプして中をのぞいてみます。これは 16bitのモノラル、サンプリングレート48kHzのデータです。

$ od -x a.pcm |head
0000000 0003 0003 0003 0004 0003 0002 0004 0001
0000020 0004 0004 0002 0002 0002 0001 0005 0003
0000040 0002 0002 0000 0003 0002 0005 0006 0002
0000060 0005 0004 0000 0005 0003 0001 0001 0003
0000100 fff5 001b ffe0 0040 ff87 07b5 037e 0292
0000120 02be 0256 0273 022f 01c2 012a 011d 0164
0000140 0157 00e6 007c 0070 00e2 01a8 0214 01cc
0000160 0167 014d 016f 01a0 019b 0133 00a4 0027
0000200 fffc 0029 0075 0092 004b ffe7 ffc6 ffc3
0000220 ffd3 ffee ffdf ff97 ff40 fef0 fed4 fef1

このデータのファイルサイズは約3MB。

 $ ls -l a.pcm
 -rwxr-xr-x 1 koba koba 3105280 Sep 30 10:09 a.pcm

再生してみます。

$ ffplay  -f s16le -ar 48k -ac 1 a.pcm
  ...
[s16le @ 0x7fc9440008c0] Estimating duration from bitrate, this may be inaccurate
Input #0, s16le, from 'a.pcm':
  Duration: 00:00:32.35, bitrate: 767 kb/s
    Stream #0:0: Audio: pcm_s16le, 48000 Hz, 1 channels, s16, 768 kb/s
  35.37 M-A: -0.000 fd=   0 aq=    0KB vq=    0KB sq=    0B f=0/0 

正しく再生できました。
演奏時間は約32秒。16bitは2バイトなので
2 * 48000 * 32 = 3,072,000
ファイルサイズとのつじつまも合います。

モノラル(1ch)のPCMデータをステレオに変換する

モノラルのPCMデータは符号付き16bitのデータが単純に並んでいるだけです。そしてステレオのデータはそれが16bit x2のペアが並びます。
以下のようなCの関数で、元のデータにひとつづつ0を挿入してみます。

int mono2left(char *inbuf, char *outbuf, int len)
{
	int16_t *inp;
	int16_t *outp;
	int cnt = len / sizeof(int16_t);
	
	inp = (int16_t*)inbuf;
	outp = (int16_t*)outbuf;
	while (cnt-- > 0) {
		*outp++ = *inp;
		*outp++ = 0;
		
		inp++;
	}
	return (char*)outp - outbuf;
}

mono2left.c
そしてでき上がったデータがこちら。

$ od -x a_left.pcm |head
0000000 0003 0000 0003 0000 0003 0000 0004 0000
0000020 0003 0000 0002 0000 0004 0000 0001 0000
0000040 0004 0000 0004 0000 0002 0000 0002 0000
0000060 0002 0000 0001 0000 0005 0000 0003 0000
0000100 0002 0000 0002 0000 0000 0000 0003 0000
0000120 0002 0000 0005 0000 0006 0000 0002 0000
0000140 0005 0000 0004 0000 0000 0000 0005 0000
0000160 0003 0000 0001 0000 0001 0000 0003 0000
0000200 fff5 0000 001b 0000 ffe0 0000 0040 0000
0000220 ff87 0000 07b5 0000 037e 0000 0292 0000

これを -ac 1 を 2 に変えて再生してみます。

$ ffplay  -f s16le -ar 48k -ac 2 a_left.pcm

みごとに元の音が左チャネルからでるステレオのPCMデータになりました。

以下のようにすれば、右チャネルからでるようになります。

	while (cnt-- > 0) {
		*outp++ = 0;
		*outp++ = *inp;
		
		inp++;
	}

mono2right.c
以下のようにすれば左右両方から同じ音が出ます。

	while (cnt-- > 0) {
		*outp++ = *inp;
		*outp++ = *inp;
		
		inp++;
	}

mono2stereo.c
なるほど。

PCMデータの音量を変更する

高校の物理で習ったとおり、音の大きさは波形の振幅で決まります。振幅を変更するにはかけ算でできます。
以下の関数は左と右で独立にボリュームを変更できるようにしてみました。

int changeVol(char *inbuf, char *outbuf, int len, float vol_l, float vol_r)
{
	int16_t *inp;
	int16_t *outp;
	int cnt = len / sizeof(int16_t) / 2;
	
	inp = (int16_t*)inbuf;
	outp = (int16_t*)outbuf;
	while (cnt-- > 0) {
		*outp++ = *inp++ * vol_l;
		*outp++ = *inp++ * vol_r;
	}
	return (char*)outp - outbuf;
}

vol.c
これで音の大きさが変わりました。

2つのPCMデータをミックスする

高校の物理で習ったとおり、2つの音の合成は足し算です。
実際には16bitで表現できる範囲を超えてしまうとまずいので、ボリュームを絞ってから足し算するようにします。
ただし2つのPCMデータを合成するには、そのフォーマット(量子化ビット数、サンプリングレート)が同じであることが前提になります。(その変換はffmpegなどのツールでやっておきます。)

int mix(char *inbuf1, char *inbuf2, char *outbuf, int len, float vol1, float vol2)
{
	int16_t *inp1, *inp2;
	int16_t *outp;
	int cnt = len / sizeof(int16_t);
	
	inp1 = (int16_t*)inbuf1;
	inp2 = (int16_t*)inbuf2;
	outp = (int16_t*)outbuf;
	while (cnt-- > 0) {
		*outp++ = (*inp1++ * vol1) + (*inp2++ * vol2);
	}
	return (char*)outp - outbuf;
}

mix.c
確かにこれで2つのPCMデータを混ぜることができました。単純なミキサーの原理がわかりました。

PCMデータの部分切り出し

PCMデータはモノラルなら2バイト、ステレオなら4バイトのデータの羅列です。なので、head, tailコマンドをバイト単位で指定することで任意の部分を切り出すことができます。
ステレオ、48kHzなら1秒間のデータ長は2 * 2 * 48000 = 192000 になります。

先頭の1秒間だけ切り出す

$ head --byte=192000 a2.pcm > a2_first1sec.pcm

最後の1秒間を捨てる

$ head --byte=-192000 a2.pcm > a2_all_but_last1sec.pcm

最後の1秒間だけ切り出す

$ tail --byte=192000 a2.pcm > a2_last1sec.pcm

先頭の1秒間を捨てる

$ tail --byte=+192000 a2.pcm > a2_all_but_first1sec.pcm

PCMデータの連結

連結は単にcatコマンドでつなぐだけです。

$ cat a2.pcm b2.pcm > c2.pcm

回数を指定してリピートするには以下のようにループを回します。

$ rm -f a2_10times.pcm
$ for i in `seq 10`; do cat a2.pcm >> a2_10times.pcm; done

まとめ

PCMデータの基本的な加工はこのように簡単な演算でできることがわかりました。これらを組み合わせれば、自在にテストデータを作れそうです。
実際にデータを加工して、その音を聞いて確認することを繰り返すうちにPCMデータがどんなものなのかを体で理解することができました。

Ubuntu 14.04でffmpegをビルドしてみた

基本的にこのページの内容をなぞっただけです。
CompilationGuide/Ubuntu – FFmpeg

準備

ソースとライブラリを置くディレクトリの作成と必要なライブラリの取得。

$ mkdir $HOME/work/ffmpeg
$ mkdir $HOME/work/ffmpeg/build

$ sudo apt-get update
$ sudo apt-get -y --force-yes install autoconf automake build-essential libass-dev libfreetype6-dev \
  libsdl1.2-dev libtheora-dev libtool libva-dev libvdpau-dev libvorbis-dev libxcb1-dev libxcb-shm0-dev \
  libxcb-xfixes0-dev pkg-config texinfo zlib1g-dev
$ sudo apt-get install yasm
$ sudo apt-get install libx264-dev

libx265のビルド

$ cd $HOME/work/ffmpeg
$ sudo apt-get install cmake mercurial
$ hg clone https://bitbucket.org/multicoreware/x265
$ cd x265/build/linux/
$ PATH="$HOME/bin:$PATH" cmake -G "Unix Makefiles" -DCMAKE_INSTALL_PREFIX="$HOME/work/ffmpeg/build" -DENABLE_SHARED:bool=off ../../source
$ make
$ make install
$ make clean

libfdk-aacのビルド

$ cd $HOME/work/ffmpeg
$ wget -O fdk-aac.tar.gz https://github.com/mstorsjo/fdk-aac/tarball/master
$ tar xvzf fdk-aac.tar.gz 
$ cd mstorsjo-fdk-aac-047376a/
$ autoreconf -fiv
$ ./configure --prefix="$HOME/work/ffmpeg/build" --disable-shared
$ make
$ make install
$ make distclean

ffmpegのビルド

$ cd $HOME/work/ffmpeg
$ tar xvf ~/Downloads/ffmpeg-2.7.2.tar.xz
$ cd ffmpeg-2.7.2/
$ vi myconfig.sh
$ sh myconfig.sh 
$ make
$ make install

myconfig.shの内容は以下の通り。

PKG_CONFIG_PATH="$HOME/work/ffmpeg/build/lib/pkgconfig" ./configure \
  --prefix="$HOME/work/ffmpeg/build" \
  --pkg-config-flags="--static" \
  --extra-cflags="-I$HOME/work/ffmpeg/build/include" \
  --extra-ldflags="-L$HOME/work/ffmpeg/build/lib" \
  --bindir="$HOME/bin" \
  --enable-gpl \
  --enable-libass \
  --enable-libfdk-aac \
  --enable-libfreetype \
  --enable-libtheora \
  --enable-libvorbis \
  --enable-libx264 \
  --enable-libx265 \
  --enable-nonfree

  #--enable-libmp3lame \
  #--enable-libopus \
  #--enable-libvpx \

できた。

$ which ffmpeg
/home/koba/bin/ffmpeg
$ ffmpeg -version
ffmpeg version 2.7.2 Copyright (c) 2000-2015 the FFmpeg developers
built with gcc 4.8 (Ubuntu 4.8.4-2ubuntu1~14.04)
configuration: --prefix=/home/koba/work/ffmpeg/build --pkg-config-flags=--static --extra-cflags=-I/home/koba/work/ffmpeg/build/include --extra-ldflags=-L/home/koba/work/ffmpeg/build/lib --bindir=/home/koba/bin --enable-gpl --enable-libass --enable-libfdk-aac --enable-libfreetype --enable-libtheora --enable-libvorbis --enable-libx264 --enable-libx265 --enable-nonfree
libavutil      54. 27.100 / 54. 27.100
libavcodec     56. 41.100 / 56. 41.100
libavformat    56. 36.100 / 56. 36.100
libavdevice    56.  4.100 / 56.  4.100
libavfilter     5. 16.101 /  5. 16.101
libswscale      3.  1.101 /  3.  1.101
libswresample   1.  2.100 /  1.  2.100
libpostproc    53.  3.100 / 53.  3.100

Ubuntu 14.04でcdebootstrapを使う

岩松さんの発表スライドを見て、cdebootstrap というものを知りました。
Raspberry Pi 2 Model BにDebian Jessie /armhfをインストールする (PDF)

Ubuntu 14.04 でこれを使おうとしてのですが、うまくいきません。

$ sudo cdebootstrap --foreign --arch armhf jessie /tmp/root http://http.debian.net/debian/
E: Unknown suite jessie
$ sudo cdebootstrap --foreign --arch armhf stable /tmp/root http://http.debian.net/debian/
P: Retrieving Release
P: Retrieving Release.gpg
P: Validating Release
I: Good signature from "Debian Archive Automatic Signing Key (7.0/wheezy) "
P: Parsing Release
E: Unknown suite jessie

どうも /usr/share/cdebootstrap/suites というファイルに記述漏れがあるようです。
以下のように修正したらうまくいくようになりました。

 $ diff -u /usr/share/cdebootstrap/suites.org /usr/share/cdebootstrap/suites
 --- /usr/share/cdebootstrap/suites.org	2013-05-15 06:01:44.000000000 +0900
 +++ /usr/share/cdebootstrap/suites	2015-09-26 16:13:39.645884787 +0900
 @@ -18,6 +18,14 @@
  Config: generic
  Keyring: debian-archive-keyring.gpg
  
 +Suite: jessie
 +Config: generic
 +Keyring: debian-archive-keyring.gpg
 +
 +Suite: stretch
 +Config: generic
 +Keyring: debian-archive-keyring.gpg
 +
  Suite: sid
  Config: generic
  Keyring: debian-archive-keyring.gpg
 @@ -71,14 +79,18 @@
  Keyring: ubuntu-archive-keyring.gpg
  
  Suite: oldstable
 +Config: generic
  Keyring: debian-archive-keyring.gpg
  
  Suite: stable
 +Config: generic
  Keyring: debian-archive-keyring.gpg
  
  Suite: testing
 +Config: generic
  Keyring: debian-archive-keyring.gpg
  
  Suite: unstable
 +Config: generic
  Keyring: debian-archive-keyring.gpg

以下のようにエラーが解消しました。

$ sudo cdebootstrap --foreign --arch armhf jessie /tmp/root http://http.debian.net/debian/
P: Retrieving Release
P: Retrieving Release.gpg
P: Validating Release
I: Good signature from "Debian Archive Automatic Signing Key (7.0/wheezy) "
P: Parsing Release
P: Validating Packages.gz
P: Retrieving Packages.gz
P: Validating Packages.gz
P: Parsing Packages
P: Retrieving gcc-4.9-base
P: Validating gcc-4.9-base
P: Retrieving multiarch-support
P: Validating multiarch-support
P: Retrieving libgcc1
   ...

でもまだすっきりとはいきませんでした。
次のトラブルは次の記事で。
cdebootstrapを使ってRaspberry Pi 2にDebain jessie armhf をインストールする - 組み込みの人。

cdebootstrapを使ってRaspberry Pi 2にDebain jessie armhf をインストールする

今回もまたNFSrootを使用します。
まずはcdebootstrapでNFSでexportするディレクトリにroot file system の元を作成します。

 sudo mkdir /export/armhf_jessie
 sudo cdebootstrap --foreign --arch armhf --include=openssh-server,ntp,ca-certificates,vim,sudo jessie /export/armhf_jessie/ http://http.debian.net/debian/

次にここからRaspberry Pi 2を起動するようにします。
Raspberry Pi 2のSDカードの/boot/cmdline.txt は以下のようにNFSサーバの/export/armhf_jessie をrootに指定します。

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.0.6:/export/armhf_jessie rw ip=dhcp elevator=deadline rootwait

前回のdebootstrapを使ったときと異なり、今回はinit=/bin/sh を追加する必要がありません。
/sbin/init が置き換えられていて、cdebootstrapのsecond stageが自動で実行されるようになっています。

しかし、実際に起動してみると以下のようなカーネルパニックを起こしました。

[    5.544858] VFS: Mounted root (nfs filesystem) on device 0:15.
[    5.552191] devtmpfs: mounted
[    5.555929] Freeing unused kernel memory: 416K (80776000 - 807de000)
trap: ERR: bad trap
[    5.905671] Kernel panic - not syncing: Attempted to kill init! exitcode=0x00000100
[    5.905671] 
[    5.914815] CPU: 0 PID: 1 Comm: init Not tainted 4.1.7-v7+ #2
[    5.920551] Hardware name: BCM2709
[    5.923987] [<80018440>] (unwind_backtrace) from [<80013e0c>] (show_stack+0x20/0x24)
[    5.931734] [<80013e0c>] (show_stack) from [<80558548>] (dump_stack+0x98/0xe0)
[    5.938959] [<80558548>] (dump_stack) from [<8055473c>] (panic+0xa4/0x204)
[    5.945836] [<8055473c>] (panic) from [<8002937c>] (do_exit+0xa0c/0xa64)
[    5.952536] [<8002937c>] (do_exit) from [<80029470>] (do_group_exit+0x50/0xcc)
[    5.959755] [<80029470>] (do_group_exit) from [<8002950c>] (__wake_up_parent+0x0/0x30)
[    5.967670] [<8002950c>] (__wake_up_parent) from [<8000f980>] (ret_fast_syscall+0x0/0x54)
[    5.975847] CPU2: stopping
[    5.978559] CPU: 2 PID: 0 Comm: swapper/2 Not tainted 4.1.7-v7+ #2
[    5.984727] Hardware name: BCM2709
[    5.988141] [<80018440>] (unwind_backtrace) from [<80013e0c>] (show_stack+0x20/0x24)
 ...

trap: ERR がまずそうですが、調べてみるとこれはbashの拡張で、sh(dash)では使えないことがわかりました。
/sbin/cdeboostrap-foregin を修正します。
また、nfsrootのせいかrootfsをremountするところでエラーが発生するので、これを無視するようにしました。
そして、rootfile systemの構築はできるようになったのですが、rootのパスワードが未設定なのでログインできません。
なのでついでにそのシェルスクリプトの中でuserというsudo可能なユーザーを登録するようにしました。
これがそのpatchです。
cdebootstrap-foregin.patch

これで起動するとroot file systemを構築した後にuserというユーザーのパスワード入力のプロンプトが出ます。そしてオリジナルの/sbin/initが起動してログインプロンプトが出るので、先ほど登録したuserでログインできます。
そして、/etc/resolv.conf や/etc/hostname, /etc/hosts などの編集を行ってください。
cdebootstrapでの最初のブートのブートログ

debootstrapと比べるとcdebootstrapの方がinit=/bin/sh として手で編集するのを省略できるぶん楽ですね。

Raspberry Pi 2のカーネルをビルドしてみた

Raspberry Pi 2でセルフビルド

Raspbianの入ったRaspberry Pi2には最初からgitやgccが入っています。コアも4つあることだし、カーネルでもビルドしてみるかと軽い気持ちで始めてみましたが、やはり無謀でした。
そもそもカーネルのソースリポジトリをgit clone するだけで、やたらと時間がかかります。測っていませんでしたが30分以上。
そして make -j4 でビルドしましたが、2時間以上かかりました。
これではconfigをちょっと変更して再ビルドするのも大変です。おすすめしません。負荷テストとして実行する以外は。

クロスビルド

やはり組み込みLinuxはクロスビルドが基本です。
いくつかつまづいたのですが、結局は以下のページの通りやればビルドして動かすことができました。
https://www.raspberrypi.org/documentation/linux/kernel/building.md

6コア12スレッドのマシンでmake -j12 としたときのビルド時間は約3分半でした。
つまづいたポイントは以下の2つ。

  • ビルド後にコピーするファイルとして、kernel7.imgと/lib/modulesの他にdtbファイルも更新が必要。
  • .configファイルとしてv3.18.11の/proc/config.gzから持ってくるのでなくmake bcm2709_defconfig する。

.configファイルにv3.18.11のを使用したらEthernetとUSBが使えませんでした。

リモートログインしてビルドしたときには、以下のスクリプトでコピーするべきファイルをひとつにまとめることができます。

set -x
export KERNEL_RELEASE=`cat include/config/kernel.release`
export INSTALL_MOD_PATH=../$KERNEL_RELEASE
mkdir -p ../$KERNEL_RELEASE/boot/overlays
make modules_install
scripts/mkknlimg arch/arm/boot/zImage ../$KERNEL_RELEASE/boot/kernel7.img
cp arch/arm/boot/dts/*.dtb ../$KERNEL_RELEASE/boot/
cp arch/arm/boot/dts/overlays/*.dtb ../$KERNEL_RELEASE/boot/overlays/
cd ..
tar cvjf $KERNEL_RELEASE.tar.bz2 $KERNEL_RELEASE/

Raspberry Piカーネルのgitリポジトリはタグ付けが適切にされておらず、現在動いていたv3.18.11のソースがどのコミットなのか見つけることができませんでした。
私がビルドして動かしてみたのは、rpi-4.1.yブランチの以下の時点のものです。

59e76bb dwc_otg: Force host mode to fix incorrect compute module board

(今みたら4.1.8がマージされてました。)

ブートログをgistに貼りました。
Raspberry Pi 2 カーネルv4.1.7のブートログ

Raspberry PiをNFSrootで起動する

組込みLinuxの開発ではルートファイルシステムをまるごとNFSにしてしまうと便利です。Raspberry Piが起動しているかどうかに関わらずルートファイルシステムのファイルをいじることができるようになります。

NFSサーバの準備

以下のページを見てください。
NFSサーバの設定 - 組み込みの人。

RaspbianのrootパーテションをNFSディレクトリにコピーする

RaspbianのインストールされたSDカードをNFSサーバとなるLinuxマシンに挿します。
SDカードのrootパーティションが /media/koba/root にマウントされたとすると

$ sudo mkdir /export/raspbian
$ cd /export/raspbian
$ sudo cp -a /media/koba/root/ root

fstabを編集

/ をmountするところをコメントアウトします。

$ cat /export/raspbian/root/etc/fstab
proc            /proc           proc    defaults          0       0
/dev/mmcblk0p6  /boot           vfat    defaults          0       2
#/dev/mmcblk0p7  /               ext4    defaults,noatime  0       1
# a swapfile is not a swap partition, so no using swapon|off from here on, use  dphys-swapfile swap[on|off]  for that

カーネルのブートパラメータの変更

RaspbianのSDカードの/boot/cmdline.txt を以下のように変更します。(NFSサーバのIPアドレスが192.168.0.6のとき)

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.0.6:/export/raspbian/root rw ip=dhcp elevator=deadline rootwait

起動ログがシリアルコンソールに出るように、console=tty1 も外しました。
これで起動したときのブートログをgistに貼りました。
NFSrootで起動したブートログ

(追記)NFSのオプションパラメータのチューニング

カーネルパラメータのnfsroot= のところにNFSのオプションを指定できます。
rsize=32768,wsize=32768,tcp,vers=3 を指定するとデフォルトに比べてパフォーマンスがあがります。測定していませんが、体感的にはSDカードにルートファイルシステムを置いたときと同等になります。

dwc_otg.lpm_enable=0 console=ttyAMA0,115200 root=/dev/nfs nfsroot=192.168.0.6:/export/raspbian/root,rsize=32768,wsize=32768,tcp,vers=3 rw ip=dhcp elevator=deadline rootwait