内存与资源生命周期

地址空间

页表、物理页、堆和用户内存访问分别由 arch/x86_64/mm 与 mm 子目录负责。 用户指针操作检查范围、权限、长度及目标页表,不能用当前 CR3 猜测其他任务。 初始栈、demand paging 与 COW 仍受地址空间资源限制和页面权限约束。

物理地址位宽按逻辑 CPU 缓存不可变的 CPUID 能力,包括“不支持”的结果, 避免用户缓冲区页表遍历时重复执行特征查询。该缓存不保存虚拟地址转换、 直映射覆盖或页权限;这些检查仍读取当前页表。各 CPU 的缓存独立,首次查询 不分配内存、不获取锁,早期启动和 IRQ 路径也可使用。

缓存与引用

映射和仍被引用的文件页可超出空闲页缓存预算,保持共享映射一致性。 页缓存只把成功从 FAT32、ext 或 exFAT 普通文件完整加载、且从未授予共享写权限的 空闲页视为可丢弃;没有可证明磁盘来源的标识(包括当前无标志的 ISO 文件)、 tmpfs 和其他未知内容均不进入此路径。共享文件页在首次映射写权限或 mprotect 授予写权限之前永久降级为可能已修改。私有缓存映射写入走 COW;撤销写权限不会 把可能已修改的页重新标记为 clean。目前没有普通文件 dirty 页完整写回,保留在 内存也不表示已经持久化到磁盘。

page_cache_reclaimable_pages 在缓存锁下提供瞬时页数快照;pin、put、失效与 回收使用同一锁,后续并发变化可能使快照过期。page_cache_reclaim 每次只摘除 无引用的可重读 clean 页,再在锁外归还其唯一 PMM 引用,按实际返回的数据页计数, 不调用 provider、分配堆或睡眠;为保证预算不被附带释放的元数据页超出,空的 overflow 元数据可保留复用。引用计数达到 UINT32_MAX 后永久 pinned,零引用的 额外 put 不会回绕。缓存未命中先确认可用槽,再在锁外分配和加载;发布前重检槽和 并发命中。加载失败保留原有条目;物理页耗尽时,已单独提交的 clean 回收可以在 后续加载失败后仍生效,因为真实 backing 仍可重读。

普通失效保留可能修改或来源未知的数据;文件终态销毁使用独立的 forget 操作撤销 旧 inode 的查找身份,仍被引用的旧帧在最后一次 put 后才释放。删除或替换一个名称 不等于 inode 销毁:ext 仅在最后链接的实际清理成功后 forget, 保留硬链接目标、同 inode rename 空操作及仍持有引用的对象。 成功的 ext fallocate 普通分配保留已有修改,zero/punch 在磁盘操作成功后更新缓存内相交字节、保留边缘 外的数据;即使随后 inode 元数据读取失败,该缓存更新也已完成。执行所有权使文件 fault、终态销毁与该操作串行;用户并发写仍可能与 range 操作竞态。ext provider 可能 先提交若干子事务再返回错误,且不报告已完成子范围;这类失败保留缓存内容并返回 错误,不能保证磁盘回滚或未报告子范围的缓存一致性。

解绑、关闭或异常退出不能提前释放设备仍在使用的 backing。 字符设备映射在替换固定地址前先取得独立视图及每页缓存策略所有权;这一准入 失败保留旧映射。设备页的策略由实际物理重叠关系约束,并用区间索引查找;部分 解除映射在撤销 PTE 和同步 TLB 后退休不再使用的页。一次操作移除同一视图的 多个拆分 VMA 时,最后 provider 回调仍受这些页的策略保护;其他页或 stage 的 独立存活引用不会延长无关页的策略。实际 PAT/MTRR 与硬件行为仍需平台验证。 DRM retire 与维护队列的空提示只跳过确定无工作的锁路径;真实队列操作、 fence 和引用仍受原同步保护。

内核堆

kernel_malloc 对 1–2048 字节请求使用 16、32、64、128、256、512、1024、 2048 字节的 payload 尺寸类;页内独立元数据和对齐开销单独计入页面预算, 不会减少分配给对象的可用字节。较大请求持有连续物理页。所有返回的 payload 至少 16 字节对齐,并在每次分配时清零,包括复用的 slot;零大小、溢出和页耗尽 返回空指针。释放最后一个活 slot 时,空 slab 页面返还物理分配器。

