http://blog.csdn.net/kongdefei5000/article/details/70183119
內存映射是個很有用,也很有意思的思想。我們都知道操作系統分為用戶態和內核態,用戶態是不能直接和物理設備打交道的,如果想把硬盤的一塊區域讀到用戶態,則需要兩次拷貝(硬盤->內核->用戶),但是內存映射的設計只需要發生一次的拷貝,大大的提高了讀取數據的效率。那么內存映射的原理和內核是如何實現的呢?
因為內存映射涉及到虛擬內存的管理,虛擬內存到物理內存的映射,因此在詳細介紹內存映射前先普及(回憶)一下相關的概念。
CPU的架構
目前CPU架構主要分為三種:SMP, NUMA, MPP,詳細的介紹參考CPU架構。這里主要關注MMU和TLB在CPU中的角色和位置。
上圖是Intel的i7處理器一個核的架構圖,圖中可以看出每一個CPU核都已一個自己的MMU和TLB。
Linux進程和線程
關於Linux進程和線程的話題想必大家都不陌生,這也是面試時候經常被問到的問題,后續我會專門寫一篇博文介紹Linux線程和進程的區別,已經內核是如何實現的。這里我們只需要知道下面幾個概念即可:
* 操作系統本身也是一個進程
* 操作系統內核層面沒有線程的概念,只有進程的概念,因此線程在內核看來也是一個進程,只是比較特殊
* 線程的實現是glibc實現的(pthread),包括創建,管理等
虛擬地址
虛擬地址是面向進程的一套虛擬內存的地址,為了更好的管理內存,並且能夠保證內存對程序員而言是透明的,也就是寫程序的時候對每一個程序來說都是一樣的地址空間,因此提出了虛擬內存的概念,對應的就是虛擬地址。這里我們不關注具體的細節,為了后面的講解,我們簡單的了解一下虛擬內存在內核層面是怎么進行管理的。
這張圖右側的進程虛擬器對於每一個進程都是一樣的,就是上面說的虛擬空間,然后對於進程,內核對每一個進程都維護了一個task_struch的結構,其中有三個重要的成員,pgd指向改進程的頁表首地址,然后mmap和mm_rb都是用來管理vm的結構,vm是虛擬內存管理的基本單元,含有內存的類型,起始和結束地址,mmap是線性表實現的,mm_rb則使用紅黑樹進行管理,分別適用不同的場景。
MMU
MMU(Memory Management Unit),內存管理單元,主要負責CPU內存訪問的時候將虛擬地址轉換為物理地址的單元。也就是說CPU想要訪問內存必須先經過MMU的轉換,獲得真正的物理地址,才能讀寫物理內存的數據。其實MMU只是一個簡單的計算單元,它通過虛擬地址查找頁表,找到對應的物理地址,然后返回給CPU。在查找頁表的過程中可能發生多次的物理內存訪問,這取決於系統采用頁表管理系統是幾級的,目前Linux的頁表是四級的,因此需要查詢四次頁表,每一次查詢都會獲得一個物理地址指向下一級頁表的內存地址,知道最后獲得實際要訪問的內存物理地址。
從圖中可以看出,當CPU得到一個虛擬地址去訪問內存的時候會先將虛擬地址發送到MMU(1),然后MMU會先從TLB查詢是否存在這個虛擬地址到物理地址的緩存,如果存在則直接返回給CPU,如果沒有則會根據虛擬地址講過計算獲得物理內存中頁表項的地址然后讀取得到PTE(Linux可能要讀取四次),PTE就是物理內存地址或者硬盤存儲地址。然后MMU會將該PTE緩存在TLB中,最后使用這個物理地址再次訪問物理內存或者硬盤地址獲得要訪問的內容。
內存映射
mmap
基礎知識介紹完畢,那么到底什么是內存映射呢,映射讓我們不禁都會想起數學上的映射關系,是的就是那個意思,這里是講設備或者硬盤存儲的一塊空間映射到物理內存,然后操作這塊物理內存就是在操作實際的硬盤空間,不需要經過內核態傳遞。比如你的硬盤上有一個文件,你可以使用linux系統提供的mmap接口,講這個文件映射到進程一塊虛擬地址空間,這塊空間會對應一塊物理內存,當你讀寫這塊物理空間的時候,就是在讀取實際的磁盤文件,就是這么直接,這么高效。通常諸如共享庫的加載都是通過內存映射的方式加載到物理內存的。
mmap本身其實是一個很簡單的操作,在進程的頁表中添加一個頁表項,該頁表項是物理內存的地址。調用mmap的時候,內核會在改進程的虛擬空間的映射區域查找一塊滿足需求的空間用於映射該文件,然后生成該虛擬地址的頁表項,改頁表項此時的有效位(標志是否已經在物理內存中)為0,頁表項的內容是文件的磁盤地址,此時mmap的任務已經完成。
當mmap建立完頁表的映射后,就可以操作改塊內存了,進行的所有改動都會自動寫會磁盤文件。第一次訪問該塊內存的時候,因為頁表項的有效位還是0,就會發生缺頁中斷,然后CPU會使用該頁表項的內容也就是磁盤的文件地址,講該地址指向的內容加載到物理內存,並需改頁表項的內容為該物理地址,有效位置為1.
munmap
有映射必然有解除映射,系統提供了一個munmap的接口去解除指定地址的映射關系。munmap主要的功能是清除頁表項,解除這個映射關系,但是這個過程中會涉及到緩存的刷新,虛擬內存vm的刪除,TLB的一致性(tlb shootdown操作),這里就不再詳細講解。