一 linux虛擬內存、MMU、分頁的基本原理

整體目錄

一 linux虛擬內存、MMU、分頁的基本原理
二 OOM打分因子、oom_adj以及oom_score
三 頁的alloc與free、Buddy算法以及CMA
四 page_fault、內存IO交互、VSS、LRU
五 DMA及Cache一致性

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

最近學習了內存管理的相關知識點,主要參考了三者:
1:宋寶華老師的內存講解視頻
2:蝸窩科技:蝸窩科技之內存管理
3:三個有意思的博客:
Memory Management分類50篇
Linux 內存管理

Linux底層內存管理、分配器、內存回收、用戶態內存分配等內容的學習筆記,該博客與宋寶華老師講解的視頻有很多相同之處。

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

虛擬內存

《深入理解計算機系統》對虛擬內存的解釋:

虛擬內存是一種操作系統對主存的抽象概念,提供了三個重要能力:
1: 它將主存看作是一個存儲在磁盤上地址空間的高速緩存,並根據需要在主存和磁盤之間來回切換數據。
2:它爲每個進程提供了一致的地址空間,從而簡化了內存管理。
3: 它保護了每個進程的地址空間不被其他進程破壞。
個人理解:
虛擬內存:從邏輯上對內存容量加以擴充,它爲每個進程提供了4G的獨享空間,由操作系統通過地址映射的方式,轉換爲對物理內存的訪問。在32位Linux機器上,每個進程的虛擬內存都是4G。(這裏的虛擬內存與操作系統使用中過程常見的虛擬內存概念不同,不要混淆了,如Linux中swap)
在這裏插入圖片描述
具體鏈接可以查看:
《深入理解計算機系統》筆記—(3)虛擬內存
linux閱碼場鏈接
廖威雄: 學習Linux必備的硬件基礎一網打盡中對於虛擬內存的解釋是“從邏輯上對內存容量加以擴充”。
我們把APP訪問到的4G虛擬內存地址叫做:虛擬地址
我們把內核實際管理的2G物理內存地址叫做:物理地址

個人理解

1:CPU 通過物理總線訪問內存,那麼訪問地址的範圍就受限於機器總線的數量,在32位機器上,有32條總線,每條總線有高低兩種電位分別代表 bit 的 1 和 0,那麼可訪問的最大地址就是 2^32bit = 4GB,所以說 32 位機器上插入大於 4G 的內存是無效的,CPU 訪問不到多於 4G 的內存。
2:下圖爲4G 虛擬地址空間分佈:
其中的 3G~4G 的空間中,是內核的地址空間,每個進程的這部分都不可用於應用程序,針對 0 ~ 3GB 的用戶空間來說,用戶的程序分爲 .data,.text,.bss,stack,heap 這幾個區域,上圖爲這些區域在 3G 的空間分佈

程序段(Text):程序代碼在內存中的映射,存放函數體的二進制代碼。
初始化過的數據(Data):在程序運行初已經對變量進行初始化的數據。
未初始化過的數據(BSS):在程序運行初未對變量進行初始化的數據。
棧 (Stack):存儲局部、臨時變量,函數調用時,存儲函數的返回指針,用於控制函數的調用和返回。在程序塊開始時自動分配內存,結束時自動釋放內存,其操作方式類似於數據結構中的棧。
堆 (Heap):存儲動態內存分配,需要程序員手工分配,手工釋放.注意它與數據結構中的堆是兩回事,分配方式類似於鏈表。
具體細節可以參考Linux 內存管理窺探(1):內存規劃與分佈

