「 2017年02月07日 」一覧

Buildrootの設定について

Buildrootの設定について、makeのオプションなどの解説です。

Buildrootのドキュメントを読めば書いてあることしか書いてませんが、ドキュメントのどこかを探すのが面倒な項目なので、ここにアップしておきます。

Buildrootの設定の変更

 

Buildroot - カーネル設定の変更

 

make linux-menuconfig

 

 

Linuxカーネルの設定を行います

デスクトップシステムでは、様々なデバイスの追加、削除をユーザーが行うことを想定して、デバイスドライバの大部分をモジュール構成にして実装することが多いですが、組込みシステムにおいては、不特定のデバイスの追加を想定する必要がありませんので、デバイスドライバはカーネルと一緒にコンパイルしておくことをお勧めします。

起動時にドライバをロードしておくことによって、Plug&Playの動作不良や、カーネルとモジュールのバージョンの差異などを気にする必要がなくなります。

 

実例

www.kernel.org にて配布されているLinuxの構成では、Intel社の1000BaseTチップであるIGBドライバは標準では無効に設定されています。(各ディストリビューションで、有効に再設定されて使用されていることが多いです)ここで、IGBドライバを有効にしておかないと、ネットワークが利用できなくなるので、有効に設定しましょう。

 

 

Buildroot - busyboxの設定変更

 

make busybox-menuconfig

 

 

Linuxの基本コマンドセットの追加、削除を行います。

組込みシステムにおいて、使用しないコマンドを減らすことによって、メモリ領域やセキュリティ上の問題を軽減することができます。

