1 名詞解釋:
(1)頁框:物理內存的描述,必須牢牢記住,頁框就是物理內存
(2)頁描述符:描述每一個頁框的狀態信息,所有的也描述符都保存在mem_map[ ]數組中,每個描述符32個字節
(3)節點:系統物理內存被劃分為多個節點,每個節點內cpu訪問頁面的時間是相同的,對應的數據結構:節點描述符
(4)管理區:每個節點又分為多個管理區 對應的數據結構: 管理區描述符
2 頁表管理
重點介紹內核頁表的管理,主要分為兩個階段:啟動階段映射8M的頁表和剩余頁表的映射階段
(1)啟動階段8M頁表的映射過程
(2)剩余頁表的映射過程
幾個比較重要的地址轉換:
虛擬地址轉換成物理地址: virt_to_phsy(address){ __pa(address) }
虛擬地址轉換頁描述符的地址: virt_to_page( kaddr ) { return mem_map + __pa(kaddr) >>12 }
3 用戶進程的地址空間
從內核看來,整個4G的地址空間是這樣的。
進程可用的地址空間是被一個叫mm_struct(進程地址空間描述符)結構體來管理的,同一個進程內的多個線程是共享這個數據結構的。
同時,對于用戶進程來說,每一個進程有一個獨一無二的mm_struct,但是內核線程確不是必須的。下面是操作mm_struct的一些函數。
當然如果在進程創建的時候指定子進程共享父進程的虛擬地址空間的話,比如:
if ( clone_flags & CLONE_VM )
{
atomic_inc(&old_mm->mm_user)
mm = &oldmm;
goto good_mm;
}
還有一點許喲阿注意的就是系統中的第一個mm_struct是需要靜態初始化的,以后的所有的mm_strcut都是通過拷貝生成的。
mmap函數,內存映射函數
該函數的主要功能是在進程地址空間中創建一個線性區。有兩種類型的內存映射:共享型和私有型。二者的主要區別可以理解成是否對其他進程可見。共享型每次對線性區的讀寫都會修改
磁盤文件,一個進程修改共享型的線性區,其他映射這一線性區的所有進程都是可見的。與內存映射相關的數據結構:
(1)與所映射的文件相關的索引節點對象
(2)所映射文件的address_space對象
(3)不同進程對同一文件進行不同映射所使用的文件對象
(4)對文件進行每一不同映射所使用的vm_area_struct
(5)對文件進行映射的線性區所分配的每個頁框對應的描述符
從圖上能看出一個文件對應一個inode,對應一個address_space,對應多個struct file, 對應多個vm_area_file ,對應多個page(頁框),當然也對應多個page(頁描述符)、
mm_struct 內存描述符中的兩棵樹: 當前進程內所有線性區的一個鏈表和所有線性區的紅黑樹
mmap和mm_rb都可以訪問線性區。事實上,它們都指向了同一個vm_area_struct結構,只是鏈接的方式不同
mmap指向的線性區鏈表用來遍歷整個進程的地址空間
紅黑樹mm_rb用來定位一個給定的線性地址落在進程地址空間中的哪一個線性區中
另外,mmap_cache用來緩存最近用過的線性區
address_space中的兩棵樹:基數和優先級搜索數。
address_space的page_tree指向了組織構成這個文件的所有的頁描述符的基樹
address_space的i_mmap指向了組織構成這個文件的所有的線性區描述符的基樹
要注意一個問題:
(1)共享內存映射的頁通常保存在也高速緩存中,私有內存映射的頁只要還沒有修改,也保存在頁高速緩存中。當進程試圖修改一個私有映射的頁時,內核 就把該頁框進行復制,并在進程頁表中用復制的頁替換原來的頁,這就是寫時復制的基礎。復制后的頁框就不會放在頁告訴緩存中了,原因是它不再是表示磁盤上那 個文件的有效數據。
(2)線性區的開始和結束地址都是4K對齊的
進程獲得新線性區的一些典型情況 :
剛剛創建的新進程
使用exec系統調用裝載一個新的程序運行
將一個文件(或部分)映射到進程地址空間中
當用戶堆棧不夠用的時候,擴展堆棧對應的線性區
創建內存映射:
要想創建一個映射,就要調用mmap, mmap()最終會調用do_mmap()
static inline unsigned long do_mmap(struct file *file, unsigned long addr, unsigned long len, unsigned long prot, unsigned long flag,unsigned long offset)
file:要映射的文件描述符,知道映射哪個文件才行
offset:文件內的偏移量,指定要映射文件的一部分,當然也可以是全部
len: 要映射文件的那一部分的長度
flag:一組標志,顯示的指定映射的那部分是MAP_SHARED或MAP_PRIVATE
prot: 一組權限,指定對線性區訪問的一種或多種訪問權限
addr: 一個可選的線性地址,表示從這個地址之后的某個位置創建線性區
基本的過程是:
(1)先為要映射的文件申請一段線性區,調用內存描述符的get_unmapped_area()
(2)做一些權限和標志位檢查
(3)將文件對象的地址struct file地址賦值給線性區描述符vm_area_struct.vm_file
(4)調用mmap方法,這個方法最后調用generic_file_mmap()
其他線性區處理函數
(1)find_vma() : 查找一個線性地址所屬的線性區或后繼線性區
(2)find_vmm_interrection(): 查找一個與給定區間重疊的線性區
(3)get_unmapped_area() : 查找一個空閑的線性區
(4)insert_vm_struct () : 向進程的內存描述符中插入一個線性區
缺頁異常處理程序
(1)背景知識
內核中的函數以直接了當的方式獲得動態內存,內核是操作系統中優先級最高的成分,內核信任自己,采用面級內存分配和小內存分配以及非連續線性區得到內存
用戶態進程分配內存時,請求被認為是不緊迫的,用戶進程不可信任,因此,當用戶態進程請求動態內存時,并沒有立即獲得實際的物理頁框,而僅僅獲得對一個
新的線性地址區間的使用權這個線性地址區間會成為進程地址空間的一部分,稱作線性區(memory areas)。 這樣,當用戶進程真正向這些線性區寫的時候,就會
產生缺頁異常,在缺頁異常處理程序中獲得真正的物理內存。
(2)缺頁異常處理程序需要區分引起缺頁的兩種情況:編程錯引起的缺頁和屬于進程的地址空間尚未分配到物理頁框
簡單流程圖:
詳細流程圖:
linux為什么要分為三個區:ZONE_DMA ZONE_NORMAL ZONE_HIGHMEM?
(1)isa總線的歷史遺留問題,只能訪問內存的前16M的空間
(2)大容量的RAM使得線性地址空間太小,并不是所有的物理空間都能映射到唯一的線性地址空間
如何確定某個頁框屬于哪個節點或管理區?
是由每個頁框描述符中的flag的高位索引的,比如page_zone()函數就是接收頁描述符的地址作為參數,返回頁描述符中flag的高位,并到zone_table[ ]數組中確定相應的管理區描述符的地址
slab算法是用來滿足對以頁框為單位的請求而設置的,簡單介紹以下slab算法的原理
對于以頁為單位的請求發送到管理區分配器,然后管理區分配器搜索它所管轄的管理區,找一個滿足請求的分配區,然后再由這個管理區中的伙伴系統去處理,為了加快這個
過程,每個分區中還提供了一個每cpu頁框高速緩存,來處理單個頁框的請求。
這個過程中有四個請求頁框的函數和宏:
(1)alloc_pages, alloc_page返回分配的第一個頁框的頁描述符的地址
(2)__get_free_pages , __get_free_page 返回分配的第一個頁框的線性地址
其實二者是相同,因為有專門用來處理線性地址到頁描述符地址轉換的函數 virt_to_page() 實現從線性地址到頁描述符地址的轉換
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
