vb.net 怎么将access的数据显示到chart中_数据的自然对齐

369fca9e31d02f08f67302ba8646ed87.png

所谓自然对齐(Natural Alignmnet),是指从一个地址读N个字节,而这个地址可以被N整除(addr % N == 0)。比如在一个以4字节为boundary的系统里,读取地址0x1008开始的4个字节就是对齐的,而读取0x1009开始的4个字节就不是。

21a4c7cb073883db46fbd8e62b8c5313.png

对于非自然对齐的数据访问,不同架构的处理器会有不同的应对策略:

  • 有的会透明地处理掉(比如x86架构),软件感觉不到,但是会影响性能(performance penalty)。
  • 有的不会报告硬件异常,但继续运行可能出现问题。
  • 有的虽然会报告异常,但提供的信息不充分,可能看不出是非对齐的访问造成的。

会填坑的编译器

那什么情况下会出现非对齐的访问呢?来看下面这样一个结构体:

struct foo {
	 uint8	field1;
	 uint16 field2;
	 uint8	field3;
};

对结构体中"field2"的访问就是非对齐的,我们平时代码中会看到不少这样的结构体,但是好像并没有出现过什么问题?这是因为编译器能够识别这种非对齐的情况,自动地加上padding,比如这个结构体的地址是0x1000,那么field2的地址不会是0x1001,而是0x1002,至多就是浪费点存储空间而已。

struct foo {
	 uint8	field1;
         #padding
	 uint16 field2;
	 uint8	field3;
         #padding
}

如果用"sizeof(struct foo)"看一下,得出的大小将是6个字节。

如果我们手动地调整一下结构体中元素的顺序,像这样:

struct foo {
	uint16 field2;
	uint8 field1;
	uint8 field3;
};

那么此时结构体中所有元素的访问都是对齐的,编译器不需要添加padding了。再用"sizeof()"看一下,得出的大小将是4个字节。

结构体reorder

可是这里有一个问题,举个I2C总线的例子,对I2C设备的访问依次是给出设备的地址、设备内寄存器的地址、寄存器的值,用结构体"foo"来封装的话,那么它们将分别对应"u8"的"field1","u16"的"field2"和"u8"的"field3"。如果调整了"field1"和"field2"的顺序,存储空间是节省了,但结构体表达的语义就不那么清晰了,程序的可读性就变差了。

针对这种情况,要是编译器可以在编译的时候,对结构体内的元素做reorder操作,自动地调换"field1"和"field2"的顺序,岂不是既可以节约空间,又不影响代码的可读性?

编译器说:我没问题啊,小菜一碟嘛。可惜,C语言并不允许编译器这样做。之所以做出这种看似「自废武功」的限制也是有道理的,因为有一些结构体所表达的对象,其在内存中的分布顺序是有「讲究」的。

最典型的例子就是一些和网络协议相关的结构体,因为协议是通信双方约定好的,每个字节代表什么都是事先确定的,如果一方在发送的时候做出reorder,另一方在接收之后就无法正确解析了。

紧凑型分布

面对网络协议,不光reorder不行,编译器的padding也不行,把内存中加了padding的数据包直接发出去,也会让对方「凌乱」的。但是编译器咋知道你这个结构体是用于网络通信的呢。没办法,只有在定义结构体的时候,显示地加上"__attribute__((packed))"。

这个"attribute"传递给编译器的意思就是,不要加padding,让结构体中的元素紧紧地挨在一起。还是上面那个例子,加上这个"attribute"之后,再用"sizeof()"看一下,得出的大小将是4个字节。

struct foo {
	 uint8	field1;
	 uint16 field2;
	 uint8	field3;
}__attribute__((packed));

可是,不加padding就是非对齐访问啊,不会触发硬件异常吗?作为很「懂」硬件的编译器,它会自信地告诉你:不会。因为它的家族里有面向不同处理器架构的成员,比如针对ARM的GCC版本,它就很清楚ARM在非对齐访问方面的限制,所以它会通过添加额外的指令,来避免非对齐访问的出现。

指针强制转换

看来好像不管怎么折腾,聪明的编译器都能给你「找补」回来,所以编程人员其实完全不用考虑非自然对齐的问题?非也,来看下面这个例子:

void func(u8 *data, u32 value)
{
	...
	*((u32 *) data) = cpu_to_le32(value);
	...
}

这里有一个指针的强制转换,假设"data"指针里存储的值是0x1001,那么将它强制转换为指向"u32"数据类型的指针后,接下来的访问就是以4字节为单位的,发生在"0x1001"这个地址处,就是非对齐的。

这种场景已经超过了编译器的「智力」范围,此时要想避免非对齐访问的发生,需要显示地加上get_unaligned()或者put_unaligned(),像这样:

value = cpu_to_le32(value);
put_unaligned(value, (u32 *) data);

DMA的非对齐

还有一种情况是在TCP/IP包的传输中,因为MAC header是14个字节,如果在接收的时候不作任何处理,那么在软件解析IP header的时候就不是自然对齐的。

88574f1a9028435a00bf53af24c5ba73.png

一个解决办法就是把接收的目标地址右移2个字节,设计为"4n+2"的形式:

f9797963ed38e3847ededaa6d6859ae0.png

在Linux中其对应的实现是使用skb_reserve():

skb_reserve(skb, NET_IP_ALIGN); 

其中"NET_IP_ALIGN"的默认值为2(定义在include/linux/skbuff.h)。

但是由于接收网络数据包通常采用DMA的形式,这又会造成的DMA地址的unalignment,在某些架构上,DMA unalignment的损害甚至会超过IP header对齐带来的性能增益。两害相权取其轻,PowerPC和x86目前都将"NET_IP_ALIGN"的值设为0,也就是不进行DMA目标地址的偏移。

参考:

内核文档 Documentation/unaligned-memory-access.txt

Struct Reordering by compiler

On the alignment of IP packets

原创文章,转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值