進程的虛擬內存空間會被分成不同的若干區域,每個區域都有其相關的屬性和用途,一個合法的地址總是落在某個區域當中的,這些區域也不會重疊。在linux內核中,這樣的區域被稱之爲虛擬內存區域(virtual memory areas),簡稱 VMA。一個vma就是一塊連續的線性地址空間的抽象,它擁有自身的權限(可讀,可寫,可執行等等) ,每一個虛擬內存區域都由一個相關的 struct vm_area_struct 結構來描述。
從進程的角度來講,VMA 其實是虛擬空間的內存塊,一個進程的所有資源由多個內存塊組成,所以,一個進程的描述結構 task_struct 中首先包含Linux的內存描述符 mm_struct 結構。
mm_struct詳解Linux之內存描述符mm_struct

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

頁和頁表 MMU

頁和頁表 MMU參考資料

頁表和MMU網上的資料相對比較多,本人比較推薦linux閱碼場鏈接
廖威雄: 學習Linux必備的硬件基礎一網打盡;但是有關多級頁表他描述的不是太到位,可以查看宋寶華: CPU是如何訪問到內存的?–MMU最基本原理

本人理解:

1:把APP的虛擬地址空間切分爲若干個大小相等的片,每一片,就是我們說的頁(Page),在Linux上,頁大小通常爲4KB,頁是內存管理的基本單位。
2:把物理內存也按"頁"的大小切分爲若干大小相等的片,每一片,就是我們說的頁框(frame),大小通常爲4KB,頁框也叫頁幀、物理塊。
3:頁表就是一個存放頁表條目(Page Table Entry,PTE)的數組,裏面存放了若干個頁表條目。頁表的每一行是32bit,所以如一個32位CPU的一級頁表所佔內存空間計算:尋址空間是4GB,每一頁大小是4KB,那麼總共需要4GB/4KB=1024*1024行;每一行的大小是32bit也就是4B,所以總共佔用內存是4MB,且一級頁表在內存中必須是連續的地址,所以,這個頁表的大小是4MB,覆蓋了整個0-4GB的虛擬地址空間,任何一個虛擬地址,都可以用地址的高20位(由於一頁是4KB,4KB=2^12,低12位就是葉內偏移了),作爲頁表這個表的行號去讀對應的頁表項。

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

分類知識點

多級頁表

一級頁表的缺陷:一個進程,真的會需要一整個虛擬地址空間去存放嗎?.
二級頁表可以查看該博客:內存管理之二級頁表詳解
我們用地址的高10位作爲一級頁表的索引,中間10位作爲2級頁表的索引。CPU訪問虛擬地址16,這個地址如果分解爲10/10/12位的話,就是這個樣子:
在這裏插入圖片描述
那麼MMU會用0這個下標去訪問一級頁表(一級頁表的地址填入MMU的頁表地址寄存器)的第0行,第0行的內容寫的是2MB(此處不再是最終的物理地址,而是二級頁表的物理地址),證明二級頁表的地址在2MB,於是MMU自動去以中間的10位作爲下標,去查詢位置在2MB的二級頁表,在2級頁表裏面,最終查到第0頁(地址範圍0x00000000~0x00000FFF)這個虛擬地址的物理地址是1GB,於是MMU去訪問內存條的1GB+16這個物理地址。
在這裏插入圖片描述
據以上分析,1級頁表佔據的內存是2的10次方,再乘以4,即4KB。而每個二級頁表,也是2的10次方,再乘以4,即4KB。分級機制的主要好處是,二級頁表不是一定存在了,比如一級頁表的第2行不命中,也即如下地址都無效的話:
在這裏插入圖片描述
那麼這一行對應的二級頁表,就整個都不需要了,於是就省掉了這段區間4KB二級頁表的內存佔用。頁表當然還有是三級甚至更多。
至於有多級頁表的時候,其實MMU也只需要知道一級頁表的基地址即可。每次切換進程的時候,把一級頁表的地址重新填入MMU,把新的進程的頁表激活即可。

進程的內存申請詳解

