内存映射mmap
先看下白話MMAP:https://www.cnblogs.com/liqiangchn/p/13587437.html
本文鏈接:找不到了
內存映射是linux中的一個重要機制,它和虛擬內存管理和文件IO都有直接的關系,本篇將詳細介紹linux中內存映射的原理。
mmap基本概念
? ? ? ? 在介紹內存映射之前,首先知道現代計算機系統普遍采用虛擬內存的方式管理物理內存。在32位機器上每個進程都有自己的4G虛擬內存空間,其中0-3G屬于用戶空間,是該進程獨有的;3-4G之間的是內核空間,是計算機中的所有進程的內核空間和內核進程所共享的地址空間。必須明確的一點是:用戶空間和內核空間屬于虛擬地址空間,是虛擬內存中的概念。進程地址空間的劃分如圖1所示。
圖 1 進程地址空間
? ? ? ? mmap將就是圖1的進程空間中用戶空間的內存映射區域和磁盤上的某個文件或者其他對象形成一一對應關系,形成這樣的關系之后,進程就可以采用對虛擬地址(指針)讀寫的方式實現對內存映射區對應的物理內存的讀寫,而這種讀寫會被系統自動通過后臺線程(flusher)刷到后備存儲空間、或者通過msync調用刷到對應的后備存儲空間,從而不用調用read(),write()等系統調用實現對文件的讀寫。如果不同的進程映射同一個文件的同樣區間或者映射同一個匿名對象到各自的虛擬內存空間,那么可以實現進程之間的通信,這是一種十分高效的IPC方式。
? ? ? ? 從圖1中可以看出進程虛擬地址空間中,用戶空間被劃分成很多的段,包含代碼段、數據段、未初始化數據段、堆、內存映射區還有棧等區間,每一個區間都對應一個或者多個管理結構體,在內核中用vm_area_struct表示。那么進程的用戶空間可以表示為vm_area_struct結構體組成的鏈表,如圖2所示。
圖 2 進程地址空間中不同區段在內核中的表示
mmap的基本原理
? ? ? ? mmap內存映射的實現過程,可以分為三個階段:
一、進程在用戶空間調用mmap啟動映射過程,內核在該進程的虛擬地址空間中為映射創建虛擬映射區域結構體---vm_area_struct
? ? ? ? 1.進程在用戶空間調用mmap,函數原型為
? ? ? ? void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
? ? ? ? 關于該系統調用的中多解釋可以參考《unix環境高級編程》中14.8節;
? ? ? ? 2.如果start為NULL(一般的使用情況,增強可移植性),則由內核在當前進程的虛擬地址空間中,尋找一段能夠滿足映射長度需求的連續虛擬地址區間;
? ? ? ? 3.為尋找到的連續虛擬地址區間分配一個vm_area_struct結構體,接著對這個結構體中的各個成員變量進行初始化;
? ? ? ? 4.將新建的虛擬區間結構體插入到當前進程的vm_area_struct結構體鏈表和相應的紅黑樹結構中;
二、調用內核空間的系統函數mmap(與為用戶空間提供的系統調用不是一回事),實現文件磁盤地址和進程虛擬地址的一一映射關系
????????1.為映射分配了新的虛擬地址區域之后,通過待映射的文件描述符在“進程文件描述符表”的struct file* fd_array中找到對應的指向文件的struct file*,每個進程都為該進程打開的所有文件保存著一個struct file*數組,其中的每個元素都是指向“系統文件描述符”的中的struct file的。系統為每個被打開的文件都維護一個struct file,struct file中存在指向已打開文件的struct path成員,struct path成員中存在指向系統打開的特定文件的struct dentry*,struct dentry存在指向該能夠唯一標識該文件的struct inode結構體(這種關系可以參考博客),簡化圖如圖3。
圖 3 進程地址空間、頁緩存和文件系統關系圖
?
? ? ? ? 2.通過該文件最終找到的struct inode結構體中的struct file_operations* i_fop模塊,調用內核函數的mmap(也就是驅動程序,該模塊也是虛擬文件系統和實際文件系統連接的橋梁),其原型為int mmap(struct file* filp, struct vm_area_struct* vma),通過該內核函數就知道這是將虛擬地址區間和實際的后備文件系統相關聯。
? ? ? ? 3.通過1中所述,由struct file能夠找到系統打開的文件所對應的inode結構體,并通過inode結構體中的設備號和塊號最終定位到文件在磁盤的物理地址。
? ? ? ? 4.通過remap_pfn_rang函數建立頁表,實現文件在磁盤中的地址和虛擬地址區域的映射關系。此時這片虛擬地址并沒有任何數據關聯到主存中,只是建立了虛擬地址和磁盤地址之間的映射關系。
三、進程訪問分配的虛擬的地址區間中的某個地址,引發缺頁異常,實現文件內容到物理內存的拷貝
? ? ? ? 1.進程的讀或者寫操作訪問虛擬地址區間中的一個或者一段映射地址,通過查詢頁表,發現這一段地址并不在主存上存在對應的物理頁面。因為目前只是建立了地址映射,真正的硬盤數據還沒有拷貝到物理內存中,由此引發缺頁異常。
? ? ? ? 2.缺頁異常通過一系列判斷,確定操作合法后,通過DMA的方式讀取數據。
? ? ? ? 3.調頁過程先在交換緩存空間(swap cache)中尋找需要訪問的內存頁,如果沒有則調用nopage函數把所缺的頁從磁盤裝入到物理內存中。這個過程還涉及分配物理頁框、確定物理頁框的地址、將讀取的數據寫入物理頁框,最終更新頁表中訪問的虛擬地址對應的物理頁框的地址。
? ? ? ? 4.之后進程從缺頁異常中恢復即可讀取該內容文件對應的實際內容或者對文件進行寫操作。
? ? ? ??mmap調用需要說明的一點是物理頁框最終是由內核以頁緩存的方式進行管理的,參見《linux內核設計與實現》中頁緩存一章。因此,如果是對文件進行寫操作,那么臟頁面不會立即更新到文件中,而是暫時寫到頁緩存中,由后臺的flusher線程將對文件的更新寫到磁盤中或者是由用戶調用msync將對應的數據刷新到磁盤中。
mmap映射的四種類型
? ? ? ? mmap分為有后備文件的映射和匿名映射,而這兩種映射又都有私有映射和共享映射之分,所以mmap一共存在四種類型的映射。
? ? ? ? 1.有后備文件的共享映射。多個進程的vm_area_struct指向同一個物理內存區域,一個進程對文件內容的修改對其他進程可見,對文件內容的修改最終會被寫到后備文件中。
? ? ? ? 2.有后備文件的私有映射。多個進程的vm_area_struct指向同一個物理內存區域,采用寫時拷貝的方式,當一個進程對文件內容做修改,不會被其他的進程所看到,另外對文件內容的修改也不會被寫到后備文件。當內存不夠需要執行頁回收操作的時候,私有映射的頁被交換到交換區或者直接寫到磁盤。一般用在加載共享代碼庫。
? ? ? ? 3.匿名文件的共享映射。內核創建一個都是0的物理內存區域,然后多個進程的vm_area_struct指向這個共享的物理內存區域,對該區域內容的修改對所有的進程都是可見的,匿名文件在頁會頁回收的時候被交換到交換區。
? ? ? ? 4.匿名文件的私有映射。內核創建一個初始都是0的物理內存區域,對該區域的內容的修改只對進程創建者可見,匿名文件在頁回收的時候被交換到交換區。malloc()的底層調用是用了匿名文件的私有映射來分配大塊的內存。
mmap的用途
? ? ? ? 內存映射的用途很多,如
? ? ? ? 1.后備文件的共享映射可以用作內存映射IO來對大文件進行操作,比普通IO減少一次內存的拷貝工作。需要注意的是內存映射IO涉及到內核的很多操作,比如vm_area_struct的創建、頁表的修改等等,比普通的IO操作更加復雜。小文件的讀寫使用普通IO更合適。
? ? ? ? 2.后備文件的私有映射可以用作共享庫二進制文件代碼段,數據段的加載。
? ? ? ? 3.匿名文件的共享映射可以用作fork時,讓父子進程共享匿名映射分配的內存。
? ? ? ? 4.匿名文件的私有映射可以用作進程的私有內存分配。
?
總結
- 上一篇: B树,B-树和B+树、B*树的区别
- 下一篇: linux文件系统体系结构 和 虚拟文件