Buildrootシステムでは、ファイル一覧の表示コマンド「ls」や、ファイルのコピーコマンド「cp」などの基本コマンドはBusyboxで提供されているものを使用することになっています。(詳しくは、http://www.busybox.org を参照)

ここで、デバッグ中はほぼすべてのコマンドを使用するが、組込みシステムの実環境では使用しないコマンドを削除するなどの設定を行うことができます。

「getty」や「login」コマンドを削除してしまえば、ログインできないシステムにするといったことも可能です。

 

Buildroot - Buildrootシステムの設定の変更

 

make menuconfig

 

 

Buildrootシステム全体の設定を行います。クロスコンパイラのバージョン、使用するライブラリの選択、パッケージの選択などシステムに関する様々な設定を行うことができます。

 

 

 

Buildroot - パッケージの追加

 

Buildrootの環境で、ユーザーの作成したプログラムをターゲットにインストールするには、実行ファイルを、“output/target”以下にコピーして、“make”を実行すれば”initrd”に統合されて、実行環境が作成されるのですが、”make“一発ですべてが作成される環境を作っておいたほうが、ユーザープログラムの数や種類が増えたときに、バージョン管理などもしやすくなるので、便利です。

ここでは、ユーザーが作成したパッケージをBuildrootのビルドツリーに組込も方法を解説していきます。

とりあず、ユーザープログラムが“make”コマンドでビルドできる物として、存在している場合にBuildrootシステムのパッケージとして追加する方法を解説します。

 

 

 

Buildroot - autotoolパッケージの追加

 

automake パッケージをBuildrootのビルドツリーに組み込む方法を解説します。

automakeパッケージとは、オープンソースソフトウェアとして世の中に出回っているソフトウェアの多くが採用しているコンパイル方式で、ソースコードをダウンロードした後、「configure」「make」「make install」の順でコマンドを実行してインストールする形式のソフトウェアです。

デフォルトでフルセットをインストールすると、「man」コマンドで参照できるドキュメント関連もすべてインストールされてしまうので、組込みシステムで使用しないマニュアル類をインストールしないような「configure」スクリプトや、パッチなどを準備する必要があります。

また、一部のパッケージでは、「make」時にセルフテストを実行しながらビルドするものもあるので、クロス開発環境が標準のBuildrootシステムでは、それらを回避するためのパッチを作成する必要がある場合もあります。

 

Buildroot - CMAKEパッケージの追加

 

CMAKEパッケージは、一部のオープンソースソフトウェアで利用されているビルドシステムで、automakeと同等のことを実行しますが、独特の癖があるので、CMAKE用のスクリプトと、パッチを準備する必要があります。

 

 

Buildroot - 特定パッケージのリビルド

make [package-name]-rebuild

 

Buildroot - スケルトン

起動スクリプトや、Linuxとして動作させるための設定ファイル群のようなテキストファイルは、Buildrootのスケルトンとして、「system/skeleton」に準備されています。

初期の設定を変更したい場合などは、ここのファイルを変更するか、buildrootのPOST-Buildスクリプトを使用することになります。

 

 

処理の追加

 

この項目は、どのような機器を開発していくかによって、様々な方法が考えられますが、組込みシステム一般として、考慮しておくべき必要がある処理について記載しておきます。

 

 

設定項目

まったく同じものを量産するのではない限り、各種機器の設定が必要になります。今回のシステムでは、書き込み可能なファイルシステムを準備してこなかったので、ここで、設定の保存方法について解説していきたいと思います。

設定とは、ネットワーク機器であればMACアドレスや、IPアドレスといったもの、Webサーバーなどであれば、管理者ユーザーのパスワードなど、機器によって様々な項目を設定する必要があり、その設定項目についても、MACアドレスやシリアル番号のように工場出荷時に設定しておくべきもの、IPアドレスのように設置時に設定し原則変更しないもの、パスワードのようにユーザーの都合で常時変更が入るもの、ログのように常時変更されるものなど、様々な項目が考えられます。

 

工場出荷時に設定しておくべき項目

工場出荷時に設定される項目については、一般的に、ハードウェアに専用の格納領域があり、そこに保存されていることが多いです。

例えば、PCでは、EthernetのMACアドレスは、ネットワークコントローラに付随しているROMに保存され、デバイスドライバのロード時にそれを読み込むことになっています。組込みシステムでは、SoCにイーサネットコントローラが内蔵され、MACアドレス専用のROMがつけられない場合や、ROMのコストや設置面積を削減したい場合などの理由で、他の領域に保存してあることもあります、その場合には、その内容をデバイスドライバのロード時に通知するなどの仕組みをLinuxのカーネルに追加する必要があります。

 

設置時に設定しておくべき項目

設置時に設定しておくべき項目を保存するには、記憶メディアに専用の領域を設け、そこに保存する必要があります。

一般的にブートデバイスに、パーティションを作成しておき、設定変更がなされた場合に、一時的にその領域を書き込み可能で再マウントした後に、設定情報を書き込み、書き込みが済んだら、即座にアンマウントするといった方法をとります。

設定情報については、ファームウェアファイルとは別の設定情報ファイルを作成し、特定領域に保存しておく必要がありますが、設定用のUIを準備する必要はありません。

 

ユーザーの操作により設定される項目

設置時に設定しておくべき項目との違いは、設置時に設定する項目は、管理者がいつ書き込みを行っているかを把握できるのに対して、ユーザーの操作は任意の時間に発生するため、機器の電源断による設定項目の破損に対応しておく必要があるといった点にあります。

このような環境下では、設定項目を保存する領域を2つ以上確保して、一方の書き込みが済んで、アンマウントが完全に完了したことを確認した後に、もう一方の保存領域に同じものを書き込むといった手段が必要になります。

読み込み時は、最初の保存領域への保存が完了して、アンマウントが済んでいれば、ファイルシステムがクリーンな状態になっているため、ファイルシステムの健全性フラグを確認して、クリーンなファイルシステムから設定情報を読み出すようにします。

また、ユーザーによる設定変更用のUIを準備する必要があります。

設定変更用のUIは、コマンドラインによる設定か、WebUIによる設定が一般的です。

保存のタイミングは、UIによる操作によりますが、設定変更とのタイミングが重要で、設定変更、設定保存の組み合わせは、以下のような状況を考慮する必要があります。

 

1. 設定変更、保存無し

設定変更だけを実行し、保存を行わない。管理者パスワードや、IPアドレスの変更など、ユーザーが入力を間違えてしまった場合、設定変更と同時に保存を行ってしまうと、操作中に電源断などが発生した場合に操作不能な状況に陥ってしまうような設定変更を行う場合、いったん設定変更だけを行い、変更に問題がないことをユーザーに確認させたうえでの設定保存を行う必要がある場合の項目になります。

2. 設定保存

1.のように設定変更だけを行った場合に、確認が完了したのでその設定を保存するためのコマンドです。

3. 設定変更と同時に保存

デバイスの追加、ユーザーの追加といった日常的に行われる操作で、誤った入力を行っても、やり直しのきく項目で、ユーザーの操作の煩雑さを削減したい場合のコマンドです。

4. 設定保存後再起動で、設定を反映

初期スクリプトでのみ設定を反映できる項目の設定変更を行った場合に実行されるコマンドです。デバイスの設定をリアルタイムで反映できない場合、設定保存後、機器を再起動することによって設定を反映させます。

ユーザーの操作は煩雑になりますが、サービスを起動したままの設定変更の必要がないため、実装は楽になります。

 

# mount -t vfat /dev/sda2 /config

#

 

 

 

常時変更される項目

常時更新される可能性のある情報を安全に保存するには、外部の記憶装置を用いる以外に、不意の電源断などから安全に保存する方法はないのですが、組み込みシステムでは以下のような方法を用いて、ファイルシステムの健全性を保ちつつある程度までの情報を保存する方法がとられることがあります。

 

1.  通常の保存は、RAMディスク上に行う

2.  一定時間毎に、RAMディスク上に保存された情報に変更があったかどうかの確認を行う。

3.  情報に変更があった場合には、3.の「ユーザーの操作により設定される項目」で示したように、複数のデバイスまたはパーティションに情報を保存する。

といった手順を行うことによって、ファイルシステムを破壊することなく、情報の保存を行うことができます。

 

 

 

ファームウェアのバージョンアップ

システムとして完成させるためには、将来のファームウェアのバージョンアップにも対応しておく必要があります。

今回、ファームウェアとして、作成するファイルは1つだけになりますので、そのバージョンの確認と、新しいファームウェアのバージョンを比較して、新しいファームウェアが存在する場合には、それに変更するためのインターフェイスを作成しておく必要があります。この機能がない場合には、一旦組み立てラインまで機器を戻さないとファームウェアのアップデートができないことになりますので、必ず実装しておきましょう。

ファームウェアのバージョンの確認には、ファイルのタイムスタンプや、ファイル名などを用いる方法があります。

また、ネットワークを用いてアップデートする場合など、セキュリティが要求される分野では、これから入れ替えようとするファームウェアが正しい開発元で開発されたものであるかどうかを確認するための仕組みも必要となります。そのための、コード署名ロジックなどの実装も考慮しておきましょう。

 

 


組込みLinuxを起動するまでの基礎知識

Linuxには、サードパーティが提供しているディストリビューションを利用することが一般的ですが、ディストリビューションによって、デスクトップ用、サーバー用など様々な利用形態を、ディストリビューションを提供している組織が想定する利用環境に応じて作成しています。

その中には、組み込みシステム用ディストリビューションなども存在し、有償サポートを利用することによって、ユーザーが想定している組み込みシステムの構築を行っている組織もあります。

組み込みシステムでは、サーバーやルーターといったネットはあるが画面の無い環境、ビデオレコーダーのように画面はあるがネットはない環境、自動販売機や工作機器のように画面もネットも無い環境など、様々な形態が考えられるので、ディストリビューションの、画面を見ながらCDROMからインストールなどのイメージとは全く違うLinuxのインストールが必要になります。

このサイトでは、Buildrootという、ソースコードディストリビューションを利用して、組み込みシステムを構築していく方法を解説していきます。Buildrootは一般的なデスクトップのディストリビューションとは異なり、ソースコードオンリーのディストリビューションで、組み込みシステムにおいて必要最低限のツールをコンパイル、パッケージにするだけでなく、必要なクロスコンパイラ等もコンパイル環境に合わせて構築されるために、かなり厳密に組み込みファームウェアのバージョン管理等も行うことができます。

また、構造も単純なため、x86PC以外でも、各種チップメーカーから出されているリファレンスコードをBuildrootのソースツリーに組み込むことも比較的単純に行うことができます。

 

Initプロセス

 

Linuxカーネルは、起動すると、ルートファイルシステムをマウントし、”init”というプログラムを起動します。”init”は/etc/inittabファイルを参照し、それに記載された動作を行います。

※一部の解説書では”init”が /etc/init.d/rcXXを読み込んでと書いてあるものもありますが、”init”が読むのは/etc/inittabだけです。/etc/inittabの中で、/etc/init.d/rcXXを実行しろと書いてあれば、それを読み込みますし、/etc/hogehogeを読めと書いてあればそちらを読みます。

 

とりあえず、Buildroot標準の/etc/inittabを見てみましょう。

# /etc/inittab

#

# Copyright (C) 2001 Erik Andersen

#

# 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        == 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

console::respawn:/sbin/getty -L  console 0 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

 

最初のコメント欄を見ると、このファイルはBuildrootではなくてBuildrootで使用しているBusybox由来のファイルであることが分かります。

システム起動直後に実行すべき項目は、sysinitとして記載されています。

::sysinit:/bin/mount -t proc proc /proc

ここでは、最初にporcファイルシステムをマウントしています。

::sysinit:/bin/mount -o remount,rw /

この項目は、ルートファイルシステムを書き込み可能としてマウントしなおしています。これを実行しないと、次のmkdirコマンドが実行できません。

::sysinit:/bin/mkdir -p /dev/pts

ここでは、ptsデバイスを配置するためのディレクトリを作成しています。

::sysinit:/bin/mkdir -p /dev/shm

ここでは、shmデバイスを配置するためのディレクトリを作成しています。

::sysinit:/bin/mount -a

/etc/fstabに記載されているファイルシステムのマウントを行います。

::sysinit:/bin/hostname -F /etc/hostname

Hostnameの設定を行っています。

# now run any rc scripts

::sysinit:/etc/init.d/rcS

ここから、/etc/init.d以下のコマンドを実行して、デーモンの起動を行います。

ptsや、shmを使用しないのであれば、2-4行目は削除しても問題ありません。

 

idのフィールドは、デバイス名を記述します。起動後に、シリアルポートにもログインプロンプトを表示したい場合などでは、idに/dev/ttyS1などと書きます。

次は、runlevelのフィールドですが、Busyboxの/etc/inittabでは、runlevelを無視していることがわかります。Runlevelの概念は、初期のUNIXシステムでは、カーネル起動時のオプションが数字1文字しか指定できなかったために、それを用いて、システムの起動条件を変えるために使用していたのですが、最近のLinux(UNIX)システムでは、カーネル起動時の引数をいろいろ指定することができるので、わざわざrunlevelとして使用する必要もなくなりました。今でも、デスクトップシステムでは、GUIログインと、CUIログインを区別するために利用されることもあるようですが、ほかの引数で代用することも可能ですので、なくても問題ないでしょう。“:”のフォーマットは、他のシステムの/etc/inittabとの互換性のため残してあるようです。

次のフィールドは、起動条件になります。sysinitは起動時に一回だけ実行、respwanは実行したプログラムが終了したら再度実行(ログインプロンプトを出す場合などに使用)、ctlaltdelは Ctrl+Alt+Delが押された場合に実行するプログラムを指定、shutdownはシャットダウンを行う場合に実行するファイルを指定します。

これらによると、起動時は、特殊なファイルシステムのマウントを行い、ホスト名を設定した後で、/etc/init.d/rcSを実行するように指定されれてます。起動後は、コンソールにログインプロンプトを表示し、ログアウトされたら再度表示。Ctrl+Alt+Delが押された場合は、rebootコマンドを実行。シャットダウン時には、/etc/init.d/rcKスクリプトの実行後スワップを無効にしています。

※組み込みシステムでは、”init”自体を置き換えてしまうことも可能です。8Bitマイコンから、Linux環境に移行してきた場合などは、APIやドライバはカーネル提供の物を使い、アプリケーションや、ファイルシステムなどは必要ないとした場合に、”init”をユーザーのプログラムに置き換えてしまうことによって、非常にシンプルな構成のシステムとすることも可能です。8Bitマイコンのプログラムは、それ自体でメモリ管理や、割り込み制御などを行っているので、単純に移植だけをするときなどは、移植のための工数を最小限にすることができます。

 

次に、inittabで指定されている/etc/init.d/rc.Sを見ていきましょう。

 

#!/bin/sh

 

# Start all init scripts in /etc/init.d

# executing them in numerical order.

#

for i in /etc/init.d/S??* ;do

# Ignore dangling symlinks (if any).

[ ! -f “$i” ] && continue

case “$i” in

*.sh)

