何时在驱动程序代码中使用 stdint.h 的标量
When to use stdint.h's scalars in driver code
我注意到,在使用本机标量类型(整数、短整型、字符)或 stdint 提供的标量类型时似乎没有一致性或最佳实践:uint32_t uint16_t uint8_t.
这让我很烦恼,因为驱动程序是内核的重要组成部分,需要可维护、一致、稳定和良好。
这是 gcc 中的一个说明性示例(用于 raspberry 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)));
对我来说,第一个例子似乎更符合逻辑,因为这段代码
仅用于 运行 raspberry pi。 运行 毫无意义
这在其他硬件上。
第二个例子看起来更实用,因为它看起来更
描述性的,因为 C 不能保证整数的大小。
它可能是 16 位或其他。 uint32_t、uint_fast32_t 和变体保证了精确或近似的大小:例如至少或最多 X 字节。
操作系统开发社区倾向于使用 stdint 类型,而 linux 内核使用多种不同的技术:u32、__u32 和字节序特定的东西,如 __le32。
何时选择标量类型以及何时使用 typedef 的标量类型应考虑哪些因素?在提供的示例中使用本机标量类型还是使用 stdint.h 更好?
1。固定与基本类型
固定宽度的字体有时很难使用。例如。 int32_t
的 printf()
说明符是 PRIi32
并且需要拆分格式字符串:
printk("foo=" PRIi32 ", bar=" PRIi32 "\n", foo, bar);
固定宽度类型should/must直接访问硬件时使用;例如写 DMA 描述符时。但是对于简单的寄存器访问,可以使用 writel()
或 readl()
函数来处理基本类型。
根据经验,当假定某种内存布局时(如示例中的 __attribute__((__aligned__(16)))
,应使用固定宽度类型。
带符号的固定宽度类型(在您的示例中为 int32_t x,y
)可能需要仔细检查它们的表示是否符合硬件预期。
注意在您的示例中,第二个结构依赖于体系结构,因为
uint32_t ptr; // convert to void* in order to use it
在普通 C 中写这样的东西是 uintptr_t ptr
而在内核中通常写成
unsigned long ptr;
或者,dma_addr_t
可能是更好的类型。
2。 uint32_t
与 __u32
10 多年前,Linus Torvalds 反对 uint32_t
因为当时非 C99 编译器很普遍,并且在(导出)linux header 中使用此类类型会污染命名空间。
但是现在,uint32_t
和类似的类型随处可见(你不能用非C99编译器编译内核)并且内核header导出已得到显着改进,因此这些参数是走了。
使用标准类型还是 typedef 变体(依赖于框架并且它们之间存在差异)是个人喜好问题。
3。 uint_fastX_t
和变体
它们没有在内核中使用,我会避免使用它们。它们结合了 uint32_t
(难以使用)和 int
(可变宽度)的缺点。
4。 __le32
与 __u32
在规范明确要求时使用字节序类型(例如在网络协议实现中)。这使得检测错误用法变得容易(例如 endian_variable = native_variable
之类的赋值)。
不要使用它们,例如用于填充处理器结构(例如 DMA 描述符);一些处理器可以 运行 小端模式和大端模式,并且本机数据类型通常是编写此类信息的正确方法。
我注意到,在使用本机标量类型(整数、短整型、字符)或 stdint 提供的标量类型时似乎没有一致性或最佳实践:uint32_t uint16_t uint8_t.
这让我很烦恼,因为驱动程序是内核的重要组成部分,需要可维护、一致、稳定和良好。
这是 gcc 中的一个说明性示例(用于 raspberry 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)));
对我来说,第一个例子似乎更符合逻辑,因为这段代码 仅用于 运行 raspberry pi。 运行 毫无意义 这在其他硬件上。
第二个例子看起来更实用,因为它看起来更 描述性的,因为 C 不能保证整数的大小。 它可能是 16 位或其他。 uint32_t、uint_fast32_t 和变体保证了精确或近似的大小:例如至少或最多 X 字节。
操作系统开发社区倾向于使用 stdint 类型,而 linux 内核使用多种不同的技术:u32、__u32 和字节序特定的东西,如 __le32。
何时选择标量类型以及何时使用 typedef 的标量类型应考虑哪些因素?在提供的示例中使用本机标量类型还是使用 stdint.h 更好?
1。固定与基本类型
固定宽度的字体有时很难使用。例如。 int32_t
的 printf()
说明符是 PRIi32
并且需要拆分格式字符串:
printk("foo=" PRIi32 ", bar=" PRIi32 "\n", foo, bar);
固定宽度类型should/must直接访问硬件时使用;例如写 DMA 描述符时。但是对于简单的寄存器访问,可以使用 writel()
或 readl()
函数来处理基本类型。
根据经验,当假定某种内存布局时(如示例中的 __attribute__((__aligned__(16)))
,应使用固定宽度类型。
带符号的固定宽度类型(在您的示例中为 int32_t x,y
)可能需要仔细检查它们的表示是否符合硬件预期。
注意在您的示例中,第二个结构依赖于体系结构,因为
uint32_t ptr; // convert to void* in order to use it
在普通 C 中写这样的东西是 uintptr_t ptr
而在内核中通常写成
unsigned long ptr;
或者,dma_addr_t
可能是更好的类型。
2。 uint32_t
与 __u32
10 多年前,Linus Torvalds 反对 uint32_t
因为当时非 C99 编译器很普遍,并且在(导出)linux header 中使用此类类型会污染命名空间。
但是现在,uint32_t
和类似的类型随处可见(你不能用非C99编译器编译内核)并且内核header导出已得到显着改进,因此这些参数是走了。
使用标准类型还是 typedef 变体(依赖于框架并且它们之间存在差异)是个人喜好问题。
3。 uint_fastX_t
和变体
它们没有在内核中使用,我会避免使用它们。它们结合了 uint32_t
(难以使用)和 int
(可变宽度)的缺点。
4。 __le32
与 __u32
在规范明确要求时使用字节序类型(例如在网络协议实现中)。这使得检测错误用法变得容易(例如 endian_variable = native_variable
之类的赋值)。
不要使用它们,例如用于填充处理器结构(例如 DMA 描述符);一些处理器可以 运行 小端模式和大端模式,并且本机数据类型通常是编写此类信息的正确方法。