Linux內核學習之四-內存管理

1、虛擬存儲器

內存管理最基礎的概念,恐怕是虛擬存儲器(Virtual Memory,簡稱VM)了,它是計算機系統(注意我沒寫操做系統,由於其中還有部分硬件功能)在物理存儲之上的一套機制。它將物理地址(Physical Address,簡稱PA)轉換爲虛擬地址(Virtual Address,簡稱VA),來訪問存儲。正是由於有了虛擬存儲器,纔有了後面的內存轉換、頁表等機制。html

看到這裏我就有疑問了,有了物理地址,程序已經能定位到內存塊而且使用了,爲何須要有虛擬地址?node

實際上,若是隻有一個程序在內存中運行,沒有虛擬存儲問題也不大,好比DOS(又提到它了!),並且聽說DOS確實是沒有虛擬存儲機制的。可是當有了多進程以後,問題就出現了:多個進程共同使用一整個物理內存,既不安全也不方便,好比A用了0xb7001008,結果B無法知道,而後也用了它,豈不是亂套了?linux

爲了解決這個問題,就有了虛擬存儲機制。對於每一個進程來講,它的虛擬地址空間老是同樣的,可是實際使用的物理內存是分開的,並且它也不知道究竟是在使用虛擬地址,仍是物理地址,反正用就對了!這樣子既簡化了程序開發,又增長了安全性,真是很是巧妙的設計!git

總結一下,虛擬存儲的最大做用就是隔離與抽象github

2、地址轉換的實現

爲了瞭解地址的轉換,咱們必須引入「頁」(page)的概念。其實頁就是一塊連續的內存,這也是操做系統利用內存的最小單位。更直觀一點的說,在Linux裏,它是這麼實現的:安全

struct page{
    //保存狀態
    unsigned long flags;
    //引用計數
    atomic_t _count;
    atomic_t _mapcount;
    unsigned long private;
    struct address_space *mapping;
    pgoff_t index;
    struct list_head lru;
    //指向虛擬地址
    void *virtual;
}

page結構體保存對應頁的引用數、虛擬地址等信息。由於這個結構自己也是消耗內存的,因此一頁的大小過小,那麼還要存page結構,就有不少內存浪費了,很不划算。若是頁大小太大,常常會出現一個頁裝不滿的狀況,也是咱們不肯看到的。在32位CPU裏,一頁是4KB。數據結構

有了頁的知識,地址轉換就能夠進行了。在看這部分以前,不妨先想一想,若是讓咱們實現一套地址轉換,會怎麼作?app

彷佛這沒有什麼難度啊?能夠分兩部分,你看我僞代碼都寫好了:ide

  1. 保存虛擬地址到物理地址的映射關係函數

    page_table={virtual_address:physical_address}
  2. 在程序中使用指針訪問物理地址的時候,對其進行轉換

    physical_address=page_table[virtual_address]

是否是很是簡單?

實際上目前的計算機系統作的方式也差很少。可是區別是,由於這兩個操做很是頻繁,光靠軟件實現性能未必有那麼好,因此這兩部分有了一些硬件上的優化。

把VA轉換爲PA的事情,由CPU裏一個專門的部件來完成,它叫作「內存管理單元」(Memory Management Unit,簡稱MMU)。

保存地址映射關係的部件,叫作頁表(Page Table),它是保存在內存中的,由操做系統維護。可是訪問一次內存還要查一次內存,這事感受不太科學,因此MMU還會維護一份用過的頁表索引,這就是傳說中的TLB(Translation Lookaside Buffer,也叫轉換備用緩衝區,咱們學校的孫鍾秀院士在他的教材中將其翻譯爲「快表」)。

因此最後的流程是:

  1. 操做系統新建進程時,爲進程分配內存空間,並更新頁表;
  2. 該進程的指令到CPU以前,其中的虛擬地址,會觸發MMU轉換流程;
  3. MMU先到TLB中找頁表,找不到再去物理內存中找頁表,最後轉換爲物理地址,遞給CPU執行。

這其實也是一個操做系統反過來影響CPU設計的案例,這也說明,其實硬件跟系統分界並非死的,好比有些CPU的指令集也會包括一些高等的操做,理論上越底層越快,因此到底放在哪一層,主要取決於這個機制的價值和通用性。

至此地址轉換算是差很少了,操做系統和MMU握了個手,合做愉快!

PS:在有些嵌入式CPU上沒有MMU,就確實是軟件來完成地址轉換。不過如今帶有MMU的CPU愈來愈多了!

3、Linux中的內存管理

Linux中內存分配相關的代碼在kernal/page_alloc.c中,其中核心的函數是struct page * __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, nodemask_t *nodemask)。我對能看懂的代碼作了註釋,大部分都沒看懂,等所有都研究一遍以後再來細細研究吧…

Linux內存管理中還有不少細節,羅列幾個,作個備忘。

  1. 分區(zone)

    Linux將內存分爲幾個區:ZONE_DMA、ZONE_NORMAL和ZONE_HIGHEN。DMA(Direct Memory Access)是一種IO直接操做內存的技術,有些硬件只能用特定地址的內存來進行DMA,對這種內存須要標記一下。

  2. NUMA

    NUMA(Non-Uniform Memory Access Architecture)是相對於UMA(Uniform Memory Access Architecture)來講的。UMA就是指多處理器共享一片內存,而NUMA則反其道行之,將CPU綁定到一些內存中,從而加快速度。聽說在多於八核的處理器中效果明顯。

  3. slab

    slab這部分LKD講的並很差,有些繞彎(也多是翻譯很差吧),而後我搜到不少資料,大體連描述都照樣複製,我也不知道是否是我智商過低,弄不懂,仍是做者只是作了個摘抄,反正對於這些技術文章只能呵呵了。

    其實slab解決了什麼問題呢?咱們知道在內核裏有些數據結構是很經常使用的,例如inode,這些數據結構會頻繁初始化和銷燬。可是初始化數據結構是有開銷的啊,更好的辦法是把它存下來,而後下次建立的時候,直接拿一個現成的,改改內容,就能夠用了!slab又譯做「板坯」,這樣子是否是好理解一點呢?

    在實現上,slab會爲一類對象開闢一段空間,存儲多個這樣的對象,而後建立和銷燬,其實只是在這片空間裏指針移動一下的事情了!咱們其實能夠叫它「對象池」或者「結構池」吧!slab的代碼實如今LKD中有很是詳細的描述,再也不贅述了。

參考資料:

相關文章
相關標籤/搜索