使用Linux内核的API

时间:2018-05-28 09:25:31

标签: linux-kernel endianness memory-alignment

如何使用Linux内核API中的函数和宏来改进以下代码,即使用类型安全性和字节序更加健壮?例如,在下面的示例中,src_data是一个由两个16位有符号整数组成的数组(通常以小端顺序存储),并以大端字节顺序通过UART发送出来。

s16 src_data[2] = {...}; /* note: this is signed data! */
u8 tx_data[4];

u8* src_data_u8 = (u8*)src_data;

tx_data[0] = src_data_u8[1];
tx_data[1] = src_data_u8[0];
tx_data[2] = src_data_u8[3];
tx_data[3] = src_data_u8[2];

我认为函数cpu_to_be16cpu_to_be16p应该在执行此转换时发挥作用。虽然我不确定如何以对字节序安全且稳健的方式使用它们。

4 个答案:

答案 0 :(得分:0)

据我了解,在将每个16位字转换成Bigendian格式后,将一个接一个地发送两个字。 我认为以下做法应该可以。

s16 src_data[2] = {...}; /* note: this is signed data! */
s16 tx_data[2];
tx_data[0] = cpu_tp_be16(src_data_u8[0]);
tx_data[1] = cpu_to_be16(src_data_u8[1]);

s16 src_data[2] = {...}; /* note: this is signed data! */
s16 tx_data[2];

tx_data[0] = cpu_tp_be16(src_data_u8[0]);
tx_data[1] = cpu_to_be16(src_data_u8[1]);

答案 1 :(得分:-1)

您的安全问题似乎是htons(x)函数/宏需要无符号整数,但您拥有一个已签名的整数。不是问题:

union {
    int16_t signed_repr;
    uint16_t unsigned_repr;
} data;

data.signed_repr = ...;

u16 unsigned_big_endian_data = htons(data.unsigned_repr);

memcpy(tx_data, &unsigned_big_endian_data,
       min(sizeof tx_data, sizeof unsigned_big_endian_data));

PS。通过工会进行的类型惩罚是perfectly well-defined

答案 2 :(得分:-1)

我相信以下是我的问题的最佳答案之一。我已经将@ 0andriy提供的链接用于内核源代码中的现有示例。

转换带符号的16位值进行传输

s16 src = -5;
u8 dst[2];
__be16 tx_buf;
*(__be16*)dst = cpu_to_be16(src);

转换多个带符号的16位值进行传输

s16 src[2] = {-5,-2};
u8 dst[4];
s16* psrc = src;
u8* pdst = dst;
int len = sizeof(src);

for ( ; len > 1; len -= 2) {
    *(__be16 *)pdst = cpu_to_be16p(psrc++);
    pdst += 2;
}

快速免责声明,我仍然需要检查此代码是否正确/编译。

总的来说,我对复制和转换多个值的字节顺序的解决方案有点不满意,因为它很容易出现拼写错误并且很容易被实现到宏中。

答案 3 :(得分:-2)

如果Linux机器总是小端,并且协议总是大端,那么代码工作正常,你不需要改变任何东西。

如果由于某种原因需要使Linux代码与字节序无关,那么您可以使用:

tx_data[0] = ((unsigned int)src_data[0] >> 8) & 0xFF;
tx_data[1] = ((unsigned int)src_data[0] >> 0) & 0xFF;
tx_data[2] = ((unsigned int)src_data[1] >> 8) & 0xFF;
tx_data[3] = ((unsigned int)src_data[1] >> 0) & 0xFF;

在那里进行强制转换以确保不对有符号类型执行右移,这将调用非可移植实现定义的行为。

与任何其他版本相比,位移的优势在于它们在硬件和字节上方的抽象级别上工作,让特定的编译器生成底层存储器访问的指令。诸如u16 >> 8之类的代码总是意味着"给我最不重要的字节"无论该字节存储在内存中的哪个位置。