ハードウェアを意識したプログラミングの基礎(後編)

 最後にもう一つ別のARMのCPUボードで試してみます.今度使用するボードはArmadillo-500注2です.CPUには Freescale Semiconductor社の i.MX31を使用しています.i.MX31はARMv6アーキテクチャを採用しています.Armadillo-9で使用したものと同じバイナリをArmadillo-500上で実行してみましょう.

注2:以下のURLを参照. http://www.atmark-techno.com/armadillo-500

  0x78563412
  0x9A785634
  0xBC9A7856
  0xDEBC9A78

 正しく読めました.実はARMv6アーキテクチャ以降ではunaligned accessがサポートされるようになったのです 注3.このように,アークテクチャによってunaligned accessを行ったときに異なる現象が発生します.デバイス・ドライバを一つのアーキテクチャで使用されている間は問題にならないのですが,ほかのアーキテクチャに移植するときには注意が必要です.

注3:このことを初めて知ったとき,筆者は1人で叫んだ.

● 問題になりそうで,ならないもの

 それでは実際にプログラムを書いているときに問題になるのはどのような場合でしょうか.

 C言語でプログラムを書いている場合,基本的にアドレスを指定してメモリを確保することはできません.スタックに取られる変数もアドレスはもちろんのこと,malloc()のようにヒープからメモリを確保する関数も,戻り値をそのままアドレスとして使用するだけです.リスト2のように整数を入れるために確保したヒープ・アドレスがアラインされていなければ,unaligned accessを起こしてしまいます.しかし,システム・ライブラリはCPUの制約を考慮しアラインされたアドレスを返してくれるようです.

int *p;
p = malloc(sizeof(int));
*p = 1;

リスト2 ヒープ・アドレスがアラインされているか

 それでは,リスト3のコードではどうでしょうか? この場合も,コンパイラはCPUのアラインメント制約を知っているため,構造体のメンバへのアクセスがunaligned accessにならないように上手に配置してくれます.testとtest.bのアドレスも表示してみましょう(リスト4).

1: #include <stdio.h>
2:
3: struct test_t {
4:   char a;
5:   int b;
6: };
7:
8: struct test_t test = {0x10, 0x20};
9:
10: int main()
11: {
12:   int val;
13:
14:   val = test.b;
15:   printf("%08x\n", val);
16:
17:   return 0;
18: }

リスト3 コンパイラがうまくアラインメントに従って配置してくれる

1: #include <stdio.h>
2:
3: struct test_t {
4:   char a;
5:   int b;
6: };
7:
8: struct test_t test = {0x10, 0x20};
9:
10: int main()
11: {
12: int val;
13:
14:   val = test.b;
15:   printf("%08x\n", val);
16:   printf("test: %p\n", &test);
17:   printf("test.b: %p\n", &test.b);
18:
19:   return 0;
20: }

リスト4 testとtest.bのアドレスを表示

 16行目と17行目に,testのアドレスとそのメンバ変数「b」のアドレスを表示するための文を追加しました.実行すると,

  $ ./unaligned-struct-offset
  00000020
  test: 0x80495c0
  test.b: 0x80495c4

と表示されました.bのオフセットが4バイトであることが分かります(図1).つまり,aとbの間には3バイトの詰め物があることになります.

図1

図1 メモリ配置

Copyright 2008 CQ Publishing Co.,Ltd.


Webmaster@kumikomi.net