堆仅以实际持有页面的地址索引识别 slab,slot 状态由页内独立位图记录;大对象 也只按实际持有的分配地址识别。持锁阶段不调用 PMM、回收或清零:新页先在锁外 取得再发布,待释放的页先从索引摘除再在锁外返还。调用方仍须保证不访问已释放 的对象,并避免释放过期指针;原始指针无法区分地址复用前后的不同对象。

早期物理拓扑和 bootstrap

mm_topology_workspace_size 校验借用的固件地图、启动块及对象范围,计算 调用方所需的工作区;mm_topology_build 不分配内存,把规范化结果写入该 工作区,并仅在成功时发布视图。工作区须 8 字节对齐、不与输入或输出重叠, 其生命周期须覆盖所有视图消费者;地图、字符串和其他启动块只借用到构建完成。 mm_topology_destroy 只撤销视图,不释放工作区。固件块及入口必须在调用前 完整可读,地址不是对任意物理地址安全解引用的承诺。

先合并 usable 字节区间,再按 4 KiB 页向内取完整 usable 页;保留向外取整, 对冲突采用固件保留优先;EFI 最终 地图存在时优先使用其可用声明,较早的 Multiboot 保留声明仍能排除重叠页面。 EFI Loader/Boot Services、ACPI reclaim 以及 EFI runtime 内存不会被当作当前可 分配页。视图中保留 RAM 仍携带 RAM 标记;total_ram_bytes 是物理上界内经 确认的完整 RAM 页总量,包括这些保留页,不包括固件冲突页,也不是运行 PMM 的 MemTotal。physical_limit 为 min(2^physical_bits, 64 TiB),输入位宽 只接受 32..52;clipped_descriptors 记录越过该上界的固件描述符数,不对 缺失地图猜测 RAM。范围和工作区大小由描述符数量决定,稀疏高物理地址不会导致 按最大 PFN 分配元数据。

拓扑保护低 1 MiB、128–192 MiB 内核孔(覆盖当前内核、启动栈及静态页表)、 借用的启动信息/地图、模块、已知 framebuffer 与 EFI 系统表所在页。实际绑定 framebuffer 的两个物理预留区由 bootstrap 另外覆盖。调用方必须先建立现有共享 identity/direct map、完成 framebuffer 初始化且尚未启动 PMM,在 BSP 关中断的 启动上下文调用 mm_bootstrap_prepare;它先确认所有借用输入的完整范围可达, 再直接扫描原始地图与全部保留,在不分配内存的条件下合并相邻及重叠 usable 字节覆盖,从当前映射内选出无冲突的页对齐工作区,最后调用纯拓扑构建器并将 工作区标记为 bootstrap 持有。

工作区按描述符及启动对象个数为扫描事件分配两条边界,并为范围与后续分割预留 4 * (地图描述符数 + 模块数 + 10) + 16 个条目;任一容量不足都明确失败。 每次 mm_bootstrap_alloc 只取得一个可用范围的连续前缀,核对完整页串是否 满足 inclusive mask 和当前共享映射,并合并相邻的 bootstrap 所有页;它不调用 PMM、堆或回收。mm_bootstrap_finish 只关闭分配,不释放工作区或任何已分配 页;后续元数据和页表消费者必须继承这些预留。

运行时物理页分配

mm_init 在现有共享 direct map 与 framebuffer 准备完成后,以实际 CPUID 物理地址位宽准备 bootstrap 拓扑。mm_runtime_init 只接受已发布的 bootstrap 视图,在释放任何页面之前为每个已映射的可信 RAM 段预留分段位图和引用计数; 元数据成本随这些 RAM 页数与段数增长,不随稀疏空洞的最高 PFN 增长。预留可能 修改拓扑范围,计量与最终可用页播种是两个独立阶段;失败时页面不发布,已经 取得的 bootstrap 页仍保留。缺失或不可信地图不会触发猜测的备用内存范围,mm_init 会停止启动。

