访问跨越MMU页面边界的变量

17 浏览
0 Comments

访问跨越MMU页面边界的变量

我正在学习Windows下的X64汇编语言和MASM64,使用的是《64位汇编语言艺术》的最新版本。关于书中的这段引文,我有一个问题:\n有一种情况下,你需要担心内存中MMU页面的组织。有时候,方便起见,我们需要访问(读取)超出内存中数据结构末尾的数据。然而,如果这个数据结构与MMU页面的末尾对齐,访问下一个内存页面可能会有问题。内存中的某些页面是无法访问的;MMU不允许在该页面上进行读取、写入或执行操作。\n试图这样做将会产生一个x86-64通用保护(分段)故障,并终止程序的正常执行。如果你要访问的数据跨越了页面边界,并且下一个内存页面是无法访问的,那么你的程序将会崩溃。例如,考虑一个对位于MMU页面末尾的字节对象进行字访问的情况,如图3-2所示。\n一般来说,你不应该读取超出数据结构末尾的数据。如果由于某种原因你需要这样做,你应该确保可以访问下一个内存页面(可惜,在现代x86-64 CPU上没有指令可以实现这一点;确保访问合法的唯一方法是确保在你访问的数据结构之后有有效的数据)。\n所以我的问题是:假设我有这种情况,一个位于数据段末尾的字变量。如何避免异常?通过手动填充00h单元格吗?将每个变量正确对齐到其大小?如果我对齐了所有变量,如果最后一个变量是跨越了4k边界的qword,会发生什么?如何避免这种情况?MASM会自动分配另一个连续的数据段来适应它吗?

0
0 Comments

在使用x86处理器进行编程时,我们可能会遇到一个名为"Accessing a variable that crosses a MMU page boundary"的问题。这个问题的出现原因是当我们访问一个跨越MMU(内存管理单元)页面边界的变量时,可能会导致访问错误或性能下降。

这个问题通常出现在读取数组或结构体中的元素时。例如,在一个存储了RGB像素的数组中,我们可能会遇到无法直接读取3个字节的问题。因为x86处理器没有直接加载3个字节的指令,所以使用mov eax, [rcx]指令加载整个像素结构体时,实际上会加载4个字节,其中包括我们不关心的1个字节。

通常情况下,这个问题不会造成太大影响,除非变量的访问越过了一个未映射的页面边界。例如,当我们处理的缓冲区的最后一个像素正好位于一个页面的边界上,而下一个页面是未映射的时候。在这种情况下,跨越不需要的缓存线会对性能产生负面影响。所以我们需要权衡使用单个指令加载整个结构体和使用多个指令加载不同部分的性能差异。

这个问题在使用SIMD(单指令多数据)指令进行向量操作时更为常见。使用SIMD指令可以一次加载多个元素到寄存器中,然后进行批量处理。例如,使用movdqu xmm0, [rcx]可以一次加载16个字节,其中包括5个完整的像素和另一个我们不关心的像素的1个字节。

解决这个问题的方法之一是在循环数组时,在最后一次迭代中遇到这个问题时,进行特殊处理。如果数组的大小大于一个SIMD向量的大小,可以尝试进行一次部分重叠的SIMD加载,这样可以减少分支跳转。这在处理ASCII字符串时非常适用,因为即使重新处理某个字节也没有关系。但是,在对数组求和等需要避免重复计算的情况下,这种方法就不那么容易实现了。

关于这个问题,还有一些其他的解决方法。例如,AVX-512指令集提供了带有错误抑制功能的掩码加载/存储指令。这些指令可以在加载/存储时忽略掉掩码中为0的部分,即使这部分数据跨越了未映射的页面边界。但是需要注意的是,这些指令在AMD处理器上的存储版本速度较慢。

总之,"Accessing a variable that crosses a MMU page boundary"问题的出现是由于访问跨越MMU页面边界的变量所导致的。为了解决这个问题,我们可以采取一些方法,例如在循环数组时进行特殊处理或使用带有错误抑制功能的指令。这样可以避免访问错误或性能下降。

0