数据对齐 总结

来源:互联网 发布:vm mac共享文件夹 编辑:程序博客网 时间:2024/06/15 06:56

Author: Yang Honggang (Joseph) <eagle.rtlinux@gmail.com>
Content: Data alignment
Date: 12-08-2011
REF:
   1> Computer Systems A Programmer's Perspective (version 2)
      3.9.3 Data Alignment
   2> Linux Device Driver 3
      11.4.4. Data Alignment
=============================================================

Many computer systems place restriction on the allowable addresses for the
primitive data types, requiring that the address for some type of object
must be a multiple of some value K( 2, 4 or 8). Such alignment restrictions
simplify the design of the hardware forming the interface between the processor
and the memory system.

For example, suppose a processor always fetches 8 bytes from memory with an
address that must be a multiple of 8, then the value can be read or written
with a single memory operation. Otherwise, we may need to perform two
memory accesses, since the object might be split across two 8-byte memory blocks.

Linux follows an alignment policy where 2-byte data types (e.g., short) must
have an address that is a multiple of 2, while any larger data types (e.g.,
int, int*, float, and double) must have an address that is a multiple of 4.
Any objcet of type int, or any pointer, must be at an address having the
low-order 2 bits equal to zero.

NOTE:
    Some of SSE instructions for implementing multimedia operations will
    not work correctly with unaligned data. These instructions operate on
    16-byte blocks of data, and the instructions  that transfer data between
    the SSE unit and memory require the memory addresses to be multiples of
    16, or it will lead to an exception, with the default behavior for the
    program to terminate.
    This is the motivation behind the IA32 convetion of making sure that every
    stack frame is a multiple of 16 bytes long. The compiler can allocate
    storage within a stack frame in such a way that a block can be stored with
    a 16-byte alignment.

Liray routines that allocate memory, such as malloc, must be designed so that
they return a pointer that satisfies the worse-case alignment restriction for
the machine it is running on, typically 4 or 8.

As the structures, compiler inserts byte gaps [1], or padd to its end [2] to satisfy
the alignment requirement for every fields.

The following program 'data_align.c' gives a good example.

--------------------------------------------------

Finally, as the compiler may quietly insert padding into structures itself to ensure
that every field is aligned for good performance on the target processor. If you are
defining a structure that is intended to match a structure expected by a device, this
automatic padding may thwart your attempt. The way around this problem is to tell the
compiler that the structure must be "packed," with no fillers added. For example, the
kernel header file <linux/edd.h> defines several data structures used in interfacing
with the x86 BIOS, and it includes the following definition:

struct {
    u16 id;
    u64 lun;
    u16 reserved1;
    u32 reserved2;
} __attribute__ ((packed)) scsi;


Without the _ _attribute_ _ ((packed)), the lun field would be preceded by two filler
bytes or six if we compile the structure on a 64-bit platform.

This is also showed  in our example 'data_align.c'.


#include <stdio.h>struct S1 {int i;char c;int j;};struct S2 {int i;int j;char c;};struct All {  struct S1 s1;int a;struct S2 s2;};struct SS1 {int i;char c;int j;} __attribute__ ((packed)) ss1;int main(void){char a_char;int a_int;float a_float;double a_double;int* p;    struct SS1 ss2;struct S1 s1;struct S2 s2;struct All a;    printf("Force compiler not to leave the structure what it is\n");printf("sizeof(ss1) is %d\n", sizeof(ss1));    printf("sizeof(ss2) is %d\n", sizeof(ss2));printf("------------------------");    printf (" struct All {\n""  struct S1 s1;\n""   int a;\n""   struct S2 s2;\n");    printf("&a %p\n"   "&a.s1 %p\n"   "&a.a%p\n"   "&a.s2%p\n",   &a,   &a.s1,   &a.a,   &a.s2   );printf("sizeof(a) %d\n", sizeof(a));printf("----\a\a\a\a\a\a\a\a\a\a--------------\n");    printf("struct S1 {\n"" int i;\n"" char c;\n"" int j;\n"    " };\n");printf("&s1 %p\n"   "&s1.i %p\n"   "&s1.c%p\n"   "&s1.j%p\n",   &s1,   &s1.i,   &s1.c,   &s1.j);printf("sizeof(s1) %d\n", sizeof(s1));    printf("---------------------\n");    printf("struct S2 {\n"" int i;\n"" int j;\n"" char c;\n"    " };\n");printf("&s2 %p\n"   "&s2.i %p\n"   "&s2.c%p\n"   "&s2.j%p\n",   &s2,   &s2.i,   &s2.c,   &s2.j);printf("sizeof(s2) %d\n", sizeof(s2));   printf("---------------\n");printf("sizeof: char %d\n""int  %d\n""float%d\n""double%d\n""pointer %d\n", sizeof(char),sizeof(int),sizeof(float),sizeof(double),sizeof(p));printf("&a_char %p\n&a_int %p\n&a_float %p\n""&a_double %p\n&p %p\n", &a_char, &a_int, &a_float, &a_double, &p);return 0;}


Example 2: use standard int types

#include <stdio.h>#include <stdint.h>struct package {uint8_t u_int_8;uint64_t u_int_64;uint32_t u_int_32;uint16_t u_int_16;} a;struct pack {uint8_t u_int_8;uint64_t u_int_64;uint32_t u_int_32;uint16_t u_int_16;} __attribute__ ((packed)) A;int main(void){printf("&a%p\n"   "&a.u_int_8%p\n"   "&a.u_int_64%p\n"   "&a.u_int_32%p\n"   "&a.u_int_16%p\n",   &a,   &a.u_int_8,   &a.u_int_64,   &a.u_int_32,   &a.u_int_16);   printf("sizeof(a) is %d\n", sizeof(a));   printf("/1/7------/8+++++++/4+++/2+/2-/\n");      printf("\n--------do not do the data aliged operation -----------\n");   printf("&A%p\n"   "&A.u_int_8%p\n"   "&A.u_int_64%p\n"   "&A.u_int_32%p\n"   "&A.u_int_16%p\n",   &A,   &A.u_int_8,   &A.u_int_64,   &A.u_int_32,   &A.u_int_16);   printf("sizeof(A) is %d\n", sizeof(A));      return 0;}result:
&a0x600ab0&a.u_int_80x600ab0&a.u_int_640x600ab8&a.u_int_320x600ac0&a.u_int_160x600ac4sizeof(a) is 24/1/7------/8+++++++/4+++/2+/2-/--------do not do the data aliged operation -----------&A0x600aa0&A.u_int_80x600aa0&A.u_int_640x600aa1&A.u_int_320x600aa9&A.u_int_160x600aadsizeof(A) is 15