內存分配的原理__進程分配內存有兩種方式,分別由兩個系統調用完成:brk和mmap系統調用
Linux進程所能直接操作的地址都爲虛擬地址。當進程需要內存時,從內核獲得的僅僅是虛擬的內存區域,而不是實際的物理地址,進程並沒有獲得物理內存,獲得的僅僅是對一個新的線性地址區間的使用權。實際的物理內存只有當進程真的去訪問新獲取的虛擬地址時,纔會由“請求頁機制”產生“缺頁”異常,從而進入分配實際頁面的例程。在int p = (int)malloc(100M)時返回的p地址指向的是內存中的zero_page,此時若打印p指針開始的100m內存區域,結果都爲0,在VMA中對於該100M區域的權限是R+W。但是在內存的頁表分配中卻只是R權限。當應用層對p開始的10個字節寫值時,發現頁表中只擁有R權限,此時會發生page_fault,此時在VMA的vm_area_struct 結構體中發現其擁有W權限,於是建立內存和虛擬內存的映射,再爲其分配一頁4KB大小的空間。(注:此爲第一次申請的情況,因爲一般來說都是通過libc或者slab來進行二次內存分配)
內核中分配空閒頁面的基本函數是get_free_page/get_free_pages,它們或是分配單頁或是分配指定的頁面(2、4、8…512頁)。
注意:get_free_page是在內核中分配內存,不同於malloc在用戶空間中分配,malloc利用堆動態分配,實際上是調用brk()系統調用,該調用的作用是擴大或縮小進程堆空間(它會修改進程的brk域)。如果現有的內存區域不夠容納堆空間,則會以頁面大小的倍數爲單位,擴張或收縮對應的內存區域,但brk值並非以頁面大小爲倍數修改,而是按實際請求修改。因此Malloc在用戶空間分配內存可以以字節爲單位分配,但內核在內部仍然會是以頁爲單位分配的。
另外,需要提及的是,物理頁在系統中由頁結構struct page描述,系統中所有的頁面都存儲在數組mem_map[]中,可以通過該數組找到系統中的每一頁(空閒或非空閒)。而其中的空閒頁面則可由上述提到的以夥伴關係組織的空閒頁鏈表(free_area[MAX_ORDER])來索引。

內存映射(mmap)

  • 首先普及kmalloc和vmalloc的區別:
    kmalloc是物理和邏輯都連續的物理內存映射虛擬地址;vmalloc是邏輯連續但非物理連續的vmalloc分配的內存虛擬地址。
    Linux 字符設備驅動—— ioremap() 函數解析
    在這裏插入圖片描述
    其次普及內存描述符
    參考網址:Linux驅動mmap內存映射在這裏插入圖片描述
    內存映射是把設備地址映射到進程空間地址(注意:並不是所有內存映射都是映射到進程地址空間的,ioremap是映射到內核虛擬空間的,mmap是映射到進程虛擬地址的),實質上是分配了一個vm_area_struct結構體加入到進程的地址空間,也就是說,把設備地址映射到這個結構體,映射過程就是驅動程序要做的事了。
    接下里是正餐,mmap詳解:
    Linux mmap
    該博客從內核角度解釋了Linux下的mmap。

在這裏插入圖片描述

局部性原理

時間局部性:如果一個數據/指令正在被訪問,那麼在近期它很可能還會被再次訪問。
空間局部性:在最近的將來將用到的信息很可能與現在正在使用的信息在空間地址上是臨近的。
順序局部性:在典型程序中,除轉移類指令外,大部分指令是順序進行的。 簡單來說,對同一個進程而言,CPU訪問了某個邏輯地址的數據/指令,則CPU在將來很有可能再次訪問這個虛擬地址或相鄰的一小片連續地址,這就是局部性原理。
學習一下什麼是頁面調入和頁面置換?如何置換頁?以及何爲LRU置換算法? 都可以查看該博客:第四章

CPU尋找頁面的過程

CPU尋址則建議查看廖威雄: 學習Linux必備的硬件基礎一網打盡的第六章,講解的很詳細。

進程切換和花銷

同上。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章