Linux記憶體管理:Vmalloc

發表於2015-09-23

在前面我們講解了kmalloc申請連續實體記憶體的操作,以及原理和基礎cache . 在核心中還有另外一個介面函式那就是vmalloc,申請一片連續的虛擬地址空間,但不保證物理空間連續,實際上我們會想到使用者空間的malloc,malloc它是標準的glibc封裝的一個函式,最終實現是通過系統呼叫brk和mmap來實現,以後在分析它的實現過程. 它就是申請連續的虛擬空間,但是不保證實體記憶體的連續,當然使用者程式也不怎麼關心這個問題,只所以會關心實體記憶體的連續性一般是由於裝置驅動的使用,或者DMA. 但是vmalloc申請效率比較低,還會造成TLB抖動. 一般核心裡常用kmalloc. 除非特殊需求,比如要獲取大塊記憶體時,例項就是當ko模組載入到核心執行時,即需要vmalloc.
釋放函式:vfree

參考核心 3.8.13

這裡是說32位的處理器,即最大定址4G虛擬空間,(當然現在已經64位比較普及了,後續補上吧)而虛擬地址到實體地址的轉化往往需要硬體的支援才能提高效率,即MMU。

當然前提需要os先建立頁表PT. 在linux核心,這4G空間並不是完全給使用者空間使用在高階0xC0000000 (3G開始)留給核心空間使用(x86預設配置,預設0-16M(DMA),16M-896M(Normal),896M-1G(128M)作為高階記憶體分配區域),當然這個區域也是可是配置的.).
kmalloc函式返回的是虛擬地址(線性地址). kmalloc特殊之處在於它分配的記憶體是物理上連續的,這對於要進行DMA的裝置十分重要. 而用vmalloc分配的記憶體只是線性地址連續,實體地址不一定連續,不能直接用於DMA。我們可以參考一個圖:(它是arm 32架構的核心虛擬地址分配圖)

下面我們就看看vmalloc函式:(mm/vmalloc.c)

這裡我們只用關注size即可,而vmalloc優先從高階記憶體分配,並且可以睡眠.
繼續:

重點看一下__vmalloc_node:

因為這裡提到了VMALLOC_START和VMALLOC_END它們究竟是什麼值呢?
這裡看了arm32和mips32的(根據架構虛擬地址分配不同而不同,比如mips就比較特殊):
在arch/mips/include/asm/pgtable-32.h中
首先看mips虛擬地址分佈圖:

從這個圖裡我們知道使用者空間為2G(0x0-0x7fff ffff),dma或者normal記憶體對映在kseg0(512M)/kseg1,而對於vmalloc申請的虛擬地址在kseg2中,當然還有其他一些特殊的對映比如io等.

在arch/arm/include/asm/pgtable.h

在看一個圖:

我們知道實體記憶體簡單分為三個區域:ZONE_NORMAL、ZONE_DMA、ZONE_HIGHMEM
vmalloc我們看到它是預設從ZONE_HIGMEM裡申請,但是這兩個函式虛擬地址是保持一致的,即都佔用了4G地址空間的核心虛擬地址.通過上面的圖,我們確定了虛擬地址從哪裡分配,以及對於的物理空間從哪裡分配。

下面看看 vmalloc核心實現:

它的基本實現思路很簡單:
1. 分配虛擬地址空間
2.對虛擬地址空間進行頁表對映

需要熟知 下面兩個結構體:
struct vmap_area

vm_struct *area :

這裡在說明一下vmalloc_init的初始化.

其實在講slab機制的時候已經說過。

下面就說說__get_vm_area_node函式:

這個函式核心就是alloc_vmap_area,這個很有趣的,之前我們講到了vmalloc申請的虛擬地址範圍,而它只傳遞了size而已,對於mips,x86,arm會有不同的虛擬空間.

既然我們已經開闢了虛擬地址空間,那麼還需要做的當然是和頁面一一對映起來.
看函式__vmalloc_area_node:

而insert_vmalloc_vmlist很明顯把vm_struct插入到vmlist。
那麼就完成了整個過程,沒有想象的複雜,當然對記憶體有了更多的認識,這裡還需要說一下,一般情況下有高階記憶體會比沒有的好些,防止了vmalloc申請的時候造成的TLB抖動等問題,更少的破壞normal空間。

可以通過proc來檢視vmalloc的一下資訊:

還有:

相關文章