萬盛學電腦網

您现在的位置: 萬盛學電腦網 >> 作業系統 >  >> Linux 教學

Linux 內存管理深度解析:地址映射、伙伴系統、Slab 分配器與高端內存

虛擬地址空間佈局 (32 位)

+----------------+ 0xFFFFFFFF (4GB)
|   內核空間      | 3GB - 4GB (1GB)
|   (內核代碼、數據、模塊、頁表、高端內存映射區) |
+----------------+ 0xC0000000 (3GB)  PAGE_OFFSET
|                |
|   用戶空間      | 0 - 3GB (3GB)
|   (用戶進程代碼、數據、堆、棧、共享庫、mmap 區) |
|                |
+----------------+ 0x00000000

頁式管理核心結構

struct page - 物理頁面描述符

  • 每個物理頁框 (4KB) 對應一個 struct page
  • 所有 struct page 組成全局數組 mem_map[]
  • 關鍵字段:flags、_count (引用計數)、mapping (address_space)、index、lru、private

三級頁表 (32 位 PAE / 64 位四級)

# 32 位無 PAE: 二級頁表
PGD (頁全局目錄) 10 位 → PTE (頁表項) 10 位 → 頁偏移 12 位

# 32 位 PAE / 64 位: 三/四級
PGD → PUD → PMD → PTE → 頁偏移

內核地址空間映射區域 (3GB-4GB)

區域虛擬地址範圍映射類型對應物理內存用途
低端內存3GB - 3GB+16MB線性映射 (__pa/__va)前 16MB (ZONE_DMA)DMA 設備、啟動早期
低端/普通內存3GB+16MB - 3GB+896MB線性映射 (__pa/__va)16MB - 896MB (ZONE_NORMAL)內核核心數據結構、頁表、Slab
高端內存3GB+896MB - 4GB動態映射 (kmap/kmap_atomic/vmap)>896MB (ZONE_HIGHMEM)大文件緩存、用戶頁面、臨時映射
vmalloc 區VMALLOC_START - VMALLOC_END非連續物理頁映射連續虛擬地址任意物理頁模塊加載、ioremap、大內存分配
永久內核映射PKMAP_BASE 下kmap (永久映射槽位有限)高端內存頁面長期訪問高端內存
臨時內核映射FIX_KMAP_BEGIN 附近kmap_atomic (每 CPU 槽位,不可睡眠)高端內存頁面短期訪問高端內存 (中斷上下文)
固定映射FIXADDR_START - FIXADDR_TOP編譯時固定虛擬地址特定物理頁ACPI、APIC、早期控制台等

頁面分配器:伙伴系統

核心 API

// 分配 2^order 個連續頁面,返回首頁 struct page*
struct page *alloc_pages(gfp_t gfp_mask, unsigned int order);
struct page *alloc_page(gfp_t gfp_mask);  // order=0

// 分配並返回線性地址 (僅低端內存)
unsigned long __get_free_pages(gfp_t gfp_mask, unsigned int order);
unsigned long __get_free_page(gfp_t gfp_mask);

// 釋放
void __free_pages(struct page *page, unsigned int order);
void free_page(unsigned long addr);
void free_pages(unsigned long addr, unsigned int order);

GFP 標志 (get_free_pages 核心參數)

MAX_ORDER = 11

最大分配 2^(11-1) = 1024 頁 = 4MB 連續物理內存。

高端內存訪問機制

kmap (永久映射)

void *kmap(struct page *page);        // 可能睡眠,佔用永久映射槽位
void kunmap(struct page *page);       // 釋放

// 使用場景:長期持有高端內存引用 (如文件映射)
struct page *page = alloc_page(GFP_HIGHUSER);
void *addr = kmap(page);
// 使用 addr...
kunmap(page);

kmap_atomic (臨時映射,高性能)

void *kmap_atomic(struct page *page);   // 不可睡眠,每 CPU 固定槽位
void kunmap_atomic(void *addr);         // 必須成對調用

// 使用場景:中斷上下文、持有自旋鎖、短期訪問
void *addr = kmap_atomic(page);
// 使用 addr...
kunmap_atomic(addr);

vmap (大塊非連續物理頁映射連續虛擬地址)

void *vmap(struct page **pages, unsigned int count, unsigned long flags, pgprot_t prot);
void vunmap(void *addr);

// vmalloc 內部使用 vmap
void *vmalloc(unsigned long size);

Slab 分配器:對象緩存層

