lightMPDのrootイメージの作成方法(暫定版)
はじめに
lightMPDのルートイメージの作成方法について説明します。
lightMPDは
- ルートイメージ(mpdも含む)
- kernel
- boot loader
の3つのコンポーネントからなります。
lightMPDではルートイメージの作成にbuildrootを使用しています。buildrootは上記のコンポーネントをまとめてbuild および管理できますが、
lightMPDではルートイメージの作成だけに使用しています。kernel,boot loaderに付いては別途用意する必要があります。
kernelはお好きなバージョンを用意して下さい。
boot loaderについてはboot loaderで音が変わるという事はないので、lightMPDにパッケージ内の物を使うようにします。
boot loader を新たにbuildしたり他のシステムのを流用する場合はlightMPD付属のuEnv.txtが使えなくなる場合がありますので注意して下さい。
この文書の為にbeaglebone用のbuildroot設定ファイルを公開しました。
最近lightMPDでは扱わないbeagleboneのI2SのDACをlightMPDで使用する動きあり、具体的にlightMPDを改造したいと要望があるためbeagleboneで説明します。
公開した設定ファイルはbeaglebone用ですが、すこしの手直しでcubox,raspi,raspi2,cuboxi4などのlightMPDがサポートしているARM系のシステムに対応することが可能です。
その説明もこの文書で行います。
公開するに当たってbuildrootの最新版であるbuildroot-2016.11.1用の物を改めて用意しました。また、この設定で作成したuInitrdを含むlightMPDのパッケージも用意しました。
この設定ファイルではmpd-native-dsdというパッケージを追加してあります。mpd-native-dsdはmpd-0.19.21にnative-dsdおよびlightMPDで作成したパッチを適用したmpdです。
注意
- ここで作成したルートイメージは従来のuEnv.txtではbootできません。lightMPD v1.0.3のパッケージ内にあるuEnv.txtを使用して下さい。
- ルートイメージの作成にはsudoでの操作があります。操作をあやまると開発環境に使用されているLinuxを壊す場合もあります。くれぐれも注意して行って下さい。
- また、下記の説明で誤りもあるかもしれません。説明を鵜呑みにするのではなく、よく理解しながら進めてください。
- そういった意味ではある程度linuxの操作に習熟した人向けのドキュメントになります。
設定
buildrootにはコンパイラーをダウンロードして設定する機能もありますが、lightMPDの様に複数システムを構築する場合、システム毎にコンパイラーをダウンロードするのも煩わしいので、
lightMPDではbuildrootとは別にコンパイラーを用意します。
説明にあたってディレクトリを下記のシンボルで表します。
- $DOWNLOAD -- アーカイブをダウンロードするディレクトリ(適当に設定して下さい)
- $BASEDIR -- lightMPD開発用のディレクトリ(適当に設定して下さい)
- $BUILDROOT -- buildrootディレクトリ
- $TOOLCHAINS -- コンパイラー類が格納されているディレクトリ
ダウンロード
下記の物を$DOWNLOADにダウンロードして下さい。
- buildrootパッケージ
- buildroot-2016.11.1.tar.bz2
- コンパイラー
- 開発環境が64bit OSの場合
- gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf.tar.xz
- 開発環境が32bit OSの場合
- gcc-linaro-6.2.1-2016.11-i686_arm-linux-gnueabihf.tar.xz
- lightMPDのbuildroot設定ファイル類
- lightmpd-buildroot-beaglebone.tgz(バグがありましたので2017.01.27に再アップロードしました)
- lightMPDのrootシステム
- lightmpd-root-v15.tgz
- lightMPD v1.0.3パッケージ
- lightMPDbb-v1.0.3.zip
gccは開発環境に合わせてダウンロードして下さい。
gcc のバージョンを変更する場合は、設定ファイルを変更する必要があります。
パッケージのインストール
下記の通りにパッケージを展開して下さい。
下記例では64bit OSを前提にしています。
$TOOLCHAINSは$BASEDIR/toolchainsにしました。
# ディレクトリを作成します
mkdir $BASEDIR <--- $BASEDIRは適当に設定して下さい
mkdir $BASEDIR/beaglebone
mkdir $BASEDIR/toolchains
#コンパイラーの設定を行います
cd $BASEDIR/toolchains
tar xvfi $DOWNLOAD/gcc-linaro-6.2.1-2016.11-i686_arm-linux-gnueabihf.tar.xz
#buildrootの設定を行います。
cd $BASEDIR/beaglebone
tar xvfi $DOWNLOAD/buildroot-2016.11.1.tar.bz2
ln -s $TOOLCHAINS/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf lightmpd-gcc
#buildrootにlightmpdの設定ファイルをコピーします。
cd $BASEDIR/beaglebone/buildroot-2016.11.1
tar xvfz $DOWNLOAD/lightmpd-buildroot-beaglebone.tgz
# lightMPDの追加ファイルを展開します
cd $BASEDIR
tar xvfz $DOWNLOAD/lightmpd-root-v15.tgz
注意
ln -s $TOOLCHAINS/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf lightmpd-gccのリンク先($TOOLCHAINS/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf)は
/から指定して下さい。相対パスで指定するとbuild時にエラーになります。
$BASEDIRは下記のようになります。
$BASEDIR
├── beaglebone
│ ├── buildroot-2016.11.1
│ └── lightmpd-gcc -> $TOOLCHAINS/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf
├── lightmpd-root-v15
└── toolchains
└── gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf
build
下記のコマンドを実行します。
$ cd $BASEDIR/beaglebone/buildroot-2016.11.1
$ make lightmpd_beaglebone_defconfig
$ make
これで$BASEDIR/beaglebone/buildroot-2016.11.1/output/imagesに
- rootfs.cpio
- rootfs.romfs
- rootfs.cramfs
ができます。(mac mini-2011(cpu 2GHz corei7)上のvmwareで構築したubuntu-16.04(64bit)で約1時間ほどかかります)
rootfs.romfsの再構成
rootfs.romfsはmount の -t loop オプションを使ってマウントできますが、rootfs.romfs がリードオンリーなファイルシステムなので直接変更する事ができません。
そこで、rootfs.romfsの内容を別なディレクトリにコピーして変更します。その後、変更したディレクトリをromfsにします。
作業ディレクトリは適当に作成して下さい。下記説明では$BASEDIR/beaglebone で行います。
# rootfs.romfsを展開するディレクトリ(rootfs)を作成します
cd $BASEDIR/beaglebone
mkdir rootfs
cd rootfs
# rootfs.romfsをrootfsに展開します。
## /mntにrootfs.romfsをマウントします
sudo mount -o loop ../buildroot-2016.11.1/output/images/rootfs.romfs /mnt
sudo cp -a /mnt/* .
sudo umount /mnt
# lightmpd用にrootfsを再構成します。
## 書き込む必要なファイルを書き込み可能な領域へのシンボリックリンクにします。
sudo ../../lightmpd-root-v15/prepare.sh
## lightmpdの各種ファイルをrootfsに展開します。
sudo cp -a ../../lightmpd-root-v15/rootdir/* .
# roofsを initrd.romfsに戻します。
sudo genromfs -f ../initrd.romfs
cd ../
# initrd.romfsをu-bootの形式にします。
gzip initrd.romfs
mkimage -A arm -O linux -T ramdisk -C gzip -a 0 -e 0 -n "lightMPD@bbb-v1.0.3 romfs" -d initrd.romfs.gz uInitrd
uInitrd がu-bootでロード出来るルートイメージになります。
これで lightMPD-bb-v1.0.3.zipに含まれるuInitrdと同じ物が作成できます。
カスタマイズ
buildrootでmake menuconfigとすると下記のメニューよって設定ができます。menuconfigの結果は.configファイルに保存されます。
Target options --->
Build options --->
Toolchain --->
System configuration --->
Kernel --->
Target packages --->
Filesystem images --->
Bootloaders --->
Host utilities --->
Legacy config options --->
lightMPDでのbuildrootの使い方はルートイメージの作成だけです。このような場合、buildrootの設定は以下のように分類できます。
- クロスコンパイラの設定
- Target options
- Build options
- Toolchain
- システムの環境設定
- System configuration
- FilesSystem Images
- パッケージの設定
- Target packages
---
lightMPDで用意したconfigファイルを元にbeaglebone以外のシステムに対応する場合は以下のように変更して下さい。
Target optionos
ターゲットシステムのCPUの情報を設定します。
これはクロス開発環境をbuildする時に参照されます。 ターゲットのCPUに関する知識が必要になりますが、解らない場合はconfigsに各種システムのconfigがあります。configはxxxx_defconfigsとなっています。xxxxがボード又はCPU名になっています。
Build options
buildする環境を設定します。
これは殆どいじる必要がありません。あるとすれば gcc optiomiztion levelぐらいです。
Toolchain
クロス開発環境の設定を行います。
lightMPDで用意したconfigファイルを使う場合、arm系のシステムでは変更の必要はありません。
Toolchain type で buildroot toolchain を選ぶとbuildrootで用意したgccをbuildします。gccのバージョン、libcのバージョン等を選択できます。
gccのbuildには相当の時間がかかります。arm系の場合はlinaroのバイナリーを別途用意したほうがいいと思います。
Target Optimizastionsで最適化のオプションを指定しますが、ここでいろいろ設定するとパッケージのbuild時にエラーがでたり実行時にエラーが場合があるので、"-O2" ぐらいにして下さい。
さらなる最適化が必要な場合はパッケージ毎に指定します。
System configuration
ターゲットシステムの実行時の環境を指定します。
System hostname,System bannerは必要に応じて変更します。Run a getty after boot をチェックした場合は、ボードの仕様に合わせてTTY port, Baudrateを指定します。
これをチェックしないとシリアル接続のコンソールが使えなくなります。
Target packages
rootイメージに含むパッケージを選択します。
Filesystem images
結果として作成するイメージファイルを指定します。
lightMPDの設定ファイルでは
- cpio the root filesystem
- cramfs root filesystem
- romfs root filesystem
をチェックしてありますが、実際につかっているのはromfsです。
cpio the root filesystem
initramfsという仕組みを使ってramdisk上にルートファイルを構築します。ファイルシステムはrootfsでこれは読み書きできます。
initramfsの前はinitrdという仕組みが一般的でした。initrdはramdisk上にext2ファイルシステムを作ってそこにルートファイルを構築します。
私はinitramfsはinitrdと殆ど同じ物と考えていました。ルートファイルシステムにext2を使うのを嫌ってromfsを採用しましたが、これならinitramfsを採用すればよかったとちょっと後悔しています。
将来romfsからinitramfsに変更するかもしれません。これから独自にシステムを構築する場合もinitramfsの方がシンプルになります。
romfsではramdiskのサイズをkernelのパラメータで指定しましたが、initramfsでは実メモリの1/2に設定されています。
cramfs root filesystem
cramfsはromfsと同じで読み込み専用のファイルシステムです。cramfsは圧縮されたままディスクに展開されるのでメモリの小さなシステムでは有効です。
romfs root filesystem
romfsはその名の通り読み込み専用のファイルシステムです。
lightMPDではプログラムをロードしたらディスクへのアクセスは行わないのでなるべく大げさなファイルシステムは使いたくありませんでした。
ファイルシステムはデータの読み書きの他に、領域の管理や排他制御などの機能があります。
領域管理というのはデータを書き込む際ににまだ使われていない領域を探し出したり、データを削除したときにその領域をまだ使われていない状態にする機能の事です。
排他制御は同じ領域に同時に読み書きが行われたときに適切に処理する機能です。読み込み専用のromfsではこれらの機能は必要なくとてもシンプルになります。
/var配下のファイルの様に書き込みが出来ないと困るファイルもあるので、/varはtmpfsにマウントしシステム初期化時にその配下のファイルを作成しています。
lightMPDのブートシーケンス
linuxでのブートは一般的に以下のようになります。
- kernelをメモリーにロード
- ミニルート(initrd,initramfs等)をロード
- arm系の場合はdtbがあればそれをロード
- kernel起動
- ramdiskにミニルートをコピー
- 初期化
- rootファイルをマウント
- init(またはinitrc)を実行
このうち、4.までの処理はブートローダーと呼ばれるプログラムが行います。arm系(raspi系は除く)はu-boot,X86系ではgrubがよく使われます。raspi系は独自のブートロードを持っています。
カーネルの初期化ではIOの初期化なども行われますが、ドライバーがモジュールになっている場合はミニルートからロードします。また、デバイスにアクセスするためのデバイスファイル(特殊ファイル)もミニルートになければなりません。
rootファイルをマウントするとミニルートが格納されているramdiskは破棄されます。rootとして/dev/ram0(ミニルートのあるramdisk) を指定した場合はこの処理は行われません。
初期化が終わるとミニルートがinitrdの場合はlinuxrcを起動します。linuxrc終了後、kernelが/sbin/initを起動します。
initramfs(romfsも該当)の場合はinitを起動します。init内で/sbin/initを起動します。
現在のinitは/sbin/initを起動する為の簡単なスクリプトです。
#!/bin/sh
# devtmpfs does not get automounted for initramfs
/bin/mount -t devtmpfs devtmpfs /dev
exec 0</dev/console
exec 1>/dev/console
exec 2>/dev/console
exec /sbin/init $*
このスクリプトを修正してシステム独自の処理を加える事も可能ですが、/sbin/initが起動する前の状態ではいろいろと制約があります。
/sbin/initは全てのプログラムの親プロセスになり、本来デーモンというのはinitの事を言ってました。initの他に最近ではsystemdもよく使われます。
busybox menuconfigの System Configurataion -> Init System で下記より選択できます。
- BusyBox
- systemV
- systemd
- None
BusyBox,systemVを選択すると/sbin/initがデーモンに成ります。
systemdはkernelとも密接に関連します。Control Group Supportとかがないとエラーになります。
lightMPDではInit System に BusyBoxを選択しています。
/sbin/initについて
BusyBoxの/sbin/initとsystemvのそれとは仕様が異なります。systemvの方が本家になります。最も大きな違いがsystemvにはrunlevelという概念がありますが、BusyBoxにはそれがありません。
ここではBusyBoxの/sbin/initについて説明します。
/sbin/initは/etc/inittabに従ってシステムの初期化を行います。システム初期化後、常駐します。
/etc/inittabは以下のようになっています。
# Copyright (C) 2001 Erik Andersen <andersen@codepoet.org>
#
# Note: BusyBox init doesn't support runlevels. The runlevels field is
# completely ignored by BusyBox init. If you want runlevels, use
# sysvinit.
#
# Format for each entry: <id>:<runlevels>:<action>:<process>
#
# id == tty to run on, or empty for /dev/console
# runlevels == ignored
# action == one of sysinit, respawn, askfirst, wait, and once
# process == program to run
# Startup the system
::sysinit:/bin/mount -t proc proc /proc
#::sysinit:/bin/mount -o remount,rw /
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
::sysinit:/bin/hostname -F /etc/hostname
# now run any rc scripts
::sysinit:/etc/init.d/rcS
# Put a getty on the serial port
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 # GENERIC_SERIAL
# Stuff to do for the 3-finger salute
#::ctrlaltdel:/sbin/reboot
# Stuff to do before rebooting
::shutdown:/etc/init.d/rcK
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
コメントにあるように各行はid:runlevels:action:process になっています。
busyboxではid,runlevelsに特に意味はないようです。
actionはプロセスの起動方法を指定し、以下の物を指定します。
- sysinit 起動時に実行する
- respawn プロセスを起動し、終了したら再度起動する
- wait プロセスを起動し、終了するまで待つ
- askfirst 不明
- shutdown システム終了時に実行する
processは起動するプロセスです。
起動時の処理の内
::sysinit:/bin/mount -t proc proc /proc
::sysinit:/bin/mkdir -p /dev/pts
::sysinit:/bin/mkdir -p /dev/shm
::sysinit:/bin/mount -a
はディスクのマウントの準備およびマウントを行います。
::sysinit:/bin/hostname -F /etc/hostname
は/etc/hostnameをホスト名として設定しています。
::sysinit:/etc/init.d/rcS
は /etc/init.d/内のS00xxxx から S99yyyy までのスクリプトを数字の順に実行します。
ttyS0::respawn:/sbin/getty -L ttyS0 115200 vt100 # GENERIC_SERIAL
はシリアル回線(ttyS0)に対してgettyを起動します。gettyはlogin処理を行うプログラムです。beagleboneのシリアル回線はttyS0ですが、他のシステムではデバイス名が異なります。
これでシステムが起動したことになります。
lightMPDの初期化
lighMPDではシステム起動前に
- /var を tmpfsにマウントする
- /var.rom を/varにコピーする
- bootデバイスからlightmpd.confやmpdなどの必要なファイルを/var/lightMPD に展開する
- lightmpd.confをパースして各種設定ファイルを作成する
を行います。
この処理は/etc/init.d/rcSの起動前に行う必要があります。lightMPDでは/etc/init.d/S00setupvar を用意して /etc/init.d/rcSからS01***以降のスクリプトの起動に先立って行うようにしました。
その他の方法としては
- /init のスクリプトを変更して/sbin/initの起動前に行う
- inittabにsysinitとして登録して/sbin/initに実行してもらう
- /etc/init.d/rcSを変更して/etc/init.d/S??*を起動する前に行う
が考えられます。
1.の方法ではinittabを見て解るように/procすらマウントされていませんので、いろいろと制約があります。
2.,3.はすでにマウントもされているのでいずれの方法でも問題ありません。
1.の方法では,S00setupvarをsetupvarにリネームして/etc/init.d/rcSのS??* 起動する前でこれを呼び出します。
2.の方法の場合、inittabのmount -aの行の後に
::sysinit:/etc/init.d/setupvar
を追加します。
buildrootのバージョンによってinittabやrcSが変更されるとその部分を変更しなければならず煩わしいのでこれらのファイルを変更しない方法を選択しました。
特定のアプリケーションだけを起動すればいい場合は、/sbin/initの代わりにそのアプリケーションを起動する事も可能です。但し、そのアプリケーションが起動する環境を先立って構築しておかなければなりません。(私は実施したことはありません)
/etc/init.d/S00setupvar
syssetup.scm
パッケージの選択
make menuconfig
mpd-native-dsdについて
パッケージの追加(polipoを例にとって)
新しいパッケージの作成(mpd-0.20を例にとって)
別システム(ARMベース)への対応
シングルCPU(cuboxを例にとって)
SMP対応のCPU(raspberry pi 2を例にとって)
soxのopenmp対応