ハードウェアを意識したプログラミングの基礎(後編)
前編では,デバイス・ドライバを理解するために必要なエンディアンやI/Oアクセス,ハードウェアとソフトウェアの境界について説明した.後編では,アクセス時のアラインメントについて説明した後,実際のコードを示しながらLinuxデバイス・ドライバの例を紹介する. (編集部)
1.アクセスは境界に沿って
エンディアン,I/Oアクセス,ハードウェアの次に問題になるのが,アクセス時のアラインメントです.CPUによってはアラインメントが合っていないアクセスが起こるとエラーになったり,意図しないデータが読めたりします.最後に紹介していますが,実は,知らないと一番見つけづらいところかもしれません.
まず,「アラインメントが合っていないアクセス」が,どういうものかを定義しましょう.
ここでは,「アクセスしようとしているアドレス値をアクセス幅で割ったときに,余りが出るようなアクセス」を「アラインメントが合っていない」,または「間違ったアラインメントでのアクセス」と定義します.以降,「unaligned access」と表記します.
つまり,
0x1000に4バイトでアクセスはOK
0x1001に4バイトでアクセスはNG
です.ちなみにどのアドレス値も1バイトでアクセスする場合は余りが出ないので,バイト・アクセス時にアラインメントが合わないということは起こりません.
では実際のコードで説明します.リスト1のコードはunaligned accessの例です.
1: #include <stdio.h>
2: #include <linux/types.h>
3:
4: int main()
5: {
6: char fifo[] = {0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0};
7: __u32 val;
8: int i;
9:
10: for (i=0; i<4; i++) {
11: val = *(__u32 *)(fifo+i);
12: printf("0x%X\n", val);
13: }
14: return 0;
15: }
これを x86で実行してみます.
$ ./unaligned-x86
0x78563412
0x9A785634
0xBC9A7856
0xDEBC9A78
思った通りの値になったでしょうか? 混乱してしまった人は,エンディアンの部分を読み直してみてください.
さて,それではほかのCPUで実行してみたいと思います.まずは,x86と同じリトル・エンディアンのARMで試してみましょう.最初に使用するボードはArmadillo-9注1です.Armadillo-9のCPUはCirrus Logic社製のEP9315です.EP9315はARMv4アーキテクチャを採用しています.
注1;以下のURLを参照. http://www.atmark-techno.com/armadillo-9
$ ./unaligned-arm
0x78563412
0x12785634
0x34127856
0x56341278
読み出すアドレスが移動しても,最初の4バイトだけがグルグルと回って読めるようです.
x86やARMとはエンディアンが違うPowerPCではどうでしょう.
$ ./unaligned-powerpc
0x12345678
0x3456789A
0x56789ABC
0x789ABCDE
エンディアンの違いはありますが,意図した値になっているようです.
次は,PowerPCと同じビッグ・エンディアンのMicroBlazeです.
endian # ./unaligned-microblaze
0x12345678
0x12345678
0x12345678
0x12345678
最初の4バイトだけ読める点はArmadillo-9と同じですが,まったく同じ値として読めるようです.