KernelNewbiesJP:

このページはWritingPortableDriversセクションの一部です。

データアラインメント

原文: DataAlignment

gccコンパイラは通常、実行速度を速くさせるために、構造体の個々のフィールドをバイト境界に合わせます。 例として、次のコードとその実行結果を見てください。

   1 #include <stdio.h>
   2 #include <stddef.h>
   3 
   4 struct foo {
   5         char  a;
   6         short b;
   7         int   c;
   8 };
   9 
  10 #define OFFSET_A  offsetof(struct foo, a)
  11 #define OFFSET_B  offsetof(struct foo, b)
  12 #define OFFSET_C  offsetof(struct foo, c)
  13 
  14 int main ()
  15 {
  16         printf ("offset A = %d\n", OFFSET_A);
  17         printf ("offset B = %d\n", OFFSET_B);
  18         printf ("offset C = %d\n", OFFSET_C);
  19         return 0;
  20 }

上記のプログラムを実行すると、次のように出力します。

offset A = 0
offset B = 2
offset C = 4

この出力は、コンパイラが構造体struct fooのフィールドbcを偶数バイト境界に合わせた ことを示しています。これは構造体のデータをメモリ上にオーバーレイするときに問題となります。一般的に、 ドライバで使用されているデータ構造は、個々のフィールドが偶数バイトとなるようにパディングしません。 そのため、gccの属性(packed)を使用し、構造体の中に「メモリホール」を作らないようにコンパイラに 指示します。

次に示すように、構造体struct fooに対してpacked属性を使用するように修正した場合、

   1 struct foo {
   2         char    a;
   3         short   b;
   4         int     c;
   5 } __attribute__((packed));

プログラムの出力は次のように変わります。

offset A = 0
offset B = 1
offset C = 3

これで構造体の中にメモリホールはなくなりました。

このpacked属性は、前述の構造体で使用したように構造体全体をパックするために使用することもできますし、 構造体の中の特定のいくつかのフィールドだけをパックすることにも使用できます。

たとえば、struct usb_ctrlrequestはinclude/usb.hの中で次のように定義されています。

   1 struct usb_ctrlrequest {
   2         __u8 bRequestType;
   3         __u8 bRequest;
   4         __le16 wValue;
   5         __le16 wIndex;
   6         __le16 wLength;
   7 } __attribute__ ((packed));

このデータ構造は、構造体全体がパックされることを保証しているので、データを直接USBコネクションに 書き込むことができます。

しかし、次に示すstruct usb_endpoint_descriptorの定義は次のようになっています。

   1 struct usb_endpoint_descriptor {
   2         __u8   bLength           __attribute__((packed));
   3         __u8   bDescriptorType   __attribute__((packed));
   4         __u8   bEndpointAddress  __attribute__((packed));
   5         __u8   bmAttributes      __attribute__((packed));
   6         __le16 wMaxPacketSize    __attribute__((packed));
   7         __u8   bInterval         __attribute__((packed));
   8         __u8   bRefresh          __attribute__((packed));
   9         __u8   bSynchAddress     __attribute__((packed));
  10         unsigned char *extra;   /* Extra descriptors */
  11         int extralen;
  12 };

このデータ構造は、構造体の最初の部分はパックされていて、USBコネクションから直接データを読み込むのに 使用できますが、extraフィールドとextralenフィールドは高速にアクセスできるように、コンパイラがバイト境界を 合わせることがあります。

KernelNewbiesJP: DataAlignment (last edited 2017-12-30 08:13:16 by localhost)