大家应该都知道计算机中间都有字节对齐问题。CPU访问内存的时候,如果从特定的地址开始访问一般可以加快速度,比如在32位机器上,如果一个32位的整数被放在能被32模除等于0的地址上,只需要访问一次,而如果不在,可能要访问两次。但是这样就要求一些数据从特定的地址开始,而不是顺序排放(中间会有一些空余的地址),这就是字节对齐。
而ACE CDR的估计也是为了加快速度,从而在CDR编码上默认也使用了字节对齐。所以在ACE的CDR编解码过程中,传入的参数地址最好是能符合字节对齐规则,否则可能会编解码错误。
ACE_OutputCDR构造函数会调用一个函数mb_align调整传入的地址参数成为地址对齐地址。但是其的调整函数ACE_ptr_align_binary不知处于什么考虑,不是按照机器的对齐长度而是采用的 ACE_CDR::MAX_ALIGNMENT(64bit,长度为8BYTPES)作为参数地址。那么ACE_OutputCDR的内部地址是按照8字节作为对齐的,但是ACE_InputCDR却没有将内部地址调整为模除64等于0的地址上,而只是调整为模除32(在32位机器上)等于0的地址。
void
ACE_CDR::mb_align (ACE_Message_Block *mb)
{
#if !defined (ACE_CDR_IGNORE_ALIGNMENT)
//如果使用字节对齐方式,使用最大的对齐方式调整内存。调整为模除64等于0的地址上。
char * const start = ACE_ptr_align_binary (mb->base (),
ACE_CDR::MAX_ALIGNMENT);
#else
……
}
使用一段简单的代码可以测试发现这个问题。
char *tmp_buffer = new char [2048];
//使用一个无法对齐的参数作为ACE_InputCDR,ACE_OutputCDR的参数地址,
char *tmp_data = tmp_buffer +1;
// output_cdr调整了对齐的起始地址为8字节的默认
ACE_OutputCDR output_cdr(tmp_data,512);
ACE_InputCDR input_cdr(tmp_data,512);
ACE_CDR::ULong cdr_long = 123;
bool bret =false;
//
bret = output_cdr.write_ulong(cdr_long);
// cdr_long 不等于123,而是一个错误无效数据。
bret = input_cdr.read_ulong(cdr_long);
其实如果编解码的BUFF都采用相同的对齐方式,那么理论上也不应该出现问题,最多是出现为了对齐而进行填补的空隙,但是这样能带来CPU的效率提升,也是好事。但是由于ACE_OutputCDR的一个地址调整。却可能导致编解码的BUFFER不一致,我不能肯定这到底是一个错误还是作者有他自己的考虑。
这个问题到5.6.1还存在。我已经提交了问题报告。
当然有一个方法解决这个问题。就是定义宏ACE_CDR_IGNORE_ALIGNMENT【注】,只要定义了这个宏,ACE就不会使用字节对齐处理CDR编码。使用这个方法的,编码占用空间会压缩一些,但效率上可能低一点(其实未必,因为为了字节对齐还要耗费一些计算时间),
【注】ACE不知道为什么在代码中使用两个不使用字节对齐的宏,一个是在CDR_Base.h CDR_Base.cpp 文件中使用的是ACE_CDR_IGNORE_ALIGNMENT,在CDR_Stream.cpp和CDR_Stream.h文件上使用的宏ACE_LACKS_CDR_ALIGNMENT。
我一般将两个宏都定义上。