mmap 機制

1. mmap介紹

mmap是一種內存映射文件的方法,即將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。實現這樣的映射關系後,進程就可以采用指針的方式讀寫操作這一段內存,而系統會自動回寫臟頁面到對應的文件磁盤上,即完成瞭對文件的操作而不必再調用read,write等系統調用函數。相反,內核空間對這段區域的修改也直接反映用戶空間,從而可以實現不同進程間的文件共享

2. Linux內存管理介紹

結構體定義

在Linux操作系統中,一切皆文件,所有概念都被通過文件描述符進行抽象建模來進行標識,內存管理也是如此,如下是Linux中進程、內存管理的結構描述。

9f157c01ebcfb651d147fb95f664b1ed

進程結構體(task_struct)

在task_struct結構體中,進程地址空間通過以下成員變量指向mm_struct即內存空間,每個進程空間都有自身的內存空間。

內存結構體(mm_struct)

在mm_struct內存結構體中,通過兩種數據結構來表示vm_area_struct內存空間,當內存空間較少時采用鏈表進行管理,當內存空間較多時會采用紅黑樹進行管理。下面是mm_struct的類結構,內容較多,為瞭保證完整性沒有做刪減聚焦。

vm_area_struct的結構體如下

  1. vm_start, vm_end表示區域的開始位置和結束位置,確定瞭區域的邊界。兩個vm_area_struct不會出現交叉的情況
  2. vm_page_prot 表示這個區域的頁的訪問權限
  3. shared結構處理有後備文件的內存映射,和後備文件的address_space地址空間關聯起來
  4. anon_vma_node, anon_vma處理匿名文件共享內存映射的情況,映射到同一物理內存頁的映射都保存在一個鏈表中
  5. vm_pgoff, vm_file都是處理有後備文件內存映射的情況,獲得該映射在文件的頁偏移量,以及打開文件file實例的信息

對於有後備文件的映射,內核還提供瞭一個優先查找樹結構,來加速確定一個文件和所有映射到這個文件的虛擬內存區域vm_area_struct實例的關系,從而可以得到所有映射到這個文件的進程信息。這張圖表示瞭一個後備文件被mmap映射後內核建立的一些數據結構,涉及到瞭內存管理的數據和文件系統的數據

9dc691e51c7eeefe8033a6a3d571032f

內核提供瞭一系列的函數對虛擬內存區域vm_area_struct進行操作,比如創建,刪除,合並,查找等等。而mmap是C標準庫提供給用戶程序的一個函數來使用內存映射,建立起文件地址空間和虛擬內存區域的映射關系。

頁表管理

b02032ec66ce7122bdef77bc95746c5b

Linux Kernel 使用內存管理的時候,采取的是頁式的管理方式,應用程序給出的內存地址是虛擬地址,是經過若幹層的頁表的轉換才能得到真正的物理地址,所以相對來說,進程的地址空間是一份虛擬的地址空間,每一個地址通過頁表的轉換映射到所謂的物理地址空間上。

3. mmap內存映射剖析

內存&文件映射結構

如上介紹Linux內存管理方式,進程、進程內存、mmap的關系圖如上

  • 進程(Process) 通過task_struct進行表示,會包含一個內存結構體mm_struct
  • 內存(Memory) 通過mm_struct進行標識,會包含一個內存映射體mmap和棧、堆、bss數據段、初始化數據段、text數據段等內存空間組成,這些組成體都是通過vm_area_struct這個最小的內存管理組成單元進行構建和表示的
  • 內存空間區域(Virtual Memory Area) 內存空間區域即組成內存管理的最小單元,裡面包含vm_start、vm_end、vm_next等變量進行內存頁結構的表示與維護

內存&文件映射過程