# Source shell script for speed.

(

trap – INT QUIT TSTP

set start

. $i

)

;;

*)

# No sh extension, so fork subprocess.

$i start

;;

esac

done

 

 

/etc/init.d/rc.Sでは、/etc/init.d以下の Sで始まっているファイルを、アルファベット順に実行していってます。そのうち、.shで終わるものは、再帰的にshellを起動せずに実行し、その他のファイルは、startオプションをつけて実行していってます。この再帰的にshellを実行しないことによる速度アップはたいしたことないと思われますので、あまり気にしなくてもいいでしょう。

このような方式をとるメリットとしては、あるサービスを起動するにあたって、例えばWebサービスはネットワークが起動した後で起動したいとした場合、Webサービスのプログラムのインストーラーは、他のスクリプトの存在や、他のスクリプトの中身を気にせずに、S41以降の番号を起動スクリプトにつけておけばいいということになるので、デスクトップOSのように、ユーザーがプログラムを必要に応じてインストールするような環境では有利に働きます。

一方、組み込みシステムのように、ユーザーがプログラムを追加することは考えなくていいシステムであれば、こういった構成をとる必要はありません。(こちらが使いやすいと思えば、このままでもいいし、他の構成をとった方がいいのであれば、他の構成をとることもできます。)

 

ディストリビューションで用いられているinitrd

 

ディストリビューションで用いられているLinuxのブートプロセスは、

 

ブートローダー

カーネル、initrdの読み込み

カーネル起動

カーネル

initrdをルートファイルシステムとしてマウント

initrdの中の”init”を起動

init

initrdの中のドライバをロード

inittab中で、ロードしたドライバを用いてDISKドライブをマウント

DISKドライブをルートファイルシステムに変更

DISKドライブ中のrcスクリプトを実行

 

といった手順で初期化されていきますが、今回のシステムでは、

 

ブートローダー

カーネル、initrdの読み込み

カーネル起動

カーネル

initrdをルートファイルシステムとしてマウント

initrdの中の”init”を起動

init

initrdの中のドライバをロード

initrdの中のrcスクリプトを実行

といった手順となります。つまり、ルートファイルシステムは、initrdのままアプリケーションを実行することになります。これによって、ディスクドライブがない状況でLinuxが動作することになり、ファイルシステムの破損などを考慮する必要がなくなります。