まずは、製品の機能を特徴づけるアプリケーションプログラムの作成方法について説明します。
アプリケーションプログラムは、C言語で作成するものとします。
これまでにC言語での開発経験がある方でも、Linuxでの開発スタイルやクロス開発特有の問題など、注意すべき点がいくつかあります。
次に、Debian ユーザーランドのルートファイルシステムアーカイブを作成する方法について説明します。
ルートファイルシステムアーカイブに、オリジナルのアプリケーションプログラムや設定ファイルを追加する方法も、ここで紹介します。
一通りの開発が完了したら、量産に向けた準備を行います。
アットマークテクノでは、Armadilloを使った製品の量産をなるべく簡単に行えるよう、カスタマイズサービスを提供しています。
本章では、C言語でアプリケーションプログラムのソースコードを作成し、コンパイル、実行する方法について説明します。
Linuxでのアプリケーション開発は初めてという方でも読み進められるように、まずホストである作業用PCでプログラムのコンパイルと実行をおこなう方法を説明します。
その後、作業用PCでターゲットとなるArmadillo用にプログラムをコンパイルし、Armadilloで実行する方法について説明します。
まずは、定番である「Hello World!」を表示するだけのアプリケーションプログラムを作成し、実行してみます。
以下に示す、図8.1「hello.c」を作成し、atmarkユーザーのホームディレクトリ(/home/atmark)に保存してください。
| テキストエディタ |
---|
Linuxでの定番のテキストエディタといえばviやemacsですが、これらは操作を覚えるだけでも大変です。
Debian GNU/Linuxでは、Windowsでのメモ帳のように気軽に使えるテキストエディタとしてgeditというアプリケーションが標準でインストールされています。 geditは「アプリケーション」-「アクセサリ」-「テキスト・エディタ」メニューから起動することができます。
操作方法は、Windowsアプリケーションに似ているので、すぐに覚えることができるでしょう。 |
入力したソースコードが意図したとおりに動作するか、まずは、ホストとなる作業用PC上で実行して確認します。
ソースコードをコンパイルするには、端末を起動して、以下のコマンドを実行してください。
Linuxでは、Cコンパイラとしてgcc(GNU C Compiler)を使用します。
gccの引数にソースコードのファイル名を与えて実行すると、コンパイル、アセンブル、リンクの一連の処理を自動で行い、実行ファイルを出力します。
-oオプションに続いて指定した引数で、実行ファイルの名前を指定することができます[]。
なお、コンパイル、アセンブル、リンクの一連の処理を行い、実行ファイルを生成することを、「ビルドする」と表現します。
実行ファイルは、カレントディレクトリにhelloというファイル名で作成されます。
カレントディレクトリにあるファイルを実行するには、「./」を付けて相対パスでファイル名を指定します。
エラーやワーニングなくコンパイルでき、意図したとおりに実行結果が表示されたでしょうか?
何か問題があれば、ソースコードを修正して、問題がなくなるまでコンパイル、実行を繰り返してください。
ホスト上で問題なく実行できたら、ターゲットとなるArmadillo用にクロスコンパイルします。
Armadillo(ARM)用にコンパイルするときは、arm-linux-gnueabihf-gccという名前のクロスコンパイラを使用します。
クロスコンパイラでコンパイルしたものは、ARM用のバイナリとなっているため、もちろんホストでは実行できません。
| ファイル形式の簡単な見分け方 |
---|
fileコマンドを使用すると、作成された実行ファイルがi386(x86)用なのか、ARM用なのかを簡単に見分ける事ができます。 i386用の実行ファイルをfileコマンドで調べると、以下のように「80386」と表示されます。 [ATDE ~]$ file hello
hello: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically
linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=84cc
e45ccca0ec56f0cbd2b36b299bdd484a3d94, not stripped ARM用のバイナリでは、「ARM」と表示されます。 [ATDE ~]$ file hello
hello: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically li
nked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=f
e0cc3af441ae9b586159ad8a9ced593a3c1c506, not stripped |
ターゲット上で実行するために、Armadilloに実行ファイルをコピーします。
Armadilloに実行ファイルをコピーする方法には様々なものがありますが、ネットワーク(Ethernet)経由で転送する方法が手間が少なくて良いでしょう。
ここでは、scp コマンドを使用することにします。
標準状態のArmadilloではSSH Serverが動作していないので、まずはArmadilloにopenssh-serverをインストールします。
次に scp コマンドを実行して、Armadilloにファイルを転送します。
scp では、リモートマシン上のファイルを次のように表現します。
"ユーザー名@IPアドレス:/ディレクトリ名/ディレクトリ名/.../ファイル名"
今回はローカルマシン上の "hello" というファイルを、リモートマシン上の atmark ユーザーのホームディレクトリ(/home/atmark/以下)にコピーすることにします。
|
"yes" と入力
|
|
Armadillo の atmark ユーザーのパスワードを入力[]
|
| 同じコマンドを入力する手間を省く |
---|
一度入力したことがあるコマンドを繰り返し入力するのは、大変面倒です。 シェルには、一度入力したコマンドを記憶しておくヒストリー機能が備わっています。 ↑キーで、それまでに入力したコマンドを表示します。 また、Ctrl-rでそれまでに入力したコマンドを遡って検索できます。 例えば、Ctrl-rに続いて、scと入力すると、「sc」を含む以前入力したコマンドを検索して表示します。
表示されたコマンドを実行するには、そのままEnterキーを入力してください。 [ATDE ~]$
(reverse-i-search)`sc': scp ./hello atmark@172.16.2.156:~/ |
転送した実行ファイルをArmadilloで実行してみます。
| |
---|
Linuxでプログラムを実行しようとした時、「Permission denied」というエラーが表示されることがあります。
これは、実行ファイルに実行権限がないことを意味しています。
Linuxシステムでは、ファイル一つ一つに、どのユーザーに対して読み、書き、実行する権限を与えるか、指定することができます。 ファイルの権限を変更するにはchmodコマンドを使用します。
+xオプションを付けてchmodコマンドを実行すると、ファイルに実行権限を付けることができます。
|
このように、Armadillo上で動作させるアプリケーションも、最初はホスト上でビルド、実行を繰り返してあらかたのバグを取り除いてから、ターゲットとなる Armadilloで動作確認するというのが、アプリケーション開発の基本的な流れになります。
ATDEを使ってシステムを構築するメリットの一つに、豊富なライブラリが利用可能である点が挙げられます。
本章では、ライブラリを使ったアプリケーションプログラムの作成方法について説明します。
| |
---|
必要なヘッダファイルの名前や、共有ライブラリのファイル名がわかっている場合は、Debian プロジェクトサイトの「パッケージの内容を検索」からファイルの含まれるパッケージの名前を探す事ができます。 また、パッケージの部分的な名前が分っている場合は apt-cache search コマンドを使って必要なパッケージを探す事もできます。 |
例として、算術演算ライブラリに含まれるsin関数を使用します。
sin関数はdouble型の引数を一つとり、その正弦の値を返す関数です。
引数はラジアンで指定します。
| 関数の定義を調べる |
---|
Linuxシステムでは、オンラインマニュアルでシステムコールとシステムライブラリに含まれる関数の定義を調べることができます。
オンラインマニュアルには、関数定義の他、関数が定義されているヘッダファイル、動作の詳細や戻り値などの情報が記載されています。
オンラインマニュアルを調べるには、manコマンドを使用します。 sin関数を調べるには、以下のコマンドを実行してください。 [ATDE ~]$ man sin |
sin関数を使用したサンプルプログラムを以下に示します。
math.hは、sin関数を定義しているヘッダファイルです。
sin.cをhello.cと同様にコンパイルすると、sinが未定義というエラーになります。
Linuxシステムでは、ライブラリはlibライブラリ名という名前になっています。
算術演算ライブラリの場合、libmです。
ビルド時にライブラリをリンクするには、-lライブラリ名オプションを指定します。
実行結果は以下のようになります。
Armadillo用にクロスコンパイルするには、hello.cの例と同様にコンパイラにクロスコンパイラを用いるだけです。
実行ファイルをArmadilloにscpで転送し、実行結果を確認してください。
ATDEでは、gccを用いてコンパイルを行った場合、インクルードパスは/usr/includeとなります。
「#include <ヘッダファイル名>」というディレクティブでヘッダファイルをインクルードした場合、インクルードパスにあるヘッダファイルを使用します。
クロスコンパイル用に、コンパイラとしてarm-linux-gnueabihf-gccを用いた場合のインクルードパスは、/usr/arm-linux-gnueabihf/includeとなります。
gccを用いた場合とは、参照するヘッダファイルが異なる事に注意してください。
また、ホスト用のライブラリは、/lib/i386-linux-gnuディレクトリにあります。
算術演算ライブラリの場合、/lib/i386-linux-gnu/libm.so.6[]です。
ARM用のライブラリは、/usr/arm-linux-gnueabihf/libディレクトリにあります。
ライブラリを使用するプログラムをターゲットで動かす場合には、実行ファイルだけでなく、ライブラリファイルもターゲット上の適切なパスに配置されている必要があります。
| |
---|
Armadillo-640でDebianから供給されるライブラリを利用する場合、基本的にはライブラリファイルの配置場所を気にする必要はありません。 Armadillo-640およびATDE7に同名のDebianパッケージをインストールすることで、自動的にライブラリファイルが適切なパスに配置されます。 例えば、libmの場合は、次のようにDebianパッケージをArmadillo-640、ATDE7にインストールします。 [armadillo ~]# apt-get update && apt-get install libc6 |
Armadillo-640に libc6 (ライブラリ本体)をインストール
|
[ATDE ~]$ sudo apt-get update
[ATDE ~]$ sudo apt-get install libc6
[ATDE ~]$ sudo apt-get install libc6-dev
[ATDE ~]$ sudo apt-get install libc6-dev-armhf-cross |
ATDE7に libc6 (ライブラリ本体)をインストール
| |
ATDE7に libc6-dev (ネイティブ開発用パッケージ)をインストール
| |
ATDE7に libc6-dev-armhf-cross (クロス開発用パッケージ)をインストール
|
|
libc6は、システム上のほぼ全てのプログラムが使用するため、すでにArmadillo-640およびATDE7にはインストール済みとなっています。
そのため、今回の例ではライブラリをインストールするという手順は省略しています。
プログラムをビルドする際、毎回gccコマンドを入力するのは手間がかかります。
makeを使うことで、複雑なビルド手順を自動化することができます。
makeは、makefileと呼ばれる設定ファイルにプログラムをビルドするルールを記述しておくと、それに従って次に行うべき手順を見つけ出し、必要なコマンドだけを実行してくれるツールです。
makefileには、以下の形式でルールを記述します。
makefileには、複数のルールを記述することができます。
1つのルールは必ず1つのターゲットを持ちます。
このターゲットが、そのルールで生成されるファイルとなります。
ルールには、ターゲットを生成するために必要な依存ファイルと、ターゲットを生成するためのコマンドを記述します。
依存ファイルは「<ターゲット>:」の後にスペース区切りで記述します。
また、コマンドは、ターゲットの次の行から行頭のタブ(スペースではなく)に続いて記述します。
依存ファイルとコマンドは、0個以上記述することができます。
つまり、依存ファイルやコマンドがない場合もあります。
また、makefileでは変数を使用することができます。
「変数名 = 値」という形式で定義し、$(変数名)で参照します。
基本的に、変数の値は文字列として扱われます。
sin.cをビルドするmakefileは以下のようになります。
sin.cと同じディレクトリに、Makefile(Mは大文字です)という名前で保存してください。
makeコマンドを引数を指定せずに実行した場合、makeはカレントディレクトリにあるGNUmakefile、makefile、Makefileという名前のファイルを順番に検索し、最初に見つけたファイルをmakefileとして認識します。
makefileを認識後、makeはmakefileで一番最初に記述されたルールに従って処理を行います。
図8.18「sin.cをビルドするMakefile」の場合、一番最初のルールは「all: $(TARGET)」です。これは、変数を展開すると「all: sin」となり、「allターゲットを生成するにはsinファイルが必要である」というルールになります。
allを作成するにはsinが必要ですので、makeは「sin: sin.o」というルールに従ってsinを生成しようと試みます。
このように、makeはルールに従って、最初のターゲットに必要なファイルを芋づる式に生成します。
「sin: sin.o」というルールは、「sinを生成するには、sin.oが必要」という意味になります。
sin.oには「%.o: %.c」というルールが適用されます。
これは、特殊なルールの書き方ですが、「.oで終わるターゲットを生成するには、.cで終わるファイルが必要」という意味になります。
.oと.cの前は、同じ文字列です。
即ち、「sin.oを生成するには、sin.cが必要」ということになります。
sin.cは、既にあるファイルなので、ここでようやくコマンドが実行されます。
%.oに対応するコマンドは、「$(CC) $(CFLAGS) -c -o $@ $<」です。
CCやCFLAGSは、Makefileの最初で定義されている変数です。
$@や$<は特殊な変数で、それぞれターゲット名と依存ファイル名を意味します。
そのため、このコマンドを展開すると、「gcc -Wall -Wextra -O2 -c -o sin.o sin.c」となります。
gccに見慣れないオプションが付いていますね。
-Wallと-Wextraは、警告オプションです。
ソースコードにバグを誘発しそうな構文があれば、コンパイル時に警告メッセージを表示してくれます。
gccでコンパイルを行う場合は、必ず警告オプションを付けておき、警告メッセージが出ないようなソースコードを記述することを習慣付けておくことで、C言語の構文が原因のバグを未然に防ぐことができます。
-O2は、最適化オプションです。
gccでは、いくつかの最適化レベルを指定することができます。
-O2を指定した場合、コードサイズと実行速度をどちらも犠牲にしないような最適化を行います。
-cオプションが付いている場合、gccはコンパイルとアセンブルまでしか行わず、リンク処理を行いません。
この時、出力ファイルはアセンブラが出力したオブジェクトファイルになります。
sin.cからsin.oが生成されると、次はsinターゲットに対応した「$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@」が実行されます。
$^も特殊な変数で、依存ファイルを意味します[]。
これを展開すると、「gcc -lm sin.o -o sin」となります。libmとsin.oをリンクして、実行ファイルsinを生成します。
sinが生成されると、allターゲットに対するルールが適用されます。
しかし、allターゲットに対応するコマンドはないので、何も行われません。
当然、allという名前のファイルも生成されません。
そのため、allターゲットに対する処理はmakeコマンドを実行するたび、毎回行われることになります。
実際の実行結果は、以下のようになります。
makeコマンドを実行すると、まず、sin.cのコンパイルが行われます。
このとき、警告オプションの影響で、使用していない変数(argcとargv)があるという警告が表示されています。
警告だけでエラーは出ていないので、オブジェクトファイルsin.oが生成され、それを元に実行ファイルsinが生成されています。
ここで、再度makeコマンドを実行しても何も行われません。
makeは、ターゲットと依存ファイルが変更された時刻を比較して、ターゲットの変更時刻が依存ファイルの変更時刻よりも新しい場合、ターゲットを再生成する必要はないと判断して、ターゲットに対応するコマンドを実行しません。
ターゲットがないか、ターゲットよりも依存ファイルが新しい場合のみターゲットの生成をおこないます。
makeコマンドには、引数にターゲット名を指定することもできます。
図8.18「sin.cをビルドするMakefile」では、cleanターゲットを引数として渡すと、生成したファイルを削除します。
最後に、makefileをクロスコンパイルに対応させる方法を紹介します。
ホスト用にビルドするか、クロス用にするかは、コンパイラにgccを使うか、arm-linux-gnueabihf-gccを使うかの違いだけで対応できることを利用します。
Makefileをこのように修正すると、引数なしでmakeコマンドを実行するとクロスコンパイルをおこない、「make CROSS=」として実行するとホスト用にコンパイルします。
Makefileの先頭で、CROSS変数にarm-linux-gnueabiを代入して定義しています。
「ifneq ($(CROSS),)」は、CROSS変数が空でなければ次の処理を実行することを意味します。
CROSS変数には値が代入されているので、次の処理が実行され、CROSS_PREFIX変数が定義されます。
CC変数には、gccの前にCROSS_PREFIX変数の値をつけた文字列を代入します。
そのため、CCの値はarm-linux-gnueabihf-gccとなり、クロスコンパイルが行われます。
makeコマンドは、引数で変数を定義でき、そのようにして定義した変数はmakefile中で定義する変数よりも優先される機能があります。
そのため、「make CROSS=」というように、CROSS変数を空文字列で定義すると、CROSS_PREFIXも定義されません。
そのため、CCの値はgccとなりホスト用のコンパイルが行われます。
このように、同じソースコード、同じmakefileを使用して、クロスコンパイルとホスト用コンパイルの両方に対応することができます。
8.2. Debian GNU/Linuxルートファイルシステムアーカイブの作成
Armadillo を一度起動した後にルートファイルシステム上には、使い方によってはsshの秘密鍵や、動作ログ、シェルのコマンド履歴、ハードウェアのUUIDに紐付く設定ファイル等が生成されています。
そのまま、他のArmadillo にルートファイルシステムをコピーした場合は、鍵の流出やUUIDの不一致による動作の相違が起きる可能性があります。
そのため、量産等に使用するルートファイルシステムは新規に at-debian-builder を使って構築します。
本章では、at-debian-builder を使用してDebian GNU/Linuxルートファイルシステムアーカイブを作成する方法と
オリジナルのアプリケーションプログラムや設定ファイルを追加する方法について説明します。
8.2.1. at-debian-builderの取得
at-debian-builderは、以下のURLからダウンロードすることができます。
基本的には、最新バージョンのソースコードを使用するようにしてください。
本章の例では、以下のバージョンを使用します。
表8.1 使用するat-debian-builderのバージョン
ファイルの取得にはwgetコマンドを使います。
アーカイブの展開にはtarコマンドを使います。
これで、ルートファイルシステムアーカイブを作成する準備が整いました。
ここで build.sh を実行すると、標準状態のルートファイルシステムアーカイブを作成することができますが、今は実行しません。
8.2.2. ルートファイルシステムのカスタマイズ
組み込み機器の場合、プロダクト固有のアプリケーションプログラムは、必ずと言っていいほど存在すると思います。
ここでは、プロダクト固有のアプリケーションプログラムをルートファイルシステムアーカイブに追加する方法を紹介します。
at-debian-builder では、製品名_resouces内のファイルを変更し、build.shを実行することで、ルートファイルシステムをカスタマイズすることができます。
Armadillo-640の場合は、a600_resources内のファイルを修正していきます。
8.2.2.1. ファイルやディレクトリを追加する
a600_resources 以下にファイルやディレクトリを追加すると、そのファイルやディレクトリはそのままルートファイルシステムに配置されます。
a600_resourcesディレクトリはルートファイルシステム上のルートディレクトリ( / )に対応しており、a600_resourcesディレクトリ直下に配置されたファイルは、ルートファイルシステム上のルートディレクトリ直下に配置されます。
ただし、最初から a600_resources 以下に配置されている resources ディレクトリは例外で、ルートファイルシステム上に配置されません。
resources ディレクトリ以下には、ルートファイルシステムアーカイブの作成の際に使用するスクリプトやインストールするパッケージ情報などが格納されています。
例として、「アプリケーションプログラムの作成」の「ライブラリとヘッダファイル」で作成した、アプリケーションプログラム sinを追加します。
ルートファイルシステム上の /usr/local/bin ディレクトリに sin を配置する場合は、次のようにします。
| |
---|
mkdir コマンドを -p オプションを付けて実行すると、dir1/dir2/dir3 のような複数の階層からなるディレクトリをまとめて作成することができます。 [ATDE ~]$ mkdir -p dir1/dir2/dir3
[ATDE ~]$ ls dir1
dir2
[ATDE ~]$ ls dir1/dir2
dir3 |
8.2.2.2. Debianパッケージを追加する
Debianパッケージを追加するには、a600_resources/resources/packagesファイルにDebianパッケージの名称を追記します。
パッケージ名は1行に1つ書くことができます。
パッケージ名は、Armadilloで動作確認をする際に"apt-get install"の引数に与えた正しい名前で記載します。
例えば、libc6 の場合は
[armadillo ~]# apt-get update && apt-get install libc6
のようにしてインストールするので、次のように追記します。
|
"#" で始まる行はコメントとして認識されます。
|
|
libc6 を追記します。
|
誤ったパッケージ名を指定した場合は、ビルドログに以下のようなエラーメッセージが表示されて当該のパッケージが含まれないアーカイブが生成されてしまいます。
例えば、libc6 を追記する際に、誤って libc としてしまった場合、ビルドログに次のエラーメッセージが表示されます。
| |
---|
パッケージに依存する他のパッケージは明記しなくても、apt によって自動的にインストールされます。また、aptやdpkg等の Debian GNU/Linux の根幹となるパッケージも自動的にインストールされます。 例えば、「アプリケーションプログラムの作成」の「ライブラリとヘッダファイル」で作成した sin で利用している libm は libc6 に含まれていますが、libc6 は明記しなくてもインストールされます。[] |
| |
---|
openssh-server のような「パッケージのインストールの際に、自動的に秘密鍵を生成する」パッケージは、基本的にpackagesには追加せず、Armadillo を起動した後に "apt-get install" を使って個別にインストールしてください。 openssh-server を packages に追加した場合、構築したルートファイルシステムアーカイブを書き込んだ全ての Armadillo に、単一の公開鍵を使ってログインすることができてしまいます。もし、意図的に、複数の Armadillo で同一の秘密鍵を利用したい場合、脆弱性となり得ることを理解して適切な対策をとった上で利用してください。 |
8.2.3. ルートファイルシステムアーカイブのビルド
ルートファイルシステムアーカイブを作成するには、次のコマンドを実行します。
生成されたルートファイルシステムアーカイブは「イメージファイルの更新」で使用するので、わかりやすいように ~/images ディレクトリを作成して、その中に移動しておきます。
[ATDE ~/at-debian-builder-v1.2.0]$ mkdir -p ~/images
[ATDE ~/at-debian-builder-v1.2.0]$ ls debian-stretch*
debian-stretch-armhf-a600-20190410.tar.gz
[ATDE ~/at-debian-builder-v1.2.0]$ mv debian-stretch* ~/images
[ATDE ~/at-debian-builder-v1.2.0]$ ls ~/images
debian-stretch-armhf-a600-20190410.tar.gz
|
at-debian-builder で作成されたルートファイルシステムアーカイブのファイル名は debian-stretch-armhf-a600-作成日.tar.gz となります。
|