我注意到,在使用本机标量类型(整数,短整数,字符)或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?
答案 0 :(得分:3)
固定宽度类型有时难以使用。例如。 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;
可能是更好的类型。
dma_addr_t
与uint32_t
十多年前,Linus Torvalds反对__u32
,因为在这个时候,非C99编译器很常见,在(导出的)linux头文件中使用这些类型会污染名称空间。
但是现在,uint32_t
和类似的类型在任何地方都可用(你不能使用非C99编译器编译内核)并且内核头导出已经显着改进,所以这些参数都消失了。
个人喜好是使用标准类型还是类型定义的变体(它们取决于框架并且在它们之间有所不同)。
uint32_t
和变体它们没有在内核中使用,我会避免使用它们。它们结合了uint_fastX_t
(难以使用)和uint32_t
(可变宽度)的缺点。
int
与__le32
当规范明确要求时使用endian类型(例如在网络协议实现中)。这样可以很容易地检测到错误的使用情况(例如__u32
等分配。)
请勿使用它们,例如用于填充处理器结构(例如DMA描述符);一些处理器可以在小端和大端模式下运行,本机数据类型通常是编写这种信息的正确方法。