何时在驱动程序代码中使用stdint.h' s标量

时间:2016-09-09 18:26:28

标签: c linux driver

我注意到,在使用本机标量类型(整数,短整数,字符)或stdint提供的类型时,似乎没有一致性和最佳实践:uint32_t uint16_t uint8_t。

这让我很烦恼,因为驱动程序构成了内核的一个重要部分,需要可维护,一致,稳定和良好。

以下是gcc中的一个示例示例(用于覆盆子pi的爱好项目):

// using native scalars
struct fbinfo {
        unsigned width, height;
        unsigned vwidth, vheight;
        unsigned pitch, bits;
        int x, y;
        void *ptr;
        unsigned size;
} __attribute__((aligned(16)));

// using stdint scalars
struct fbinfo {
        uint32_t width, height;
        uint32_t vwidth, vheight;
        uint32_t pitch, bits;
        int32_t x, y;
        uint32_t ptr; // convert to void* in order to use it
        uint32_t size;
} __attribute__((aligned(16)));

对我来说,第一个例子似乎更符合逻辑,因为这段代码 只打算在覆盆子pi上运行。跑步毫无意义 这在其他硬件上。

第二个例子似乎更实用,因为它看起来更多 因为C不能保证很多关于整数的大小。 它可能是16位或其他东西。 uint32_t,uint_fast32_t和variants保证了精确或近似的大小:例如至少或最多X个字节。

操作系统开发社区倾向于使用stdint类型,而linux内核使用多种不同的技术:u32,__ u32和endian特定的东西,如__le32。

何时选择标量类型以及何时使用typedef标量类型时应考虑哪些因素?在提供的示例中使用本机标量类型或使用stdint.h是否更好?' s?

1 个答案:

答案 0 :(得分:3)

1。固定与基本类型

固定宽度类型有时难以使用。例如。 printf()的{​​{1}}说明符为int32_t,需要拆分格式字符串:

PRIi32

直接访问硬件时应该/必须使用固定宽度类型;例如在编写DMA描述符时。但是对于简单的寄存器访问,可以使用printk("foo=" PRIi32 ", bar=" PRIi32 "\n", foo, bar); writel()函数来处理基本类型。

根据经验,当假定某个内存布局时(如示例中的readl(),应使用固定宽度类型。

签名的固定宽度类型(示例中为__attribute__((__aligned__(16))))可能需要进行双重检查,无论它们的表示是否与硬件期望相符。

注意在您的示例中,由于

,第二个结构依赖于体系结构
int32_t x,y

在共同的C中编写这样的东西将是 uint32_t ptr; // convert to void* in order to use it ,在内核中通常会写

uintptr_t ptr

或者, unsigned long ptr; 可能是更好的类型。

2。 dma_addr_tuint32_t

十多年前,Linus Torvalds反对__u32,因为在这个时候,非C99编译器很常见,在(导出的)linux头文件中使用这些类型会污染名称空间。

但是现在,uint32_t和类似的类型在任何地方都可用(你不能使用非C99编译器编译内核)并且内核头导出已经显着改进,所以这些参数都消失了。

个人喜好是使用标准类型还是类型定义的变体(它们取决于框架并且在它们之间有所不同)。

3。 uint32_t和变体

它们没有在内核中使用,我会避免使用它们。它们结合了uint_fastX_t(难以使用)和uint32_t(可变宽度)的缺点。

4。 int__le32

当规范明确要求时使用endian类型(例如在网络协议实现中)。这样可以很容易地检测到错误的使用情况(例如__u32等分配。)

请勿使用它们,例如用于填充处理器结构(例如DMA描述符);一些处理器可以在小端和大端模式下运行,本机数据类型通常是编写这种信息的正确方法。