摘要:物理内存布局:启动时探测:系统启动时,通过BIOS/UEFI或设备树(Device Tree)获取物理内存的总大小和布局(例如,哪些地址范围被保留给硬件设备,即“内存映射I/O”)。节点(Node)、区域(Zone):Linux将物理内存划分为不同的节点(用于
这是内存管理的基石,决定了系统如何看待和使用内存。
物理内存布局:启动时探测: 系统启动时,通过BIOS/UEFI或设备树(Device Tree)获取物理内存的总大小和布局(例如,哪些地址范围被保留给硬件设备,即“内存映射I/O”)。节点(Node)、区域(Zone): Linux将物理内存划分为不同的节点(用于NUMA架构,每个CPU有本地内存)和不同的区域(Zones)。ZONE_DMA: 用于直接内存访问(DMA)的设备,因为有些老设备只能访问16MB以下的内存。ZONE_DMA32: 用于只能访问4GB以下内存的64位设备。ZONE_NORMAL: 内核可直接映射的“普通”内存区域(在x86_64上,通常是所有内存)。ZONE_HIGHMEM: (在32位系统上)内核无法直接映射的物理内存,现已较少使用。伙伴系统(Buddy System): 使用这种算法来管理这些物理页帧(Page Frame),它以页(通常4KB)为单位,负责物理内存的分配和回收,致力于减少内存碎片。虚拟内存布局:划分地址空间: 将每个进程的4GB(32位)或128TB(64位)的虚拟地址空间划分为内核空间和用户空间。用户空间: 每个进程独有,存放其代码、堆、栈、库等。内核空间: 所有进程共享(但大部分区域受保护,用户进程无法访问),存放内核代码、数据结构、设备驱动等。进程通过系统调用陷入内核态时,才会访问内核空间。这是实现虚拟内存的核心机制,负责将“虚拟地址”翻译成“物理地址”。
页表(Page Table)结构: 一个多级树状数据结构(如x86_64上的4级页表:PGD -> P4D -> PUD -> PMD -> PT)。它逐级向下索引,最终找到对应的物理页帧号(PFN)。地址转换过程(MMU): 当CPU执行一个内存访问指令时,给出的是一个虚拟地址。内存管理单元(MMU) 硬件会自动查询当前进程的页表(通过CR3寄存器找到页表基址)来完成翻译。如果找不到有效的映射,则会产生一个页错误(Page Fault)。翻译后备缓冲区(TLB): 为了加速翻译过程,CPU内部有一个缓存,叫做TLB,用于存放最近使用过的虚拟地址到物理地址的映射。内存保护是操作系统稳定性和安全性的关键。
页表项(PTE)中的权限位:读/写(Read/Write): 控制一个页是可读还是可读写。执行(Execute): 控制一个页的代码是否可以被CPU执行(配合CPU的NX/XD功能,防止数据区代码执行,抵御病毒攻击)。用户/超级用户(User/Supervisor): 控制一个页是否可以被运行在用户态(Ring 3)的代码访问。内核空间的页通常被标记为Supervisor,阻止用户程序非法访问。实现隔离:进程隔离: 每个进程有自己独立的页表,因此进程A的虚拟地址无法访问到进程B的物理内存。这是最基本的内存保护。内核态与用户态隔离: 通过User/Supervisor位,确保用户程序无法随意访问或修改内核数据,必须通过严格的系统调用接口。这是在上述基础设施之上,为内核和应用程序提供动态获取内存的接口。
内核层:物理页分配器: 核心是伙伴系统(Buddy System),负责以页(4KB) 为单位分配和释放连续的物理内存。它解决了外部碎片问题。** slab/slub/slob 分配器:** 构建在伙伴系统之上,用于高效分配小块内存(如任务结构体task_struct、索引节点inode等内核对象)。它通过对象缓存和池化技术,极大地减少了内部碎片和分配延迟。用户层:C库内存分配器(如glibc的malloc): 这是应用程序开发者最常打交道的接口。malloc/free等函数并不是直接调用内核,而是由C库管理。系统调用接口: C库的内存分配器最终在需要更多内存时,会通过以下系统调用来向内核申请:brk / sbrk: 调整堆(heap) 的大小。mmap: 更强大的调用,可以创建匿名映射(用于大块内存分配)或文件映射,也可以在进程地址空间中任意空闲位置创建新的内存区域(如用于加载动态库)。规划布局(基础) -> 2. 建立映射(核心机制) -> 3. 施加保护(安全措施) -> 4. 提供服务(上层接口)。来源:小唐科技频道