mmap內存映射的實現過程,總的來說可以分為三個階段:

  • 進程啟動映射過程,並在虛擬地址空間中為映射創建虛擬映射區域
    • 進程在用戶空間調用庫函數mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
    • 在當前進程的虛擬地址空間中,尋找一段空閑的滿足要求的連續的虛擬地址
    • 為此虛擬區分配一個vm_area_struct結構,接著對這個結構的各個域進行瞭初始化
    • 將新建的虛擬區結構(vm_area_struct)插入進程的虛擬地址區域鏈表或樹中
  • 調用內核空間的系統調用函數mmap(不同於用戶空間函數),實現文件物理地址和進程虛擬地址的一一映射關系
    • 為映射分配瞭新的虛擬地址區域後,通過待映射的文件指針,在文件描述符表中找到對應的文件描述符,通過文件描述符,鏈接到內核“已打開文件集”中該文件的文件結構體(struct file),每個文件結構體維護著和這個已打開文件相關各項信息。
    • 通過該文件的文件結構體,鏈接到file_operations模塊,調用內核函數mmap,其原型為:int mmap(struct file *filp, struct vm_area_struct *vma),不同於用戶空間庫函數。
    • 內核mmap函數通過虛擬文件系統inode模塊定位到文件磁盤物理地址。
    • 通過remap_pfn_range函數建立頁表,即實現瞭文件地址和虛擬地址區域的映射關系。此時,這片虛擬地址並沒有任何數據關聯到主存中。
  • 進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存(主存)的拷貝
    • 進程的讀或寫操作訪問虛擬地址空間這一段映射地址,通過查詢頁表,發現這一段地址並不在物理頁面上。因為目前隻建立瞭地址映射,真正的硬盤數據還沒有拷貝到內存中,因此引發缺頁異常。
    • 缺頁異常進行一系列判斷,確定無非法操作後,內核發起請求調頁過程。
    • 調頁過程先在交換緩存空間(swap cache)中尋找需要訪問的內存頁,如果沒有則調用nopage函數把所缺的頁從磁盤裝入到主存中。
    • 之後進程即可對這片主存進行讀或者寫的操作,如果寫操作改變瞭其內容,一定時間後系統會自動回寫臟頁面到對應磁盤地址,也即完成瞭寫入到文件的過程。

值得註意的是:

  • 前兩個階段僅在於創建虛擬區間並完成地址映射,但是並沒有將任何文件數據的拷貝至主存。真正的文件讀取是當進程發起讀或寫操作時。
  • 修改過的臟頁面並不會立即更新回文件中,而是有一段時間的延遲,可以調用 msync() 來強制同步, 這樣所寫的內容就能立即保存到文件裡瞭。

mmap分為有後備文件的映射和匿名文件的映射,這兩種映射又有私有映射和共享映射之分,所以mmap可以創建4種類型的映射

  1. 後備文件的共享映射,多個進程的vm_area_struct指向同一個物理內存區域,一個進程對文件內容的修改,會被其他進程可見。對文件內容的修改會被寫回到後備文件。
  2. 後備文件的私有映射,多個進程的vm_area_struct指向同一個物理內存區域,采用寫時拷貝的方式,當一個進程對文件內容做修改,不會被其他進程看到。另外對文件內的修改也不會被寫回到後備文件。當內存不夠需要進行頁回收時,私有映射的頁被交換到交換區。一般用在加載共享代碼庫
  3. 匿名文件的共享映射,內核創建一個初始都是0的物理內存區域,然後多個進程的vm_area_struct指向這個共享的物理內存區域,對該區域內容的修改對所有進程可見。匿名文件在頁回收時被交換到交換區
  4. 匿名文件的私有映射,內核創建一個初始都是0的物理內存區域,對該區域內容的修改隻對創建者進程可見。匿名文件在頁回收時被交換到交換區。malloc()底層是用瞭匿名文件的私有映射來分配大塊內存。

发表回复

相关推荐

防雾眼镜布是智商税吗?哪种防雾眼镜布是真的有用?

冬天到了!又到了起雾的季节! 口罩+眼镜=一秒起雾! 于是眼镜党们的疑惑又来了:如何戴口罩眼镜不起雾? 往年我也有这样烦 ...

· 1分钟前

如何與命運抗爭?

文|袁運錄這是千千萬萬抑鬱癥患者面臨的同樣問題。你為什麼抑鬱?因為你在命運面前被打趴瞭下來,因為你鬥不過命運,所以你才...

· 1分钟前

那些恐龍時代的巨人

一說起恐龍時代的巨人,大傢會不約而同想到一個名字那就是蜥腳類,但是蜥腳類恐龍傢族不全是大個子,也有袖珍版成員。馬紮爾...

· 1分钟前

深污!媽媽送18歲女兒不可描述的禮物,網友們都集體凌亂瞭……

記者:Lydia來源:BuzzFeed咳咳,各位L社的小夥伴們,今天要開車瞭,大傢請坐穩……幾天前,一位外國女網友在推特上分享瞭媽媽...

· 2分钟前

2023年苹果电脑鼠标推荐:从罗技到Mac都有,价格仅为妙控鼠标四分之一,Windows用户也更好用!

大部分 MacBook Air 或是 MacBook Pro 的使用者,可能会觉得触摸板已经十分好用,不需要再加购其他外设工具;但是在制作图标 ...

· 2分钟前