任何大于页面大小的(数组、结构、...)都必须拆分为多个页面;因此可能是“虚拟连续的,物理上不连续的”。
没有进一步的知识或限制;在其最小对齐(例如,uint32_t 数组的 4 个字节)和页面大小之间的任何内容(数组、结构、...)都有可能被拆分为多个页面;其中概率取决于其大小和对齐方式。例如,如果页面大小为 4096 字节,并且数组的最小对齐为 4 字节且大小为 4092 字节,那么 1024 中有 2 次机会最终会出现在单个页面上(并且有 99.8% 的机会会分成多个页面)。
大小等于其最小对齐的任何内容(变量、微型数组、微型结构......)都不会(不应该 - 参见注释 3)被拆分到多个页面中。
注 1:对于任何使用从堆分配的内存的东西,可以假定最小对齐是堆提供的(实现定义的)最小对齐,而不是对象本身的最小对齐。例如。对于uint16_t 的数组,最小对齐为 2 个字节;但是malloc() 将返回对齐更大的内存(可能是 16 个字节)
注意 2:当事物是嵌套的(例如,数组在一个结构中的另一个结构中)时,上述所有内容仅适用于外部结构。例如。如果你在一个结构中有一个uint16_t 数组,而该数组恰好从结构中的偏移量 4094 开始;那么数组被跨页拆分的可能性会大大增加。
注意 3:可以使用指针显式破坏最小对齐(例如,使用 malloc() 分配 1024 字节,然后创建一个指向从分配区域内您想要的任何偏移量开始的数组的指针)。
注 4:如果某些内容(数组、结构、...)被拆分为多个页面;那么它有可能在物理上仍然是连续的。在最坏的情况下,这取决于物理内存的数量(例如,如果计算机有 1 GiB 的可用物理内存和 4096 字节的页面,那么在 262000 中大约有 1 个机会,2 个虚拟连续的页面将“意外地物理上连续”)。如果操作系统实现页面/缓存着色(请参阅https://en.wikipedia.org/wiki/Cache_coloring),它会通过页面/缓存“颜色”的数量来提高“物理连续”的概率(例如,如果计算机有 1 GiB 的可用物理内存和 4096 字节页面,并且操作系统使用 256 种页面/缓存颜色,那么在 1024 中大约有 1 个机会 2 个虚拟连续的页面将“意外地物理上连续”)。
注 5:大多数现代操作系统使用多种页面大小(例如 4 KiB 页面和 2 MiB 页面,也可能还有 1 GiB 页面)。如果您假设使用了最小的页面大小,这可能会导致难以猜测页面大小实际上是多少,或者会提高“意外物理上连续”的可能性。
注 6:对于某些 CPU(例如最近的 AMD/Zen),当且仅当页表条目兼容时,TLB 的行为就好像页面更大(例如,好像您使用的是 16 KiB 页面而不是 4 KiB 页面) (例如,如果 4 个页表条目描述了具有相同权限/属性的四个物理上连续的 4 KiB 页)。如果操作系统针对这些 CPU 进行了优化,结果类似于具有额外的页面大小(4 KiB、“16 KiB”、2 MiB 和可能 1 GiB)。
在大小为一页的虚拟内存中分配数组时,它必须是一页还是可以在虚拟内存中分成两个连续的页面(例如第一个的下半部分和第二个的上半部分)?
在堆内存中分配一页大小的数组时;最小对齐将是堆管理器/malloc() 提供的实现定义的最小对齐(例如,可能是 16 个字节)。然而;当分配的内存量“足够大”时,大多数现代堆管理器会切换到使用替代方案(例如mmap() 或VirtualAlloc() 或类似);所以(取决于实现和他们对“足够大”的定义)它可能是页面对齐的。
在原始虚拟内存中分配数组时(例如,使用mmap() 或VirtualAlloc() 或您自己类似的,而不使用堆而不使用malloc() 之类的东西);页面对齐是有保证的(主要是因为虚拟内存管理器不处理任何更小的东西)。