| 標志 | 含義 |
| GFP_KERNEL | 內核常規分配,可睡眠,可回收,最常用 |
| GFP_ATOMIC | 中斷/自旋鎖上下文,不可睡眠,不回收,易失敗 |
| GFP_USER | 用戶空間分配,可睡眠 |
| GFP_HIGHUSER | 用戶空間高端內存 |
| GFP_DMA | ZONE_DMA (ISA 設備) |
| GFP_DMA32 | ZONE_DMA32 (32位設備) |
| __GFP_HIGHMEM | 允許高端內存 |
| __GFP_WAIT / __GFP_RECLAIM | 可睡眠/可回收 |
| __GFP_IO / __GFP_FS | 可發起 I/O / 文件系統操作 |
| __GFP_NOWARN | 失敗不打印警告 |
| __GFP_NORETRY | 不重試直接失敗 |
| __GFP_ZERO | 返回零頁 |
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 列表管理頁面活躍度
內存分配選擇指南
| 場景 | 推薦 API | 關鍵點 |
| 小於頁面大小、頻繁分配 | kmalloc / kmem_cache_alloc | Slab 緩存,無頁表開銷 |
| 大於頁面、需要連續物理內存 | alloc_pages / __get_free_pages | order 盡量小,失敗率指數增長 |
| 大塊虛擬地址、物理可不連續 | vmalloc / vzalloc | 映射開銷大,TLB 压力,不可用於 DMA |
| 用戶空間映射 (mmap) | vmalloc + remap_pfn_range / vm_insert_page | 需預留 vmalloc 區地址空間 |
| DMA 緩衝區 | dma_alloc_coherent / dma_map_single | DMA API 統一管理,跨架構可移植 |
| 高端內存短期訪問 | kmap_atomic | 不可睡眠,每 CPU 槽位 |
| 高端內存長期訪問 | kmap / vmap | kmap 槽位稀缺,優先 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 管理對象,高端內存通過動態映射訪問。開發內核模塊/驅動時:
- 優先
kmalloc/Slab 分配小對象
- 大塊連續物理內存用
alloc_pages,order 盡量小
- 高端內存短期訪問用
kmap_atomic,長期用 vmap
- DMA 必須用 DMA API
- 時刻注意上下文 (可睡眠 vs 原子) 選擇正確 GFP 標志
copyright ©
萬盛學電腦網 all rights reserved