浅析dbus-1.2.14数据8字节下对齐后数据搬移
不知道为什么这样一个经过如此长时间严克测试的dbus,开始还以为存在bug,
其实是自己对memmove()函数不熟悉,通过malloc或者calloc申请的内存首地址存可能为0x800002,
0x800002不在8字节边界上,所以dbus对该0x800002地址进行调整,调整有2种方式,将0x800002进行8字节上对齐,
即: (0x800002) & ~0x07 = 0x800000; // 8字节内存地址上对齐
另外一种就是8字节下对齐,
即: (0x800002 + 7) & ~0x07 = 0x800008; // 8字节内存地址下对齐
很明显malloc到的首地址为0x800002我们只能进行8字节内存地址下对齐调整,这也是dbus使用的,这没有什么问题,
以为bug发生在出现内存未8字节对齐之后进行内存数据搬移函数memmove的执行上[其实没有问题].
比如
old_str = 0x800002
new_str = 0x800008
len = 原有数据长度
看看dbus是怎么将old_str中的数据搬移到new_str的,
memmove (0x800002, 0x800008, len);原以为这样不就发生数据覆盖了吗,
其实memmove()函数已经解决了覆盖问题,当发现src小于dst时,memmove()就
从&src[len]和&dst[len]结尾开始倒着拷贝[luther.gliethttp].
void *memmove(void *dst, const void *src, size_t n)
{
const char *p = src;
char *q = dst;
if (__builtin_expect(q < p, 1)) { // src 大于 dst,说明没有发生覆盖,直接从头部开始顺序拷贝即可.[luther.gliethttp]
return memcpy(dst, src, n);
} else {
// src 小于 dst,如果顺序从头开始拷贝会发生数据覆盖,所以从尾部开始倒着拷贝.
#define PRELOAD_DISTANCE 64
/* a semi-optimized memmove(). we're preloading the src and dst buffers
* as we go */
size_t c0, c1, i;
p += n;
q += n;
/* note: we preload the destination as well, because the 1-byte at a time
* copy below doesn't take advantage of the write-buffer, we need
* to use the cache instead as a poor man's write-combiner */
__builtin_prefetch(p-1);
__builtin_prefetch(q-1);
if (PRELOAD_DISTANCE > 32) {
__builtin_prefetch(p-(32+1));
__builtin_prefetch(q-(32+1));
}
/* do the prefetech as soon as possible, prevent the compiler to
* reorder the instructions above the prefetch */
asm volatile("":::"memory");
c0 = n & 0x1F; /* cache-line is 32 bytes */ // 32字节cache线边界拷贝,这样提升数据搬移速度[luther.gliethttp]
c1 = n >> 5;
while ( c1-- ) {
/* ARMv6 can have up to 3 memory access outstanding */
__builtin_prefetch(p - (PRELOAD_DISTANCE+1));
__builtin_prefetch(q - (PRELOAD_DISTANCE+1));
asm volatile("":::"memory");
for (i=0 ; i<32 ; i++) {
*--q = *--p; // 倒着拷贝32字节cache line边界数据,直到对齐
}
}
while ( c0-- ) {
*--q = *--p; // 开始大批量cache line对齐后数据搬移[luther.gliethttp]
}
}
return dst;
}
static void
fixup_alignment (DBusRealString *real)
{
unsigned char *aligned;
unsigned char *real_block;
unsigned int old_align_offset;
/* we have to have extra space in real->allocated for the align offset and nul byte */
_dbus_assert (real->len <= real->allocated - _DBUS_STRING_ALLOCATION_PADDING);
old_align_offset = real->align_offset;
real_block = real->str - old_align_offset;
aligned = _DBUS_ALIGN_ADDRESS (real_block, 8);
real->align_offset = aligned - real_block; // 到下一个8字节边界的新offset.[luther.gliethttp]
real->str = aligned; // 新地址
if (old_align_offset != real->align_offset)
{
/* Here comes the suck */
memmove (real_block + real->align_offset,
real_block + old_align_offset,
real->len + 1); // 数据搬移,memmove()函数已经内置解决了数据发生overlap覆盖拷贝问题[luther.gliethttp]
/*
The memmove() function copies n bytes from memory area src to memory area dest. The memory areas may overlap:
copying takes place as though the bytes in src are first copied into a temporary array that does not overlap src or
dest, and the bytes are then copied from the temporary array to dest.
*/
}
_dbus_assert (real->align_offset < 8);
_dbus_assert (_DBUS_ALIGN_ADDRESS (real->str, 8) == real->str);
}
void
_dbus_string_free (DBusString *str)
{
DBusRealString *real = (DBusRealString*) str;
DBUS_GENERIC_STRING_PREAMBLE (real);
if (real->constant)
return;
dbus_free (real->str - real->align_offset); // 计算出real->str通过malloc申请到的原始地址[luther.gliethttp]
real->invalid = TRUE;
}
阅读(2303) | 评论(0) | 转发(0) |