Linux内存管理子系统是整个内核的核心组件之一,它负责管理计算机系统中最宝贵的资源之一 - 内存。
Linux内存管理子系统由多个紧密协作的组件构成,包括物理内存管理、虚拟内存管理、页面回收、内存分配器等,它们共同构建了一个高效、灵活的内存管理框架。
包括伙伴系统、每CPU页帧缓存和内存区域
包括页表机制、TLB管理和地址空间管理
包括页面回收、交换和OOM Killer
包括Slab/Slub/Slob分配器和kmalloc
物理内存管理是Linux内核内存管理的基础,它直接处理硬件上的物理内存。
Linux内核将物理内存划分为不同的区域(Zone),以满足不同硬件和功能的需求。
伙伴系统是Linux内核中最重要的物理内存分配机制,负责分配物理页面。其核心思想是将内存分为大小为2的幂次方的连续页块,通过分裂和合并这些页块来满足内存分配需求。
struct page *__alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,
int preferred_nid, nodemask_t *nodemask)
{
/* ... */
page = get_page_from_freelist(gfp_mask, order, ...);
if (likely(page))
goto out;
page = __alloc_pages_slowpath(gfp_mask, order, ...);
/* ... */
}
为了优化频繁的页面分配,Linux内核实现了每CPU页帧缓存,它在每个CPU上缓存一定数量的页面,减少对伙伴系统的访问,提高分配效率。
经典实现,按对象大小划分缓存
简化设计,优化多核性能
适用于嵌入式系统的简化版本
void *kmalloc(size_t size, gfp_t flags)
{
struct kmem_cache *cachep;
void *ret;
/* 获取适合大小的缓存 */
cachep = kmalloc_caches[kmalloc_index(size)];
/* 从缓存分配内存 */
ret = kmem_cache_alloc_trace(cachep, flags, size);
return ret;
}
内存分配器在伙伴系统的基础上提供了更细粒度的内存分配,它们通过预分配和复用相似大小的对象来提高内存分配的效率。
虚拟内存是现代操作系统的核心特性,它提供了进程间的内存隔离,以及比物理内存更大的地址空间。
typedef struct { unsigned long pte; } pte_t;
typedef struct { unsigned long pmd; } pmd_t;
typedef struct { unsigned long pud; } pud_t;
typedef struct { unsigned long pgd; } pgd_t;
Linux使用多级页表来管理虚拟地址到物理地址的映射。在x86_64架构下,通常使用4级页表:PGD、PUD、PMD和PTE,每一级页表通过索引定位到下一级表。
static inline void flush_tlb_single(unsigned long addr)
{
/* 刷新单个TLB条目 */
__asm__ __volatile__("invlpg (%0)" ::"r" (addr) : "memory");
}
翻译后备缓冲区(TLB)是CPU中的一个硬件缓存,用于加速虚拟地址到物理地址的转换过程。当页表发生变化时,内核需要刷新TLB。
struct mm_struct {
struct vm_area_struct *mmap; /* 虚拟内存区域链表 */
struct rb_root mm_rb; /* 虚拟内存区域红黑树 */
pgd_t * pgd; /* 页全局目录 */
atomic_t mm_users; /* 用户计数 */
atomic_t mm_count; /* 引用计数 */
/* ... */
};
struct vm_area_struct {
unsigned long vm_start; /* 开始地址 */
unsigned long vm_end; /* 结束地址 */
struct vm_area_struct *vm_next; /* 链表下一个VMA */
struct rb_node vm_rb; /* 红黑树节点 */
pgprot_t vm_page_prot; /* 访问权限 */
unsigned long vm_flags; /* 标志 */
struct mm_struct *vm_mm; /* 所属mm_struct */
/* ... */
};
Linux内存管理子系统包含许多高级特性,用于提高性能和资源利用率。
维护活跃和不活跃页面列表
将脏页写回磁盘
内存严重不足时终止进程
大页通过减少页表项数量,提高TLB命中率,显著提升内存密集型应用程序的性能。
在NUMA(非统一内存访问)架构上,Linux内核考虑内存访问的局部性,尽量让进程使用与其运行CPU相近的内存节点,减少跨节点访问带来的延迟。
int do_migrate_pages(struct mm_struct *mm,
const nodemask_t *from,
const nodemask_t *to, int flags)
{
/* 在NUMA节点之间迁移页面 */
}
创建内存映射,将文件或设备映射到进程地址空间。
调整进程的堆空间,用于动态内存分配。
锁定进程地址空间中的页面,防止其被交换出内存。
向内核提供关于内存区域使用模式的建议。
SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
unsigned long, prot, unsigned long, flags,
unsigned long, fd, unsigned long, off)
{
/* ... */
return ksys_mmap_pgoff(addr, len, prot, flags, fd, off >> PAGE_SHIFT);
/* ... */
}
Linux内存管理子系统是一个复杂而精密的机制,通过多层次的抽象和优化,高效地管理系统内存资源。它的设计要点包括:
物理内存管理:伙伴系统提供了高效的页面分配
内存分配器:Slab/Slub/Slob提供了细粒度的内存分配
虚拟内存管理:多级页表和TLB优化了地址转换
页面回收:LRU算法和OOM Killer保证了内存紧张时的系统稳定性
高级特性:大页和NUMA支持提供了针对特定场景的性能优化
通过深入理解内存管理子系统的源码实现,我们可以更好地优化系统性能,开发高效的应用程序和内核模块。
探索Linux内存管理源码