在頁面分配器之上,針對頻繁分配/釋放同一大小對象 (inode、dentry、task_struct、sk_buff 等)。

核心概念

  • Cache (kmem_cache):特定對象類型的緩存池
  • Slab:一組連續頁面,包含多個對象
  • Object:實際分配單元
  • Partial/Full/Empty Slab:不同填充狀態的 Slab 鏈表

API

// 創建緩存
struct kmem_cache *kmem_cache_create(const char *name, size_t size, size_t align,
                                     unsigned long flags, void (*ctor)(void *));

// 分配對象
void *kmem_cache_alloc(struct kmem_cache *cachep, gfp_t flags);

// 釋放對象
void kmem_cache_free(struct kmem_cache *cachep, void *objp);

// 銷毀緩存
void kmem_cache_destroy(struct kmem_cache *cachep);

// 通用對象分配 (內建通用緩存)
void *kmalloc(size_t size, gfp_t flags);   // 尺寸向上取整到最近 kmalloc-* 緩存
void kfree(const void *objp);

// 批量分配/釋放 (高性能路徑)
void *kmalloc_array(size_t n, size_t size, gfp_t flags);
void kfree_array(void *ptr, size_t n, size_t size);

常用內建緩存

kmalloc-32, kmalloc-64, kmalloc-128, kmalloc-256, kmalloc-512,
kmalloc-1k, kmalloc-2k, kmalloc-4k, kmalloc-8k ...

Slab 狀態查看

cat /proc/slabinfo
# name                 : tunables    : slabdata   

# 圖形化查看
slabtop

頁面回收與壓縮

  • 直接回收:分配失敗時同步回收 (GFP_KERNEL)
  • 後台回收 (kswapd):水位線觸發異步回收
  • 頁面遷移/壓縮:解決外部碎片,分配高階頁面
  • LRU 列表:active/inactive anon/file 列表管理頁面活躍度

內存分配選擇指南

標志含義
GFP_KERNEL內核常規分配,可睡眠,可回收,最常用
GFP_ATOMIC中斷/自旋鎖上下文,不可睡眠,不回收,易失敗
GFP_USER用戶空間分配,可睡眠
GFP_HIGHUSER用戶空間高端內存
GFP_DMAZONE_DMA (ISA 設備)
GFP_DMA32ZONE_DMA32 (32位設備)
__GFP_HIGHMEM允許高端內存
__GFP_WAIT / __GFP_RECLAIM可睡眠/可回收
__GFP_IO / __GFP_FS可發起 I/O / 文件系統操作
__GFP_NOWARN失敗不打印警告
__GFP_NORETRY不重試直接失敗
__GFP_ZERO返回零頁
場景推薦 API關鍵點
小於頁面大小、頻繁分配kmalloc / kmem_cache_allocSlab 緩存,無頁表開銷
大於頁面、需要連續物理內存alloc_pages / __get_free_pagesorder 盡量小,失敗率指數增長
大塊虛擬地址、物理可不連續vmalloc / vzalloc映射開銷大,TLB 压力,不可用於 DMA
用戶空間映射 (mmap)vmalloc + remap_pfn_range / vm_insert_page需預留 vmalloc 區地址空間
DMA 緩衝區dma_alloc_coherent / dma_map_singleDMA API 統一管理,跨架構可移植
高端內存短期訪問kmap_atomic不可睡眠,每 CPU 槽位
高端內存長期訪問kmap / vmapkmap 槽位稀缺,優先 vmap

監控與調試

# 內存總覽
free -h
cat /proc/meminfo

# 區域詳情
cat /proc/zoneinfo

# Slab 詳情
cat /proc/slabinfo
slabtop

# 內核內存泄漏檢測
echo 1 > /proc/sys/vm/kmemleak

# 頁面類型分佈
cat /proc/pagetypeinfo

# 碎片指數
cat /proc/extfrag_index

總結

Linux 內存管理分層清晰:伙伴系統管理頁面,Slab 管理對象,高端內存通過動態映射訪問。開發內核模塊/驅動時:

  1. 優先 kmalloc/Slab 分配小對象
  2. 大塊連續物理內存用 alloc_pages,order 盡量小
  3. 高端內存短期訪問用 kmap_atomic,長期用 vmap
  4. DMA 必須用 DMA API
  5. 時刻注意上下文 (可睡眠 vs 原子) 選擇正確 GFP 標志
copyright © 萬盛學電腦網 all rights reserved