我一直在尝试阅读内核模块的实现,并且我在这段代码上绊脚石。
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控制器被覆盖的事物无关的随机变量有时...)。
至于"宏代码如何工作?",如果您从以全零结尾的数字中减去1,则将得到以全零结尾的数字。例如,0b00010000-0b1 = 0b00001111。这是从整数个所需对齐字节创建二进制掩码的方法。该掩码仅在我们感兴趣的零值检查位中包含一个。在我们将地址与带有最低位的1的掩码的地址进行"与"运算后,只有在最低9位(在这种情况下)为零的情况下,我们才能得到0。
"为什么需要对齐?":这归结为闪存的内部组成。擦除和写入闪存比读取闪存要简单得多,并且通常需要将高于逻辑电平的电压提供给存储单元。使写入和擦除操作以一个字节的粒度成为可能所需的电路将浪费大量的硅空间,而这种硅空间很少使用。基本上,设计闪存芯片是一种统计和权衡的游戏(就像工程学中的其他任何事物一样),并且统计数据的计算结果使得成组写入和擦除能带来最大的收益。
不收费,我将告诉您,如果您正在阅读驱动程序和内核代码,您将会看到很多这种类型的事情。熟悉本文的内容(或至少保留作为参考)可能会有所帮助:https://graphics.stanford.edu/~seander/bithacks.html
是的,内核模块正在用于为HDD创建写回SSD缓存。 因此,该应用程序使用闪存。 我了解必须在两次写入之间擦除闪存。 您能否详细说明为什么需要确保最后几位为零?
抱歉,我知道为什么内存地址的后9个条目必须为零,以便使内存地址在512字节边界上对齐。 您是否知道块或页面大小是512字节?
错误消息中使用的术语"行业对齐"。 我一直忘记块,扇区和页面之间的定义差异。 刚刚查找时,页面是最小的WRITE单元。 扇区或块是最小的ERASE单位。
我懂了。 非常感谢你
ah,请参见上面的评论编辑。
@ user6337看到补充答案。 我链接的文章应该会有所帮助。
感谢您提供其他信息和链接