內存管理最基礎的概念,恐怕是虛擬存儲器(Virtual Memory,簡稱VM)了,它是計算機系統(注意我沒寫操做系統,由於其中還有部分硬件功能)在物理存儲之上的一套機制。它將物理地址(Physical Address,簡稱PA)轉換爲虛擬地址(Virtual Address,簡稱VA),來訪問存儲。正是由於有了虛擬存儲器,纔有了後面的內存轉換、頁表等機制。html
看到這裏我就有疑問了,有了物理地址,程序已經能定位到內存塊而且使用了,爲何須要有虛擬地址?node
實際上,若是隻有一個程序在內存中運行,沒有虛擬存儲問題也不大,好比DOS(又提到它了!),並且聽說DOS確實是沒有虛擬存儲機制的。可是當有了多進程以後,問題就出現了:多個進程共同使用一整個物理內存,既不安全也不方便,好比A用了0xb7001008
,結果B無法知道,而後也用了它,豈不是亂套了?linux
爲了解決這個問題,就有了虛擬存儲機制。對於每一個進程來講,它的虛擬地址空間老是同樣的,可是實際使用的物理內存是分開的,並且它也不知道究竟是在使用虛擬地址,仍是物理地址,反正用就對了!這樣子既簡化了程序開發,又增長了安全性,真是很是巧妙的設計!git
總結一下,虛擬存儲的最大做用就是隔離與抽象。github
爲了瞭解地址的轉換,咱們必須引入「頁」(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
保存虛擬地址到物理地址的映射關係函數
page_table={virtual_address:physical_address}
在程序中使用指針訪問物理地址的時候,對其進行轉換
physical_address=page_table[virtual_address]
是否是很是簡單?
實際上目前的計算機系統作的方式也差很少。可是區別是,由於這兩個操做很是頻繁,光靠軟件實現性能未必有那麼好,因此這兩部分有了一些硬件上的優化。
把VA轉換爲PA的事情,由CPU裏一個專門的部件來完成,它叫作「內存管理單元」(Memory Management Unit,簡稱MMU)。
保存地址映射關係的部件,叫作頁表(Page Table),它是保存在內存中的,由操做系統維護。可是訪問一次內存還要查一次內存,這事感受不太科學,因此MMU還會維護一份用過的頁表索引,這就是傳說中的TLB(Translation Lookaside Buffer,也叫轉換備用緩衝區,咱們學校的孫鍾秀院士在他的教材中將其翻譯爲「快表」)。
因此最後的流程是:
這其實也是一個操做系統反過來影響CPU設計的案例,這也說明,其實硬件跟系統分界並非死的,好比有些CPU的指令集也會包括一些高等的操做,理論上越底層越快,因此到底放在哪一層,主要取決於這個機制的價值和通用性。
至此地址轉換算是差很少了,操做系統和MMU握了個手,合做愉快!
PS:在有些嵌入式CPU上沒有MMU,就確實是軟件來完成地址轉換。不過如今帶有MMU的CPU愈來愈多了!
Linux中內存分配相關的代碼在kernal/page_alloc.c
中,其中核心的函數是struct page * __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order, struct zonelist *zonelist, nodemask_t *nodemask)
。我對能看懂的代碼作了註釋,大部分都沒看懂,等所有都研究一遍以後再來細細研究吧…
Linux內存管理中還有不少細節,羅列幾個,作個備忘。
分區(zone)
Linux將內存分爲幾個區:ZONE_DMA、ZONE_NORMAL和ZONE_HIGHEN。DMA(Direct Memory Access)是一種IO直接操做內存的技術,有些硬件只能用特定地址的內存來進行DMA,對這種內存須要標記一下。
NUMA
NUMA(Non-Uniform Memory Access Architecture)是相對於UMA(Uniform Memory Access Architecture)來講的。UMA就是指多處理器共享一片內存,而NUMA則反其道行之,將CPU綁定到一些內存中,從而加快速度。聽說在多於八核的處理器中效果明顯。
slab
slab這部分LKD講的並很差,有些繞彎(也多是翻譯很差吧),而後我搜到不少資料,大體連描述都照樣複製,我也不知道是否是我智商過低,弄不懂,仍是做者只是作了個摘抄,反正對於這些技術文章只能呵呵了。
其實slab解決了什麼問題呢?咱們知道在內核裏有些數據結構是很經常使用的,例如inode,這些數據結構會頻繁初始化和銷燬。可是初始化數據結構是有開銷的啊,更好的辦法是把它存下來,而後下次建立的時候,直接拿一個現成的,改改內容,就能夠用了!slab又譯做「板坯」,這樣子是否是好理解一點呢?
在實現上,slab會爲一類對象開闢一段空間,存儲多個這樣的對象,而後建立和銷燬,其實只是在這片空間裏指針移動一下的事情了!咱們其實能夠叫它「對象池」或者「結構池」吧!slab的代碼實如今LKD中有很是詳細的描述,再也不贅述了。
參考資料: