Esphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget/mm/memory-modelmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/zh_TW/mm/memory-modelmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/it_IT/mm/memory-modelmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/ja_JP/mm/memory-modelmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/ko_KR/mm/memory-modelmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget#/translations/sp_SP/mm/memory-modelmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhP/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/mm/memory-model.rsthKubhsection)}(hhh](htitle)}(h物理内存模型h]h物理内存模型}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhhhKubh paragraph)}(hXv系统中的物理内存可以用不同的方式进行寻址。最简单的情况是,物理内存从地址0开 始,跨越一个连续的范围,直到最大的地址。然而,这个范围可能包含CPU无法访问的 小孔隙。那么,在完全不同的地址可能有几个连续的范围。而且,别忘了NUMA,即不 同的内存库连接到不同的CPU。h]hXv系统中的物理内存可以用不同的方式进行寻址。最简单的情况是,物理内存从地址0开 始,跨越一个连续的范围,直到最大的地址。然而,这个范围可能包含CPU无法访问的 小孔隙。那么,在完全不同的地址可能有几个连续的范围。而且,别忘了NUMA,即不 同的内存库连接到不同的CPU。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hLinux使用两种内存模型中的一种对这种多样性进行抽象。FLATMEM和SPARSEM。每 个架构都定义了它所支持的内存模型,默认的内存模型是什么,以及是否有可能手动 覆盖该默认值。h]hLinux使用两种内存模型中的一种对这种多样性进行抽象。FLATMEM和SPARSEM。每 个架构都定义了它所支持的内存模型,默认的内存模型是什么,以及是否有可能手动 覆盖该默认值。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hs所有的内存模型都使用排列在一个或多个数组中的 `struct page` 来跟踪物理页 帧的状态。h](hC所有的内存模型都使用排列在一个或多个数组中的 }(hhhhhNhNubhtitle_reference)}(h `struct page`h]h struct page}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhubh# 来跟踪物理页 帧的状态。}(hhhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(h无论选择哪种内存模型,物理页框号(PFN)和相应的 `struct page` 之间都存 在一对一的映射关系。h](hF无论选择哪种内存模型,物理页框号(PFN)和相应的 }(hj hhhNhNubh)}(h `struct page`h]h struct page}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj ubh, 之间都存 在一对一的映射关系。}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(h每个内存模型都定义了 :c:func:`pfn_to_page` 和 :c:func:`page_to_pfn` 帮助函数,允许从PFN到 `struct page` 的转换,反之亦然。h](h每个内存模型都定义了 }(hj)hhhNhNubh)}(h:c:func:`pfn_to_page`h]hliteral)}(hj3h]h pfn_to_page()}(hj7hhhNhNubah}(h]h ](xrefcc-funceh"]h$]h&]uh1j5hj1ubah}(h]h ]h"]h$]h&]refdoc"translations/zh_CN/mm/memory-model refdomainjBreftypefunc refexplicitrefwarn reftarget pfn_to_pageuh1hhhhKhj)ubh 和 }(hj)hhhNhNubh)}(h:c:func:`page_to_pfn`h]j6)}(hj\h]h page_to_pfn()}(hj^hhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjZubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjT page_to_pfnuh1hhhhKhj)ubh 帮助函数,允许从PFN到 }(hj)hhhNhNubh)}(h `struct page`h]h struct page}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj)ubh 的转换,反之亦然。}(hj)hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(hFLATMEMh]hFLATMEM}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhK#ubh)}(hu最简单的内存模型是FLATMEM。这个模型适用于非NUMA系统的连续或大部分连续的 物理内存。h]hu最简单的内存模型是FLATMEM。这个模型适用于非NUMA系统的连续或大部分连续的 物理内存。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK%hjhhubh)}(h在FLATMEM内存模型中,有一个全局的 `mem_map` 数组来映射整个物理内存。对 于大多数架构,孔隙在 `mem_map` 数组中都有条目。与孔洞相对应的 `struct page` 对象从未被完全初始化。h](h/在FLATMEM内存模型中,有一个全局的 }(hjhhhNhNubh)}(h `mem_map`h]hmem_map}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubhH 数组来映射整个物理内存。对 于大多数架构,孔隙在 }(hjhhhNhNubh)}(h `mem_map`h]hmem_map}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh/ 数组中都有条目。与孔洞相对应的 }(hjhhhNhNubh)}(h `struct page`h]h struct page}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh" 对象从未被完全初始化。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK(hjhhubh)}(h为了分配 `mem_map` 数组,架构特定的设置代码应该调用free_area_init()函数。 然而,在调用memblock_free_all()函数之前,映射数组是不能使用的,该函数 将所有的内存交给页分配器。h](h 为了分配 }(hjhhhNhNubh)}(h `mem_map`h]hmem_map}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 数组,架构特定的设置代码应该调用free_area_init()函数。 然而,在调用memblock_free_all()函数之前,映射数组是不能使用的,该函数 将所有的内存交给页分配器。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK,hjhhubh)}(h一个架构可能会释放 `mem_map` 数组中不包括实际物理页的部分。在这种情况下,特 定架构的 :c:func:`pfn_valid` 实现应该考虑到 `mem_map` 中的孔隙。h](h一个架构可能会释放 }(hjhhhNhNubh)}(h `mem_map`h]hmem_map}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubhT 数组中不包括实际物理页的部分。在这种情况下,特 定架构的 }(hjhhhNhNubh)}(h:c:func:`pfn_valid`h]j6)}(hj4h]h pfn_valid()}(hj6hhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hj2ubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjT pfn_validuh1hhhhK0hjubh 实现应该考虑到 }(hjhhhNhNubh)}(h `mem_map`h]hmem_map}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 中的孔隙。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK0hjhhubh)}(h使用FLATMEM,PFN和 `struct page` 之间的转换是直接的。 `PFN - ARCH_PFN_OFFSET` 是 `mem_map` 数组的一个索引。h](h使用FLATMEM,PFN和 }(hjmhhhNhNubh)}(h `struct page`h]h struct page}(hjuhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjmubh 之间的转换是直接的。 }(hjmhhhNhNubh)}(h`PFN - ARCH_PFN_OFFSET`h]hPFN - ARCH_PFN_OFFSET}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjmubh 是 }(hjmhhhNhNubh)}(h `mem_map`h]hmem_map}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjmubh 数组的一个索引。}(hjmhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK3hjhhubh)}(h^`ARCH_PFN_OFFSET` 定义了物理内存起始地址不同于0的系统的第一个页框号。h](h)}(h`ARCH_PFN_OFFSET`h]hARCH_PFN_OFFSET}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubhM 定义了物理内存起始地址不同于0的系统的第一个页框号。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK6hjhhubeh}(h]flatmemah ]h"]flatmemah$]h&]uh1hhhhhhhhK#ubh)}(hhh](h)}(h SPARSEMEMh]h SPARSEMEM}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhhhK9ubh)}(hSPARSEMEM是Linux中最通用的内存模型,它是唯一支持若干高级功能的内存模型, 如物理内存的热插拔、非易失性内存设备的替代内存图和较大系统的内存图的延迟 初始化。h]hSPARSEMEM是Linux中最通用的内存模型,它是唯一支持若干高级功能的内存模型, 如物理内存的热插拔、非易失性内存设备的替代内存图和较大系统的内存图的延迟 初始化。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK;hjhhubh)}(hXDSPARSEMEM模型将物理内存显示为一个部分的集合。一个区段用mem_section结构 体表示,它包含 `section_mem_map` ,从逻辑上讲,它是一个指向 `struct page` 阵列的指针。然而,它被存储在一些其他的magic中,以帮助分区管理。区段的大小 和最大区段数是使用 `SECTION_SIZE_BITS` 和 `MAX_PHYSMEM_BITS` 常量 来指定的,这两个常量是由每个支持SPARSEMEM的架构定义的。 `MAX_PHYSMEM_BITS` 是一个架构所支持的物理地址的实际宽度,而 `SECTION_SIZE_BITS` 是一个任 意的值。h](hvSPARSEMEM模型将物理内存显示为一个部分的集合。一个区段用mem_section结构 体表示,它包含 }(hjhhhNhNubh)}(h`section_mem_map`h]hsection_mem_map}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh) ,从逻辑上讲,它是一个指向 }(hjhhhNhNubh)}(h `struct page`h]h struct page}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 阵列的指针。然而,它被存储在一些其他的magic中,以帮助分区管理。区段的大小 和最大区段数是使用 }(hjhhhNhNubh)}(h`SECTION_SIZE_BITS`h]hSECTION_SIZE_BITS}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 和 }(hjhhhNhNubh)}(h`MAX_PHYSMEM_BITS`h]hMAX_PHYSMEM_BITS}(hj2hhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubhW 常量 来指定的,这两个常量是由每个支持SPARSEMEM的架构定义的。 }(hjhhhNhNubh)}(h`MAX_PHYSMEM_BITS`h]hMAX_PHYSMEM_BITS}(hjDhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh> 是一个架构所支持的物理地址的实际宽度,而 }(hjhhhNhNubh)}(h`SECTION_SIZE_BITS`h]hSECTION_SIZE_BITS}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 是一个任 意的值。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK?hjhhubh)}(h7最大的段数表示为 `NR_MEM_SECTIONS` ,定义为h](h最大的段数表示为 }(hjnhhhNhNubh)}(h`NR_MEM_SECTIONS`h]hNR_MEM_SECTIONS}(hjvhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjnubh ,定义为}(hjnhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKGhjhhubh math_block)}(hDNR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}h]hDNR\_MEM\_SECTIONS = 2 ^ {(MAX\_PHYSMEM\_BITS - SECTION\_SIZE\_BITS)}}hjsbah}(h]h ]h"]h$]h&]docnamejNnumberNlabelNnowraphhuh1jhhhKIhjhhubh)}(h`mem_section` 对象被安排在一个叫做 `mem_sections` 的二维数组中。这个数组的 大小和位置取决于 `CONFIG_SPARSEM_EXTREME` 和可能的最大段数:h](h)}(h `mem_section`h]h mem_section}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 对象被安排在一个叫做 }(hjhhhNhNubh)}(h`mem_sections`h]h mem_sections}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh? 的二维数组中。这个数组的 大小和位置取决于 }(hjhhhNhNubh)}(h`CONFIG_SPARSEM_EXTREME`h]hCONFIG_SPARSEM_EXTREME}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 和可能的最大段数:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKMhjhhubh bullet_list)}(hhh](h list_item)}(h当 `CONFIG_SPARSEMEM_EXTREME` 被禁用时, `mem_sections` 数组是静态的,有 `NR_MEM_SECTIONS` 行。每一行持有一个 `mem_section` 对象。h]h)}(h当 `CONFIG_SPARSEMEM_EXTREME` 被禁用时, `mem_sections` 数组是静态的,有 `NR_MEM_SECTIONS` 行。每一行持有一个 `mem_section` 对象。h](h当 }(hjhhhNhNubh)}(h`CONFIG_SPARSEMEM_EXTREME`h]hCONFIG_SPARSEMEM_EXTREME}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 被禁用时, }(hjhhhNhNubh)}(h`mem_sections`h]h mem_sections}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 数组是静态的,有 }(hjhhhNhNubh)}(h`NR_MEM_SECTIONS`h]hNR_MEM_SECTIONS}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 行。每一行持有一个 }(hjhhhNhNubh)}(h `mem_section`h]h mem_section}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 对象。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKPhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(h当 `CONFIG_SPARSEMEM_EXTREME` 被启用时, `mem_sections` 数组被动态分配。 每一行包含价值 `PAGE_SIZE` 的 `mem_section` 对象,行数的计算是为了适应所有的 内存区。 h]h)}(h当 `CONFIG_SPARSEMEM_EXTREME` 被启用时, `mem_sections` 数组被动态分配。 每一行包含价值 `PAGE_SIZE` 的 `mem_section` 对象,行数的计算是为了适应所有的 内存区。h](h当 }(hjMhhhNhNubh)}(h`CONFIG_SPARSEMEM_EXTREME`h]hCONFIG_SPARSEMEM_EXTREME}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjMubh 被启用时, }(hjMhhhNhNubh)}(h`mem_sections`h]h mem_sections}(hjghhhNhNubah}(h]h ]h"]h$]h&]uh1hhjMubh0 数组被动态分配。 每一行包含价值 }(hjMhhhNhNubh)}(h `PAGE_SIZE`h]h PAGE_SIZE}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjMubh 的 }(hjMhhhNhNubh)}(h `mem_section`h]h mem_section}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjMubh> 对象,行数的计算是为了适应所有的 内存区。}(hjMhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKRhjIubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]bullet*uh1jhhhKPhjhhubh)}(hR架构设置代码应该调用sparse_init()来初始化内存区和内存映射。h]hR架构设置代码应该调用sparse_init()来初始化内存区和内存映射。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKVhjhhubhdefinition_list)}(hhh]hdefinition_list_item)}(h通过SPARSEMEM,有两种可能的方式将PFN转换为相应的 `struct page` --"classic sparse"和 "sparse vmemmap"。选择是在构建时进行的,它由 `CONFIG_SPARSEMEM_VMEMMAP` 的 值决定。 h](hterm)}(hf通过SPARSEMEM,有两种可能的方式将PFN转换为相应的 `struct page` --"classic sparse"和h](hC通过SPARSEMEM,有两种可能的方式将PFN转换为相应的 }(hjhhhNhNubh)}(h `struct page`h]h struct page}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh --“classic sparse”和}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1jhhhKZhjubh definition)}(hhh]h)}(hf"sparse vmemmap"。选择是在构建时进行的,它由 `CONFIG_SPARSEMEM_VMEMMAP` 的 值决定。h](h?“sparse vmemmap”。选择是在构建时进行的,它由 }(hjhhhNhNubh)}(h`CONFIG_SPARSEMEM_VMEMMAP`h]hCONFIG_SPARSEMEM_VMEMMAP}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 的 值决定。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKYhjubah}(h]h ]h"]h$]h&]uh1jhjubeh}(h]h ]h"]h$]h&]uh1jhhhKZhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubh)}(hClassic sparse在page->flags中编码了一个页面的段号,并使用PFN的高位来访问映射该页 框的段。在一个区段内,PFN是指向页数组的索引。h]hClassic sparse在page->flags中编码了一个页面的段号,并使用PFN的高位来访问映射该页 框的段。在一个区段内,PFN是指向页数组的索引。}(hj#hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK\hjhhubh)}(hX)Sparse vmemmapvmemmap使用虚拟映射的内存映射来优化pfn_to_page和page_to_pfn操 作。有一个全局的 `struct page *vmemmap` 指针,指向一个虚拟连续的 `struct page` 对象阵列。PFN是该数组的一个索引,`struct page` 从 `vmemmap` 的偏移量是该页的PFN。h](huSparse vmemmapvmemmap使用虚拟映射的内存映射来优化pfn_to_page和page_to_pfn操 作。有一个全局的 }(hj1hhhNhNubh)}(h`struct page *vmemmap`h]hstruct page *vmemmap}(hj9hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj1ubh& 指针,指向一个虚拟连续的 }(hj1hhhNhNubh)}(h `struct page`h]h struct page}(hjKhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj1ubh1 对象阵列。PFN是该数组的一个索引,}(hj1hhhNhNubh)}(h `struct page`h]h struct page}(hj]hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj1ubh 从 }(hj1hhhNhNubh)}(h `vmemmap`h]hvmemmap}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1hhj1ubh 的偏移量是该页的PFN。}(hj1hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhK_hjhhubh)}(hX为了使用vmemmap,一个架构必须保留一个虚拟地址的范围,以映射包含内存映射的物理页,并 确保 `vmemmap`指向该范围。此外,架构应该实现 :c:func:`vmemmap_populate` 方法, 它将分配物理内存并为虚拟内存映射创建页表。如果一个架构对vmemmap映射没有任何特殊要求, 它可以使用通用内存管理提供的默认 :c:func:`vmemmap_populate_basepages`。h](h为了使用vmemmap,一个架构必须保留一个虚拟地址的范围,以映射包含内存映射的物理页,并 确保 }(hjhhhNhNubh)}(hQ`vmemmap`指向该范围。此外,架构应该实现 :c:func:`vmemmap_populate`h]hOvmemmap`指向该范围。此外,架构应该实现 :c:func:`vmemmap_populate}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 方法, 它将分配物理内存并为虚拟内存映射创建页表。如果一个架构对vmemmap映射没有任何特殊要求, 它可以使用通用内存管理提供的默认 }(hjhhhNhNubh)}(h$:c:func:`vmemmap_populate_basepages`h]j6)}(hjh]hvmemmap_populate_basepages()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjTvmemmap_populate_basepagesuh1hhhhKchjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKchjhhubh)}(hX虚拟映射的内存映射允许将持久性内存设备的 `struct page` 对象存储在这些设备上预先分 配的存储中。这种存储用vmem_altmap结构表示,最终通过一长串的函数调用传递给 vmemmap_populate()。vmemmap_populate()实现可以使用 `vmem_altmap` 和 :c:func:`vmemmap_alloc_block_buf` 助手来分配持久性内存设备上的内存映射。h](h=虚拟映射的内存映射允许将持久性内存设备的 }(hjhhhNhNubh)}(h `struct page`h]h struct page}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 对象存储在这些设备上预先分 配的存储中。这种存储用vmem_altmap结构表示,最终通过一长串的函数调用传递给 vmemmap_populate()。vmemmap_populate()实现可以使用 }(hjhhhNhNubh)}(h `vmem_altmap`h]h vmem_altmap}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 和 }(hjhhhNhNubh)}(h!:c:func:`vmemmap_alloc_block_buf`h]j6)}(hjh]hvmemmap_alloc_block_buf()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjTvmemmap_alloc_block_bufuh1hhhhKhhjubh: 助手来分配持久性内存设备上的内存映射。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhhjhhubeh}(h] sparsememah ]h"] sparsememah$]h&]uh1hhhhhhhhK9ubh)}(hhh](h)}(h ZONE_DEVICEh]h ZONE_DEVICE}(hj*hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj'hhhhhKnubh)}(hX#`ZONE_DEVICE` 设施建立在 `SPARSEM_VMEMMAP` 之上,为设备驱动识别的物理地址范 围提供 `struct page` `mem_map` 服务。 `ZONE_DEVICE` 的 "设备" 方面与以下 事实有关:这些地址范围的页面对象从未被在线标记过,而且必须对设备进行引用,而不仅仅 是页面,以保持内存被“锁定”以便使用。 `ZONE_DEVICE` ,通过 :c:func:`devm_memremap_pages` , 为给定的pfns范围执行足够的内存热插拔来开启 :c:func:`pfn_to_page`, :c:func:`page_to_pfn`, ,和 :c:func:`get_user_pages` 服务。由于页面引 用计数永远不会低于1,所以页面永远不会被追踪为空闲内存,页面的 `struct list_head lru` 空间被重新利用,用于向映射该内存的主机设备/驱动程序进行反向引用。h](h)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hj<hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubh 设施建立在 }(hj8hhhNhNubh)}(h`SPARSEM_VMEMMAP`h]hSPARSEM_VMEMMAP}(hjNhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubh< 之上,为设备驱动识别的物理地址范 围提供 }(hj8hhhNhNubh)}(h `struct page`h]h struct page}(hj`hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubh }(hj8hhhNhNubh)}(h `mem_map`h]hmem_map}(hjrhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubh 服务。 }(hj8hhhNhNubh)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubh 的 “设备” 方面与以下 事实有关:这些地址范围的页面对象从未被在线标记过,而且必须对设备进行引用,而不仅仅 是页面,以保持内存被“锁定”以便使用。 }(hj8hhhNhNubh)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubh ,通过 }(hj8hhhNhNubh)}(h:c:func:`devm_memremap_pages`h]j6)}(hjh]hdevm_memremap_pages()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjTdevm_memremap_pagesuh1hhhhKohj8ubhC , 为给定的pfns范围执行足够的内存热插拔来开启 }(hj8hhhNhNubh)}(h:c:func:`pfn_to_page`h]j6)}(hjh]h pfn_to_page()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjT pfn_to_pageuh1hhhhKohj8ubh, }(hj8hhhNhNubh)}(h:c:func:`page_to_pfn`h]j6)}(hjh]h page_to_pfn()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjT page_to_pfnuh1hhhhKohj8ubh , ,和 }(hj8hhhNhNubh)}(h:c:func:`get_user_pages`h]j6)}(hjh]hget_user_pages()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjTget_user_pagesuh1hhhhKohj8ubhv 服务。由于页面引 用计数永远不会低于1,所以页面永远不会被追踪为空闲内存,页面的 }(hj8hhhNhNubh)}(h`struct list_head lru`h]hstruct list_head lru}(hj4hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj8ubhb 空间被重新利用,用于向映射该内存的主机设备/驱动程序进行反向引用。}(hj8hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKohj'hhubh)}(hX;虽然 `SPARSEMEM` 将内存作为一个区段的集合,可以选择收集并合成内存块,但 `ZONE_DEVICE` 用户需要更小的颗粒度来填充 `mem_map` 。鉴于 `ZONE_DEVICE` 内存从未被在线标记,因此它的内存范围从未通过sysfs内存热插拔api暴露在内存块边界 上。这个实现依赖于这种缺乏用户接口的约束,允许子段大小的内存范围被指定给 :c:func:`arch_add_memory` ,即内存热插拔的上半部分。子段支持允许2MB作为 :c:func:`devm_memremap_pages` 的跨架构通用对齐颗粒度。h](h虽然 }(hjLhhhNhNubh)}(h `SPARSEMEM`h]h SPARSEMEM}(hjThhhNhNubah}(h]h ]h"]h$]h&]uh1hhjLubhS 将内存作为一个区段的集合,可以选择收集并合成内存块,但 }(hjLhhhNhNubh)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hjfhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjLubh) 用户需要更小的颗粒度来填充 }(hjLhhhNhNubh)}(h `mem_map`h]hmem_map}(hjxhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjLubh 。鉴于 }(hjLhhhNhNubh)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjLubh 内存从未被在线标记,因此它的内存范围从未通过sysfs内存热插拔api暴露在内存块边界 上。这个实现依赖于这种缺乏用户接口的约束,允许子段大小的内存范围被指定给 }(hjLhhhNhNubh)}(h:c:func:`arch_add_memory`h]j6)}(hjh]harch_add_memory()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjTarch_add_memoryuh1hhhhKxhjLubhD ,即内存热插拔的上半部分。子段支持允许2MB作为 }(hjLhhhNhNubh)}(h:c:func:`devm_memremap_pages`h]j6)}(hjh]hdevm_memremap_pages()}(hjhhhNhNubah}(h]h ](jAjBc-funceh"]h$]h&]uh1j5hjubah}(h]h ]h"]h$]h&]refdocjN refdomainjBreftypefunc refexplicitrefwarnjTdevm_memremap_pagesuh1hhhhKxhjLubh% 的跨架构通用对齐颗粒度。}(hjLhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKxhj'hhubh)}(h`ZONE_DEVICE` 的用户是:h](h)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubh 的用户是:}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhj'hhubj)}(hhh](j)}(hLpmem: 通过DAX映射将平台持久性内存作为直接I/O目标使用。 h]h)}(hKpmem: 通过DAX映射将平台持久性内存作为直接I/O目标使用。h]hKpmem: 通过DAX映射将平台持久性内存作为直接I/O目标使用。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hhmm: 用 `->page_fault()` 和 `->page_free()` 事件回调扩展 `ZONE_DEVICE` , 以允许设备驱动程序协调与设备内存相关的内存管理事件,通常是GPU内存。参见Documentation/mm/hmm.rst。 h]h)}(hhmm: 用 `->page_fault()` 和 `->page_free()` 事件回调扩展 `ZONE_DEVICE` , 以允许设备驱动程序协调与设备内存相关的内存管理事件,通常是GPU内存。参见Documentation/mm/hmm.rst。h](h hmm: 用 }(hj#hhhNhNubh)}(h`->page_fault()`h]h->page_fault()}(hj+hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj#ubh 和 }(hj#hhhNhNubh)}(h`->page_free()`h]h ->page_free()}(hj=hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj#ubh 事件回调扩展 }(hj#hhhNhNubh)}(h `ZONE_DEVICE`h]h ZONE_DEVICE}(hjOhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj#ubh , 以允许设备驱动程序协调与设备内存相关的内存管理事件,通常是GPU内存。参见Documentation/mm/hmm.rst。}(hj#hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubj)}(hp2pdma: 创建 `struct page` 对象,允许PCI/E拓扑结构中的peer设备协调它们之间的 直接DMA操作,即绕过主机内存。h]h)}(hp2pdma: 创建 `struct page` 对象,允许PCI/E拓扑结构中的peer设备协调它们之间的 直接DMA操作,即绕过主机内存。h](hp2pdma: 创建 }(hjqhhhNhNubh)}(h `struct page`h]h struct page}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjqubhq 对象,允许PCI/E拓扑结构中的peer设备协调它们之间的 直接DMA操作,即绕过主机内存。}(hjqhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjmubah}(h]h ]h"]h$]h&]uh1jhjhhhhhNubeh}(h]h ]h"]h$]h&]jjuh1jhhhKhj'hhubeh}(h] zone-deviceah ]h"] zone_deviceah$]h&]uh1hhhhhhhhKnubeh}(h]id1ah ]h"]物理内存模型ah$]h&]uh1hhhhhhhhKubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(hN generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerjerror_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}nameids}(jjjjj$j!jju nametypes}(jjj$juh}(jhjjj!jjj'u footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jKsRparse_messages]transform_messages] transformerN include_log] decorationNhhub.