まずは肩慣らし. アラインメントについては RISC アーキテクチャでは一般的な話で,ARM に限っ たことではありません.
x86 以外の アーキテクチャでは,2バイト整数型や4バイト整数型の変数を 配置できるメモリアドレスに制限があります.
アラインメント条件を満たさないメモリアクセスが発生した場合,ARM では例 外が発生します. が,ARM Linux 上ではこの例外をカーネルで捕捉し,「うまく処理する」よう になっています.
ARM Linux では /proc/cpu/alignment
で,この例外の捕捉状況を表
示したり,処理の変更を指示することができます.
$ cat /proc/cpu/alignment
User: 0
System: 0
Skipped: 0
Half: 0
Word: 0
Multi: 0
User faults: 0 (ignored)
試しに以下のようなプログラムを実行し,x86 上の挙動と比較してみましょう.
#include <stdio.h> main ( void ) { char *str = "\x01\x23\x45\x67\x89\xab\xcd\xef"; unsigned *u = (unsigned *)(str + 1); printf ( "%08x\n", *u ); }
x86 上で実行すると以下のような結果が得られます.
$ ./a.out
89674523
ARM 上で実行すると以下のような結果が得られます.
$ ./a.out
01674523
このとき,/proc/cpu/alignment
の内容は以下のように変化しまし
た.
カーネルで例外を捕捉し,User の項目がカウントアップしていることがわかります.
$ cat /proc/cpu/alignment
User: 1
System: 0
Skipped: 0
Half: 0
Word: 0
Multi: 0
User faults: 0 (ignored)
出力結果から考えられるメモリアクセスの様子を,以下に図示します. どうしてこんな結果が得られるのか,なんとなく想像できますね.
+3 +2 +1 +0
67 45 23 01 str のダンプ
ef cd ab 89
+3 +2 +1 +0
02 01 00 x86 でのアクセス
03 (値は unsigned アクセスでのバイト位置)
+3 +2 +1 +0
02 01 00 03 ARM でのアクセス(同上)
結論から言うと
「アラインメントをまたぐアクセスをするプログラムを書かない」これに尽きます.
アラインメントをまたがないプログラムは,x86 上でも何の問題もなく動作し ます. また,x86 上であってもアラインメントをまたぐメモリアクセスは余計なメモ リアクセスが入るため,パフォーマンス上,悪影響があります.
既存のプログラムで x86 以外のアーキテクチャで動作実績の無いプログラム の場合,「アラインメントの問題があるかもしれない」と疑ってかかる必要が あるでしょう.
ARM Linux ではうまく処理すると先に書きました が,その内容をもう少し掘り下げてみます.
/proc/cpu/alignment
に数値を書き込むことにより,例外ハンドラ
の振る舞いを変更することができます.
たとえば,以下のように `2' を設定すると
# echo 2 >/proc/cpu/alignment
# cat /proc/cpu/alignment
User: 1
System: 0
Skipped: 0
Half: 0
Word: 0
Multi: 0
User faults: 2 (fixup)
先のプログラムの実行結果はは以下のように,x86 と同様になります. つまり,例外ハンドラで「x86 と同様のメモリアクセス」をエミュレートして いることになります.
$ ./a.out
89674523
/proc/cpu/alignment
に書き込む値とハンドラの動作は以下の通り
になります.
また,各値の bit OR で,複数の動作を指示することもできます.
例えば `3' を書き込むと warn と fixup の両方の処理を行います.
ignore.例外を単に無視する
warn.例外発生を dmesg に出力
fixup.例外発生の命令を解釈し,メモリアクセスのつじつまを合わせる
signal.バスエラーを発生させる.ユーザプログラムは core を吐い て終了
これで一応 x86 互換のメモリアクセスは実現できました. が,あくまでも「例外ハンドラによる補正」であることを忘れないように.
read →…
read → read →…
read →(トラップ)→エミュレート→(復帰) →…
aligned access の場合は単なる read サイクルです. が,fixup による補正処理が入った場合,ユーザ空間 → カーネル空間 → ユー ザ空間と,コンテキストスイッチが入ります. このため,単なるメモリアクセスに比べ,非常に重い処理となっています. この方法でアラインメント問題を回避しようとする場合,注意が必要です.