1.背景
虛擬內(nèi)存的一大優(yōu)勢就是每個進(jìn)程有自己的虛擬地址空間君躺,OS 負(fù)責(zé)將其虛擬地址空間映射到物理內(nèi)存中驰吓。
內(nèi)核處理用戶部分的地址空間的方式姿鸿,與內(nèi)核部分不同:
- 內(nèi)核部分內(nèi)存的分配是立刻滿足的,并對全局可見:
1)__get_free_pages()或alloc_pages() 從page frame 分配器獲取page frame;
2)kmem_cache_alloc() 或kmalloc() 使用專用或通用的slab 分配器分配objects砰粹;
3)vmalloc() 獲取非連續(xù)的內(nèi)存。
如此簡單的分配方式的兩個原因是:
1)內(nèi)核是整個OS中優(yōu)先級最高的部分造挽;
2)內(nèi)核可靠安全碱璃,所有內(nèi)核函數(shù)都是無error的,不需要加入保護(hù)以防止錯誤 - 用戶進(jìn)程情況刽宪,則不同
1)進(jìn)程內(nèi)存申請的請求厘贼,通常內(nèi)核會推遲分配內(nèi)存給用戶進(jìn)程;
i. 每個進(jìn)程通常實際使用的地址空間圣拄,就只有整個虛擬地址空間的其中的幾個區(qū)域嘴秸;并且每個區(qū)域相隔比較遠(yuǎn)。 內(nèi)核需要高效管理這些分散的區(qū)域庇谆。
ii. 進(jìn)程可執(zhí)行文件加載時岳掐,進(jìn)程不可能在不遠(yuǎn)的將來會訪問所有的code page;
iii. 進(jìn)程通過malloc()分配內(nèi)存時,并不意味著進(jìn)程會很快訪問所有分配的內(nèi)存
2)用戶進(jìn)程并不可靠饭耳,內(nèi)核必須捕捉所有用戶態(tài)的錯誤以確保用戶進(jìn)程會破壞系統(tǒng)穩(wěn)定性和安全性串述。
2. 進(jìn)程虛擬地址空間
2.1 進(jìn)程虛擬地址空間布局
(1)進(jìn)程虛擬地址空間由很多虛擬內(nèi)存區(qū)域(VMA)構(gòu)成:
-
stack
用于存放局部變量,函數(shù)參數(shù)和函數(shù)調(diào)用 -
mmap
主要用于映射文件內(nèi)容到虛擬地址空間的內(nèi)存映射,也用于映射共享object 和動態(tài)庫 -
heap
動態(tài)分配的內(nèi)存存放的地方寞肖,允許進(jìn)程存放運(yùn)行時的數(shù)據(jù)纲酗。malloc()分配的內(nèi)存來自此區(qū)域 -
BSS
存放未初始化的靜態(tài)變量 -
data
存放全局變量衰腌,靜態(tài)初始化的變量 -
text
映射程序二進(jìn)制文件到內(nèi)存的區(qū)域
(2)每個VMA, 物理上映射到一到多個內(nèi)存塊觅赊,進(jìn)程的page table 做相應(yīng)的地址映射右蕊。
2.2 進(jìn)程地址空間描述符
- 進(jìn)程可用的地址空間通過進(jìn)程task_struct 中的struct mm_struct 結(jié)構(gòu)管理
- 每個進(jìn)程有一個mm_struct ,用戶線程共享同一個地址空間;
- 通過尋找所有task_structs 中指向相同的mm_struct 可以識別出task list 中的線程吮螺;
- 內(nèi)核線程不需要mm_struct饶囚,通常內(nèi)核線程的task_struct->mm為NULL;
- mmap
是進(jìn)程地址空間中所有VMA 區(qū)域的鏈表頭 - mmap_cache
緩存上次訪問的VMA鸠补; - mm_rb
所有的VMA 排放在鏈表mm_struct->mmap中萝风,并為了快速查詢,也放在紅黑樹中紫岩,這個是樹的root
- start_code, end_code
對應(yīng)程序二進(jìn)制文件映射的code VMA 區(qū)域的起始和結(jié)束虛擬地址 - start_data, end_data
對應(yīng)data VMA 區(qū)域的起始和結(jié)束虛擬地址 - start_brk, brk
對應(yīng)heap VMA 區(qū)域的起始和當(dāng)前結(jié)束的虛擬地址 - start_stack
對應(yīng)stack VMA 區(qū)域的起始和當(dāng)前結(jié)束的虛擬地址 - arg_start, arg_end
對應(yīng)命令行參數(shù)列表的 VMA 區(qū)域的起始和當(dāng)前結(jié)束的虛擬地址 -
env_start, env_end
對應(yīng)環(huán)境參數(shù)的 VMA 區(qū)域的起始和當(dāng)前結(jié)束的虛擬地址
2.3 虛擬內(nèi)存區(qū)域VMA
-
每個虛擬內(nèi)存區(qū)域由vm_area_struct 管理
- VMA 區(qū)域之間不會重疊规惰,每個VMA區(qū)域代表著相同保護(hù)訪問方式和目的的的區(qū)域
- 進(jìn)程的完整的VMA區(qū)域可通過/proc/PID/maps 查看
- vm_start, vm_end
對應(yīng)這個該vma 區(qū)域的起始虛擬地址和結(jié)束地址 - vm_next
VMA直接通過vm_next 鏈接到一起,通過地址排序 - vm_rb
VMA 通過vm_rb 鏈接到紅黑樹中 - vm_mm
指向所屬的進(jìn)程的mm_struct 結(jié)構(gòu) - vm_file
對于基于文件的VMA 區(qū)域(code 區(qū)被因,共享庫卿拴,共享內(nèi)存映射區(qū)等等)衫仑,vm_file 指向?qū)?yīng)文件的struct file 指針梨与,該文件struct file ->f_dentry->d_inode->i_mapping 的struct address_space 擁有所有關(guān)于文件的信息,包含指向文件系統(tǒng)操作的文件系統(tǒng)函數(shù) - anon_vma_node
heap文狱,stack 和mmap 的虛擬地址空間的分配都通過匿名內(nèi)存映射分配的, 內(nèi)核通過struct anon_vma 將代表匿名內(nèi)存的VMA區(qū)域鏈接到一起粥鞋,便于快速訪問所有的映射匿名頁的進(jìn)程VMA -
vm_ops
VMA 操作指針,open 和close 通常為NULL瞄崇,nopage 用于page fault
-
vm_operations_struct
2.4 基于文件的VMA區(qū)域
-
基于文件的VMA 區(qū)域 通過struct address_space 描述
-
每個address_space結(jié)構(gòu)作為一個文件inode 對應(yīng)的pages 的抽象
i. 文件打開的時候呻粹,kernel 設(shè)置file->f_mapping為inode->i_mapping
ii. inode 是per-file 數(shù)據(jù)結(jié)構(gòu),而file 是per-process 數(shù)據(jù)結(jié)構(gòu)苏研;這樣讓多個進(jìn)程能直接訪問同樣的文件等浊,而不需要與其他進(jìn)程交互
host
指向擁有對于pages的owner inode;i_mmap
為了高效管理cache 中的文件pages, 需要跟蹤所有映射到同一個address_space的VMA摹蘑。 i_mmap 是所有包含當(dāng)前映射到該address_space的VMA的紅黑樹筹燕。-
page_tree
i. 處于address_space對象的所有包含文件數(shù)據(jù)的物理page 都通過radix tree 數(shù)據(jù)結(jié)構(gòu)組織管理起來以高效訪問,page_tree 是radix tree 的root衅鹿,是struct radix_tree_root 類型的實例撒踪;
ii. 一方面一個進(jìn)程的所有VMA 被同時組織管理在一個鏈表和紅黑樹數(shù)據(jù)結(jié)構(gòu)中
iii. 另一方面radix 樹數(shù)據(jù)結(jié)構(gòu)用于反向查找一個給定文件的所有VMA;
-
struct radix_tree_root
radix tree 的每個node 是struct radix_tree_node 類型的,struct radix_tree_root 中的*rnode 作為radix tree的 第一個node;
2.6 文件的虛擬地址空間與文件系統(tǒng)之間的聯(lián)系
文件的內(nèi)存映射是兩個不同地址空間的映射
i. 一個地址空間是用戶進(jìn)程的虛擬內(nèi)存地址空間大渤;
ii. 另一個是文件系統(tǒng)覆蓋的地址空間制妄;內(nèi)核以read, write 請求的方式建立兩個地址空間的聯(lián)系
-
vm_operations_struct 結(jié)構(gòu)體用于建立聯(lián)系,提供方法讀內(nèi)容到物理內(nèi)容
-
而具體的文件系統(tǒng)的操作是通過address_space 結(jié)構(gòu)的address_space_operation
i. readpage,readpages 讀塊設(shè)備的單個和多個頁內(nèi)容到內(nèi)存中泵三;
ii.writepage耕捞,writepages 寫內(nèi)存中的單個頁和多個頁到塊設(shè)備中對應(yīng)的位置
iii. set_page_dirty 標(biāo)記頁內(nèi)容改變衔掸,不在與塊設(shè)備中的內(nèi)容一致
vm_operation_struct 與 address_space 之間的聯(lián)系
i. 通過kernel 的標(biāo)準(zhǔn)實現(xiàn)vm_operations_struct 實例,幾乎所有文件系統(tǒng)在使用 generic_file_vm_ops.-
filemap_faul的實現(xiàn)使用底層文件系統(tǒng)對應(yīng)的address_space提供的readpage 操作