我一直在尝试阅读内核模块的实现,而且我在这段代码上遇到了绊脚石。
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。
为什么这段代码有效?这是如何检查字节对齐的?
答案 0 :(得分:2)
在系统编程中,通常需要将存储器地址与一定数量的字节对齐 - 即,几个最低位为零。
基本上,!IS_ALIGNED(addr,1&lt;&lt; 9)检查addr是否在512字节(2 ^ 9)边界上(最后9位为零)。这是擦除闪存位置时的常见要求,因为闪存被分成大块,必须擦除或写入单个单元。
我的另一个应用程序遇到了。我正在使用具有模数功能的某个DMA控制器。基本上,这意味着您可以允许它仅更改地址的最后几位(在本例中为目标地址)。这对于保护内存不受使用DMA控制器错误的方式很有用。问题,我最初忘了告诉编译器将DMA目标缓冲区与模数值对齐。这引起了一些非常有趣的错误(随机变量与使用DMA控制器被覆盖的东西无关......有时候)。
至于&#34;宏代码是如何工作的?&#34;,如果你从一个以全零结尾的数字中减去1,你将得到一个以全部结尾的数字。例如,0b00010000 - 0b1 = 0b00001111。这是一种从整数个必需对齐字节创建二进制掩码的方法。这个掩码只有我们感兴趣的检查零值的位。在我们使用包含最低位的1的掩码和地址之后,如果最低的9(在这种情况下)位为零,则得到0(如果有的话)。
&#34;为什么需要对齐?&#34;:这归结为闪存的内部构成。擦除和写入闪存是一个不那么简单的过程,然后读取它,并且通常需要将高于逻辑电平的电压提供给存储器单元。以一字节粒度进行写入和擦除操作所需的电路将浪费大量的硅片空间,但很少使用。基本上,设计一个闪存芯片是一个统计和权衡游戏(就像工程中的任何其他东西一样),统计数据可以解决这样的问题,即分组编写和擦除可以获得最佳效果。
如果您正在阅读驱动程序和内核代码,我将告诉您,如果您正在阅读此类类型的东西,我会告诉您。熟悉本文的内容可能会有所帮助(或至少保留它作为参考):https://graphics.stanford.edu/~seander/bithacks.html