一.什么是字节对齐,为什么要对齐?
现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定类型变量的时候经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序的一个接一个的排放,这就是对齐。
对齐的作用和原因:各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。比如有些架构的CPU在访问一个没有进行对齐的变量的时候会发生错误,那么在这种架构下编程必须保证字节对齐。其他平台可能没有这种情况,但是最常见的是如果不按照适合其平台要求对数据存放进行对齐,会在存取效率上带来损失。比如有些平台每次读都是从偶地址开始,如果一个int型(假设为32位系统)如果存放在偶地址开始的地方,那么一个读周期就可以读出这32bit,而如果存放在奇地址开始的地方,就需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该32bit数 据。显然在读取效率上下降很多。
二、看看在结构体中的对齐的例子
假设在32位的机子上面。
示例:
strucrt Test {
int a;
char b;
};
int main()
{
int size = sizeof(struct Test);
printf(“size: %d\n”, size);
}
输出的结果为:size: 8
这是因为缺省情况下会根据结构体里面最大的类型长度对齐的。当然,这样会产生内存空隙。
下面我们使用预编译指令#pragma pack (value)来告诉编译器,使用我们指定的对齐值来取代缺省的。#pragma pack() 来取消指定的对齐值。
如,在Test结构体前面加上了
#pragma pack(1)
那么得出来的值是:5
三、在linux内核中,怎样写对齐呢?
如:有下面的结构体,
struct Test {
int a;
char c;
};
但是我现在想在堆里面申请8个字节对齐的?怎么办呢?
形如:void *p = kzalloc(sizeof(struct Test), GFP_KERNEL);
上面的方式只能保证是在默认的字节对齐。
#define ALIGN_CONST 7
void *p = kzalloc((sizeof(struct Test) + ALIGN_CONST) & ~ALIGN_CONST, GET_KERNEL);
这样分配出来的内存就是以8字节对齐的了
如果想把返回的地址也是8字节的整数。嗯,好办!
p += ALIGN_CONST; //防止内存用到了别的内存
void *q = ((long)p + ALIGN_CONST) & ~ALIGN_CONST;
下面是linux内核网卡驱动的一段代码,linux 内核2.6.25
struct net_device *alloc_netdev(int sizeof_priv, const char *name,
void (*setup)(struct net_device *))
{
void *p;
struct net_device *dev;
int alloc_size;
BUG_ON(strlen(name) >= sizeof(dev->name));
/* ensure 32-byte alignment of both the device and private area */
alloc_size = (sizeof(*dev) + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST;
alloc_size += sizeof_priv + NETDEV_ALIGN_CONST;
p = kzalloc(alloc_size, GFP_KERNEL);
if (!p) {
printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");
return NULL;
}
dev = (struct net_device *)
(((long)p + NETDEV_ALIGN_CONST) & ~NETDEV_ALIGN_CONST);
dev->padded = (char *)dev - (char *)p;
if (sizeof_priv)
dev->priv = netdev_priv(dev);
setup(dev);
strcpy(dev->name, name);
return dev;
}