linux 内核中的 IS_ALIGNED 宏有什么作用?
What does the IS_ALIGNED macro in the linux kernel do?
我一直在尝试阅读内核模块的实现,但在这段代码上遇到了困难。
unsigned long addr = (unsigned long) buf;
if (!IS_ALIGNED(addr, 1 << 9)) {
DMCRIT("@%s in %s is not sector-aligned. I/O buffer must be sector-aligned.", name, caller);
BUG();
}
IS_ALIGNED宏在内核源码中定义如下:
#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
我知道数据必须按照数据类型的大小对齐才能工作,但我仍然不明白代码的作用。
将 1 左移 9,然后减 1,得到 111111111。然后 111111111 对 x 进行按位与运算。
为什么这段代码有效?这是如何检查字节对齐的?
在系统编程中,通常需要将内存地址与一定数量的字节对齐——也就是说,几个最低位为零。
基本上,!IS_ALIGNED(addr, 1 << 9) 检查 addr 是否在 512 字节 (2^9) 边界上(最后 9 位为零)。这是擦除闪存位置时的常见要求,因为闪存被分成大块,必须作为一个单元擦除或写入。
另一个应用这个我运行进了。我正在使用某个具有模数功能的 DMA 控制器。基本上,这意味着您可以允许它仅更改地址的最后几位(在本例中为目标地址)。这对于防止内存在使用 DMA 控制器时出现错误很有用。问题来了,我最初忘记告诉编译器将 DMA 目标缓冲区与模值对齐。这导致了一些非常有趣的错误(运行与使用 DMA 控制器的东西无关的 dom 变量被覆盖......有时)。
就"how does the macro code work?"而言,如果您从一个以全零结尾的数字中减去 1,您将得到一个以全 1 结尾的数字。例如,0b00010000 - 0b1 = 0b00001111。这是一种从整数个所需对齐字节创建二进制掩码的方法。该掩码仅在我们有兴趣检查零值的位中有 1。在我们将地址与最低位包含 1 的掩码进行 AND 运算之后,只有当最低 9 位(在这种情况下)为零时,我们才能得到 0(如果有的话)。
"Why does it need to be aligned?":这归结为闪存的内部构造。擦除和写入闪存的过程远没有读取闪存那么简单,通常需要向存储单元提供高于逻辑电平的电压。以单字节 g运行 粒度使写入和擦除操作成为可能所需的电路会浪费大量很少使用的硅空间。基本上,闪存芯片的设计是一种统计和权衡游戏(就像工程中的其他任何事情一样),统计结果表明,成组写入和擦除最划算。
我会免费告诉您,如果您正在阅读驱动程序和内核代码,您将会看到很多此类内容。熟悉本文的内容(或至少保留它作为参考)可能会对您有所帮助:https://graphics.stanford.edu/~seander/bithacks.html
我一直在尝试阅读内核模块的实现,但在这段代码上遇到了困难。
unsigned long addr = (unsigned long) buf;
if (!IS_ALIGNED(addr, 1 << 9)) {
DMCRIT("@%s in %s is not sector-aligned. I/O buffer must be sector-aligned.", name, caller);
BUG();
}
IS_ALIGNED宏在内核源码中定义如下:
#define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0)
我知道数据必须按照数据类型的大小对齐才能工作,但我仍然不明白代码的作用。
将 1 左移 9,然后减 1,得到 111111111。然后 111111111 对 x 进行按位与运算。
为什么这段代码有效?这是如何检查字节对齐的?
在系统编程中,通常需要将内存地址与一定数量的字节对齐——也就是说,几个最低位为零。
基本上,!IS_ALIGNED(addr, 1 << 9) 检查 addr 是否在 512 字节 (2^9) 边界上(最后 9 位为零)。这是擦除闪存位置时的常见要求,因为闪存被分成大块,必须作为一个单元擦除或写入。
另一个应用这个我运行进了。我正在使用某个具有模数功能的 DMA 控制器。基本上,这意味着您可以允许它仅更改地址的最后几位(在本例中为目标地址)。这对于防止内存在使用 DMA 控制器时出现错误很有用。问题来了,我最初忘记告诉编译器将 DMA 目标缓冲区与模值对齐。这导致了一些非常有趣的错误(运行与使用 DMA 控制器的东西无关的 dom 变量被覆盖......有时)。
就"how does the macro code work?"而言,如果您从一个以全零结尾的数字中减去 1,您将得到一个以全 1 结尾的数字。例如,0b00010000 - 0b1 = 0b00001111。这是一种从整数个所需对齐字节创建二进制掩码的方法。该掩码仅在我们有兴趣检查零值的位中有 1。在我们将地址与最低位包含 1 的掩码进行 AND 运算之后,只有当最低 9 位(在这种情况下)为零时,我们才能得到 0(如果有的话)。
"Why does it need to be aligned?":这归结为闪存的内部构造。擦除和写入闪存的过程远没有读取闪存那么简单,通常需要向存储单元提供高于逻辑电平的电压。以单字节 g运行 粒度使写入和擦除操作成为可能所需的电路会浪费大量很少使用的硅空间。基本上,闪存芯片的设计是一种统计和权衡游戏(就像工程中的其他任何事情一样),统计结果表明,成组写入和擦除最划算。
我会免费告诉您,如果您正在阅读驱动程序和内核代码,您将会看到很多此类内容。熟悉本文的内容(或至少保留它作为参考)可能会对您有所帮助:https://graphics.stanford.edu/~seander/bithacks.html