运行 PMM 只管理当前实际共享映射覆盖的可信 RAM。启动时的临时低地址映射用于 取得早期页表;运行时按拓扑稀疏建立直映射,而不是只保留 16 GiB 前缀。 64 TiB 是虚拟窗口的设计上界,不能当作已经验证的 RAM 容量。 mm_phys_is_ram 从冻结的可信拓扑查询完整字节范围,包含保留及当前尚未 映射的可信 RAM,且不建立映射或转移引用;mm_page_refcount 快照不获取引用。调用方持有引用时才可 retain,计数 饱和后页面保持 pinned,重复 free 不会增加空闲容量。普通分配优先使用 4 GiB 以上的高地址并向低地址寻找连续页面;DMA 限制检查分配串最后一个字节。持锁 阶段不分配、睡眠或回收,页面声明成功后在锁外清零再返回。页表和 PMM 元数据 占用实际 RAM;具体容量、启动拓扑和性能须在目标环境分别验证。

用户地址空间的丢弃和锁定

MADV_DONTNEED 按页处理现有用户映射:私有匿名页(包括 fork 后的 COW 副本) 重新访问时从零页开始;文件页重新从原文件或缓存取得。共享匿名页由 MM 私有的 RAM backing 对象保持,fork 的不同地址空间持有同一对象;SysV 共享页由原 segment 持有,tmpfs 由 inode 持有。丢弃只撤销调用地址空间的 PTE,仍有其他引用的共享数据 不会因此消失。跨空洞的范围仍处理存在的映射并返回 ENOMEM;锁定页及设备映射 返回 EINVAL。每批 PTE 撤销后先同步远端用户 TLB,再释放该批 PTE 的物理页或 缓存引用。MADV_FREE 尚未实现,返回 EOPNOTSUPP,不会被当作立即清零。

mincore 同时考虑有 backing 的普通和 PROT_NONE PTE,以及仍驻留的文件缓存、 tmpfs、SysV 和共享匿名 backing;没有 PTE 不等同于文件或共享 backing 不在 RAM。 返回值是瞬时快照,不固定后续驻留状态。共享匿名页按对象页号使用固定深度基数树; 每个 MM 的虚址范围和每个对象的 backing 范围使用平衡区间索引。部分移除只检查 被移除范围的已驻留页及其幸存引用,不逐页扫描全局 MM 注册表。

mlock 按 Linux 规则覆盖未对齐端点所在的整页;部分范围按 VMA 切分并保留 文件、设备和 SysV backing 引用;不允许拆分的设备映射拒绝部分锁定或解锁。 普通锁定在成功前预故障,ONFAULT 延迟分配但仍占用完整范围的 RLIMIT_MEMLOCK 配额;有效 CAP_IPC_LOCK 可越过配额。 MCL_CURRENT 包含原始隐式栈,MCL_FUTURE 随共享 MM 应用于新映射、brk 和 栈增长;已显式解锁的既有栈页重新 fault 不会自动再次锁定。独立 fork 不继承锁, 共享 MM 线程共享锁定状态,包含不同线程栈的 peer 对原始栈的重访;exec 的新地址 空间从空策略开始。MCL_CURRENT、MCL_FUTURE 新映射和已锁映射的 mremap 在配额及元数据准入后尽力预故障,不可访问的 guard 或物理页分配失败不伪造驻留页。 普通 mlock 的预故障失败仍会返回错误。 预故障按最多 32 次 VMA 槽位检查或页处理让出外层执行事务,重新取得事务后重新 解析 MM 身份和映射;不可访问的 VMA 整段跳过,单个范围分配或后端失败后继续检查 其他独立映射。最佳努力遍历按槽位而非虚址排序;同一 MM 的映射元数据或已驻留 PTE 被撤销时重新检查,其他 MM 的活动不会重置进度。普通 mlock 先检查全部页, 再重新核对配额、覆盖范围和切分资源,最后一次发布锁策略;失败不会改变已有锁。 预故障期间的私有临时范围不占锁配额;换出候选检查会避开这些范围。 批次之间可处理本 CPU 中断并允许其他 CPU 获得执行事务;当前内核不能在该 Ring 0 调用中切换本 CPU 到其他用户任务,因此这不保证单 CPU 的任务调度延迟。

锁策略和共享匿名 backing 归属以物理 CR3 根及私有 generation 标识;销毁根时先 撤销该身份,避免根帧复用后继承旧状态。generation 可供后续 MM 服务使用,不改变 公开 task 或 address_space 布局。内存压力路径可回收可证明干净、可重读的 缓存页,并将满足独占条件的私有匿名页写入已启用的 swap;其条件、恢复及 OOM 边界见 Swap。并非所有 backing 都可换出,换页与硬件能力 也需要通过目标环境的实际运行结果分别确认。