linux內核內存分配(三、虛擬內存管理)


        在分析虛擬內存管理前要先看下linux內核內存的具體分配我開始就是困在這個地方。對內核內存的分類不是非常清晰。我摘錄當中的一段:


內核內存地址

===========================================================================================================

在linux的內存管理中,用戶使用0~3GB的地址空間。而內核僅僅是用了3GB~4GB區間的地址空間。共1GB。非連
續空間的物理映射就位於3GB~4GB之間。例如以下圖示

0GB                                                          3GB                   4GB
而關於內核空間中這1GB是怎樣分配的呢,詳細請看下圖:

一般會把內核空間中大於896M的空間稱作內核空間中的高端內存。內核能夠用三種不同的機制將頁框映射到高端
內存:永久內核映射、暫時內核映射和非連續內存分配。本文中將要談論的是非連續內存分配。
       從上圖能夠知道,在物理內存的末尾和非連續內存區之間插入了一個大小為8MB的區間,這是一個安全區,
目的是“捕獲”對非連續區的非法訪問。出於相同的理由。在其他非連續區間也插入了大小為4KB的安全區。而每一個
非連續區的大小都是4KB的倍數。例如以下圖:

非連續內存的線性地址空間是從VMALLOC_START~VMALLOC_END,共128MB大小。

當內核須要用vmalloc類的函數
進行非連續內存分配時,就會申請一個vm_struct結構來描寫敘述相應的vmalloc區,若分配多個vmalloc的內存區,那
么相鄰兩個vmalloc區之間的間隔大小至少為4KB,即至少是一個頁框大小PAGE——SIZE。如上圖。

===============================================================================================================

        這里強調下:上面的圖示表示的不過虛擬地址,而實際的物理地址是分DMA和常規地址及高端地址的;

        linux內核內存大概的就是上面的圖示了。當中8MB說是為了安全。防止越界訪問(看了非常多書,都這么說),就是這8MB虛擬地址不做不論什么映射(這樣不過虛擬地址。沒有實際的物理地址浪費)

        由上面的圖示能夠知道,前面896MB(其它架構能夠能不是以896MB切割的)就是我們說的內核邏輯地址(記住是內核邏輯地址。假設就說邏輯地址的話應該是指x86架構中虛擬地址中不包含段地址部分,也就是段內偏移部分);這部分內存地址已經在系統初始化的時候和物理頁做好了映射,並且是一一映射,我們一般使用的時候就是用該部分的內存地址(kmalloc函數使用就是該部分)。這段內存是很高效的,由於不須要做其它的映射和改動頁表就能夠直接使用。本blog是分析下虛擬內存地址的映射,主要是vmalloc函數和ioremap函數;


vmalloc函數

        vmalloc函數是驅動模塊常常使用的內存分配函數。該函數返回的虛擬地址連續的(事實上這也有疑問。由於上面vmalloc的虛擬地址區有4k切割地址,假設vmalloc分配的虛擬地址非常大。那么中間是否有4kb的切割地址?),可是不保證所映射的物理地址也是連續的。

它主要對上面的vmalloc_start到vmalloc_end這段內存操作,返回的虛擬地址就是這一部分的。

        在大多數情況下,不鼓舞使用vmalloc來申請內存,原因: 1、通過vmalloc函數獲取的內存使用效率不高(由於要自己做映射,要推斷哪些是空暇頁等操作)。2、有些架構上給vmalloc使用的內存地址很小。對vmalloc調用可能會由於沒有空暇地址而失敗;3、不能保證物理地址是連續的,對一些驅動程序來說這是硬傷;綜上所述。最好不要用包括vmalloc的代碼作為內核的主線代碼。

        以下大概來說下vmalloc函數的原型:

         void *vmalloc(unsigned long size);

        該函數的實現有3個步驟:1、在vmalloc區域分配一段連續的虛擬內存地址;2、通過伙伴系統獲取物理頁;3、通過對頁表的操作。把1中獲取到的虛擬地址映射到2中分配到物理頁上;

        注意:

        1、上面的圖示我們能夠看出每一個vmalloc虛擬地址之間都有4kb的切割區域(其作用就是防止越界。形成一個空洞,越界時產生異常),所以vmalloc函數實現時,會在size對齊后再添加4kb大小(一個頁的大小)。

        2、在分配物理頁時,會從高端地址(上面的圖示表示的不過虛擬地址而已,物理內存分配能夠看 linux內核內存分配(一、基本概念)中物理頁和虛擬地址的映射圖)分配。gfp為:GFB_KERNEL | _GFP_HIGHMEM;表示該函數可能睡眠,分配的物理地址來自高端物理頁。

常規物理頁給kmalloc使用;vmalloc函數分配高端物理頁時使用alloc_page函數或者alloc_pages_node函數來分配一個整頁,多次調用分配函數來完畢全部的物理頁的分配,這樣就不能保證全部的物理頁一定連續了。

        3、對虛擬地址映射時不會對額外的4k的切割地址進行映射,第2步中也不會對這4k的虛擬切割地址進行分配映射的物理頁。

        以下是vmalloc的映射圖。圖來自《深入linux設備驅動程序內核機制》



        上圖中:從vmalloc區域分配的兩個虛擬頁地址映射到物理地址的高端頁面。當中高端內存是不連續的,虛擬地址最后一個頁沒有進行映射,那就是額外的4k切割頁面。

        用vmalloc分配得到的地址是不能在微處理器之外使用的。由於它們僅僅在處理器的內存管理單元上才有意義。

使用vmalloc函數的正確場合是在分配一大塊連續的、僅僅在軟件中存在的、用於緩沖的內存區域的時候。


ioremap函數

        函數原型:void __iomem  *ioremap(unsigned long phys_addr,  size_t  size);此處的__iomem僅僅是標識返回的地址是io類型的地址;該函數用來把vmalloc區域之間的內存映射到設備I/O地址空間,這個函數和vmalloc函數的實現很相似,不同的地方就是vmalloc是通過伙伴系統分配到物理頁。而ioremap函數卻是利用設備的I/O空間,而不是系統物理頁;至於其它操作能夠看:訪問I/O內存和I/Oport設備

       ioremap函數很多其它用於映射(物理的)PCI緩沖區地址到(虛擬的)內核空間。ioremap函數映射的內存須要用iounmap函數來釋放;


vmalloc和kmalloc比較

        kmalloc函數:

        1、得到的內存保留上次使用的數據,不正確申請到的內存進行初始化(zmalloc函數會初始化申請到的內存)。

        2、返回的邏輯地址(事實上也是虛擬地址)和映射的物理頁都是連續的。調用該函數時可能會休眠;

        3、kmalloc函數和__get_free_pages函數返回的內存地址都是虛擬地址,事實上際的物理地址須要通過MMU轉換后得到(事實上MMU就是通過頁表機制來轉換的)。

        4、kmalloc函數和__get_free_pages函數使用的虛擬地址范圍與物理內存是一一相應的,可能有個常量偏移。這兩個函數不須要改動頁表。

        5、kmalloc函數申請的內存大小是有限制的,一般依據架構決定;


         vmalloc函數和ioremap函數:

        1、使用效率不高,物理頁不保證連續,虛擬地址保證連續。

        2、vmalloc函數和ioremap函數使用的地址范圍全然是虛擬的。每次分配都要通過對頁表的操作來建立映射關系;

        3、vmalloc函數一般用來分配大塊的內存。而且返回的地址不能在微處理器之外使用;

        轉載地址:http://blog.csdn.net/yuzhihui_no1/article/details/47429411



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM