闲聊Linux内存管理(1)
今天還有時間,之前一直想抽時間來寫寫Linux內核原理相關的東西,關注點不在代碼,而在于內在的原理和機制,讓大家對Linux內核有個總體的感性認識,個人認為這很有必要,把看似復雜、深不可測的內核實現,用大家都能理解的方式,用講故事的方式,講給大家聽,如果有人聽后,有原來不過如此的感覺,那我的目的就達到了。
# 內存管理
從哪里開始呢?還是從最基礎的內存管理開始吧。
內存管理是Linux內核中最基礎,也是相當重要的部分。理解相關原理,不管是對內存的理解,還是對大家寫用戶態代碼都很有幫助。很多書上、很多文章都寫了相關內容,但個人總覺得內容太復雜,不是太容易理解,這里想用我自己理解的簡單的方式來描述,希望能有所幫助,按自己的思路,可能有點亂,見諒。
從內存分配開始
大家寫代碼時,應該都會分配內存,不同語言,層次不同,使用的接口不同,不管使用哪種方式,在Linux系統中,基本上都會調用到C庫的malloc接口,那就從malloc分配內存開始。
malloc就是用于分配一段內存,但這里分配到的內存并非物理內存,而是虛擬內存,這里沒有嚴格區分虛擬地址、線性地址之類的概念,只會給大家添負擔,也不深入講述物理內存和虛擬內存的概念,書上通常有大量的篇幅介紹,大家可以簡單這樣理解:
-
虛擬內存就是從進程的角度看,邏輯上的概念,并不實際存在;
-
物理內存就對應物理上內存條上的內存;
- 虛擬內存和物理內存有對應關系;
- 虛擬內存分配時,相應的物理內存還沒有分配;
既然虛擬內存并不實際存在,那么分配來有何用處呢,如果要向其中寫數據,數據如何寫入呢?會寫到哪里去呢?
當然,內存分配后就是拿來用的,如果不能用(比如寫數據),那就沒有意義了。前面說的虛擬內存和物理內存有對應關系,當分配虛擬內存時,相應的物理內存還沒有分配,這里有幾個關鍵問題:
虛擬內存到物理內存的映射
先來解答問題1。
由頁表來建立虛擬內存到物理內存之間的映射關系。?頁表是啥?
簡單看,頁表就是在內存中的一張表(不詳細介紹頁表的具體格式啥的,看書就可以了),可以簡單看做一張hash表,記錄的是虛擬地址和物理地址的對應關系,每個虛擬地址對應一個表項,通過這張表,就能將虛擬地址轉換為物理地址,也就能建立虛擬內存到物理內存的映射關系了。
誰用頁表?
頁表有了,那誰來用呢?不可能是應用程序自己用吧,我寫代碼時好像從來都沒見過頁表?
當然,用戶看到的只是虛擬地址(虛擬內存),其他的對用戶都是透明的~
CPU中有個硬件單元,叫MMU(內存管理單元),頁表就是給MMU硬件用的,MMU使用頁表進行虛擬地址到物理地址的映射。也就是說,地址映射是由硬件完成的,軟件(包括操作系統內核自身)都不管關心。
都說軟件不用關心了,那我們為嘛還需要講頁表?
軟件只是不用頁表而且,但頁表的創建和維護都是由軟件(操作系統內核)負責的,也就是說我們(軟件)創建虛擬內存和物理內存的映射關系,然后由硬件來自動進行地址映射(轉換),我們不需要關心具體的轉換過程。
前面說了,頁表是在內存中,而頁表是由軟件創建的,那MMU如果知道頁表到底在哪兒呢?
簡單說,需要我們(軟件)告訴它在哪兒,如何告訴?當然,寫寄存器。CPU上有特別的寄存器(CR3),向其中寫入頁表的地址,MMU就知道了,然后硬件自己使用即可,我們就不管了。
有多少張頁表?
看似一張頁表就能完成所有的地址映射了?
當然不行,如果是這樣,虛擬內存就沒有什么必要存在了。
這里又涉及新概念了:進程,這是操作系統中最基礎的概念,其實不“新”。
系統中,所有任務都是以進程方式運行的,每個進程都有自己的獨立的虛擬地址空間,好像又說復雜了,簡單說,就是每個進程都有自己的虛擬內存,獨立代表,其他進程看不到自己的虛擬內存,那么就意味著,每個進程都需要獨立的虛擬內存到物理內存的映射,就是說,每個進程都需要自己的頁表。
所以,系統中有多少進程,就有多少張頁表。
頁表創建
前面說了,頁表的創建和維護是由操作系統內核完成的,那何時創建頁表呢?
如前面所說,每個進程都需要自己的頁表,顯然,頁表需要在進程創建時建立。具體的創建過程和原理就不講了。
頁表的維護
如果維護頁表?
當然,進程自己維護,如果維護?
拿前面的例子說,malloc分配虛擬內存后,并沒有分配物理內存,也沒有建立映射關系,沒有修改頁表,那到底什么時候,由誰來做映射?
答案是:“缺頁異?!?。
缺頁異常
缺頁異常(page fault)又是另一個專業術語,表面上看好像不好理解。
簡單看,就是一個異常。什么是異常?這又是硬件上的概念了,具體看看書
簡單說,就是硬件上的一種機制,當硬件檢測到某種“不對”時,主動觸發,然后會自動跳轉到異常處理程序處理。異常跟中斷類似,區別在于,中斷是異步的,由外設觸發;異常是同步的,由CPU自己觸發;好像又扯遠了。
缺頁異常就是一種特定的異常,觸發條件是:MMU檢測到頁表項(頁表中的項~)不存在。
何時觸發缺頁異常?
MMU何時會去檢測頁表項?
當CPU需要訪問某個虛擬地址時,比如向某個虛擬地址寫數據(memset),需要將虛擬地址轉換成物理地址,此時MMU就會自動去頁表中找相應的頁表項,如果發現此時相應的頁表項(也就是虛擬地址到物理地址的映射關系)還不存在,那么就會自動從硬件層面觸發缺頁異常。
也就說,缺頁異常是硬件自己觸發的,條件是當需要訪問某個虛擬地址,而該虛擬地址還沒有相應的頁表項時。
典型的場景就是,當malloc之后,對相應的虛擬地址執行memset操作。
缺頁異常中干嘛?
缺頁異常后,會跳轉到相應的異常處理程序處理,該異常處理程序是內核中提前注冊好的,其中要做的主要操作,就是:分配物理內存,然后修改相應進程的頁表,建立該物理內存和虛擬內存的映射關系(頁表項)。
當然,實際實現要復雜得多,這里不詳細描述。
回頭看看
再來重頭捋一下內存分配過程:
至此,虛擬內存和物理內存都分配完成,并完成映射。
另一個角度看,如果malloc分配內存后,一直不使用,那就一直不會分配物理內存,這種內存分配策略叫延遲分配,有其相應的好處,自己理解一下~
接下來?
本章,從內存分配的角度看了Linux內核中內存管理的關鍵原理,已經以盡量簡單的方式描述了,希望沒給大家帶來負擔~
Linux內存管理還涉及其他很多方面,如:
- 內核自身使用的內存(slab、vmalloc)
- 伙伴系統
- 進程地址空間管理
后面抽空慢慢講,但都希望能盡量簡單,希望大家一看就明白。
原文地址:?http://happyseeker.github.io/kernel/2016/11/10/memory-management-in-kernel.html
總結
以上是生活随笔為你收集整理的闲聊Linux内存管理(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mips TLB miss实现in Li
- 下一篇: mmap原理及流程(kernel 4.1