代码之家  ›  专栏  ›  技术社区  ›  mallwright

使用Linux内核的API可移植的数据终端转换

  •  1
  • mallwright  · 技术社区  · 8 年前

    我如何改进下面的代码,也就是说,在使用Linux内核API中的函数和宏时,在类型安全性和endianness方面使它更加健壮?例如,在下面的示例中 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_be16 cpu_to_be16p 应该在这个转换中扮演一个角色。尽管我不确定如何以安全可靠的方式使用它们。

    3 回复  |  直到 8 年前
        1
  •  -1
  •   Michael F    8 年前

    你的安全问题似乎是 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
  •   mallwright    8 年前

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

    转换有符号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;
    }
    

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

    总的来说,我对复制和转换多个值的endianness的解决方案有点不满意,因为它容易出现拼写错误,而且可以很容易地实现为宏。

        3
  •  -2
  •   Lundin    8 年前

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

    如果出于某种原因需要使Linux代码endian独立,那么可以使用:

    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 总是意味着“给我最低有效字节”,不管该字节存储在内存中的什么位置。