深入理解記憶體映射mmap

来源:https://www.cnblogs.com/linhaostudy/archive/2019/03/31/10632082.html
-Advertisement-
Play Games

記憶體映射mmap是Linux內核的一個重要機制,它和虛擬記憶體管理以及文件IO都有直接的關係,這篇細說一下mmap的一些要點。 修改(2015 11 12):Linux的虛擬記憶體管理是基於mmap來實現的。vm_area_struct是在mmap的時候創建的,vm_area_strcut代表了一段連續 ...


記憶體映射mmap是Linux內核的一個重要機制,它和虛擬記憶體管理以及文件IO都有直接的關係,這篇細說一下mmap的一些要點。

修改(2015-11-12):Linux的虛擬記憶體管理是基於mmap來實現的。vm_area_struct是在mmap的時候創建的,vm_area_strcut代表了一段連續的虛擬地址,這些虛擬地址相應地映射到一個後備文件或者一個匿名文件的虛擬頁。一個vm_area_struct映射到一組連續的頁表項。頁表項又指向物理記憶體page,這樣就把一個文件和物理記憶體頁相映射。

來理解一下虛擬地址映射的過程:拿到一個虛擬地址,根據已有的vm_area_struct看這個虛擬地址是否屬於某個vm_area_struct

  • 如果沒有匹配到,就報段錯誤,訪問了一個沒有分配的虛擬地址。
  • 如果匹配到了vm_area_struct,根據虛擬地址和頁表的映射關係,找到對應的頁表項PTE,如果PTE沒有分配,就報一個缺頁異常,去載入相應的文件數據到物理記憶體,如果PTE分配,就去相應的物理頁的偏移位置讀取數據

所以虛擬頁的三種狀態的實際含義如下:

  • 未分配虛擬頁,指的是沒有使用mmap建立vm_area_struct,所以也就沒有對應到具體的頁表項
  • 已分配虛擬頁,未映射到物理頁,指的是已經使用了mmap建立的vm_area_struct,可以映射到對應的頁表項,但是頁表項沒有指向具體的物理頁
  • 已分配虛擬頁,已映射到物理頁,指的是已經使用了mmap建立的vm_area_struct,可以映射到對應的頁表項,並且頁表項指向具體的物理頁

mmap要麼映射到一個後備文件,要麼映射到一個匿名文件。操作系統分配物理記憶體時實際用到了匿名文件的mmap。

mmap和虛擬記憶體管理

先來看看Linux內核的用戶進程虛擬記憶體管理。內核定義了mm_struct結構來表示一個用戶進程的虛擬記憶體地址空間。

  1. start_code, end_code指定了進程的代碼段的邊界,start_data, end_data指定了進程數據段的邊界。在ELF二進位文件映射到虛擬記憶體地址空間後,這幾個長度就不會再改變

  2. start_brk, brk指定了堆的邊界。start_brk表示堆的起始地址,在進程整個生命周期不會改變,brk表示堆的結束位置,會隨著堆的長度改變而改變

  3. stack_top指定了棧的起始位置,一般是

  4. task_size指定了用戶進程地址空間的長度,也就是用戶進程地址空間的頂部邊界

  5. mmap_base指定了用戶進程虛擬地址空間中用作記憶體映射部分的地址的基地址,這個位置不是隨機的,通常是TASK_SIZE / 3位置處

image

這裡各個區域的地址都是用戶進程的虛擬地址,用戶進程使用虛擬地址和頁表結構來訪問記憶體

  1. 首先根據所在區域的虛擬地址轉換成對應的頁表數組的數組項索引,找到頁表索引最後定為到PTE中保存的物理記憶體頁的頁號,加上虛擬地址低12位的offset來確定一個唯一的物理記憶體地址

  2. 如果物理記憶體地址所在的頁存在,就返回該物理地址存放的內容。如果不存在就觸發缺頁異常。虛擬記憶體管理採用按需分配 + 缺頁異常機制來管理頁表項和分配對應的物理記憶體頁。當一個虛擬地址對應的頁表項不存在時,先創建頁表結構,再分配物理記憶體頁,再修改頁表

image

進程的mm_struct除了包含虛擬記憶體地址空間佈局的信息,還包含了虛擬記憶體區域vm_area_struct的信息。虛擬記憶體區域vm_area_struct是內核管理用戶進程虛擬地址空間的方式,實際上數據段,文本段,共用庫這些都是通過vm_area_struct來管理的

image

vm_area_struct由兩種組織形式,一種是單鏈表,包含了所有創建的vm_area_struct實例,一種是紅黑樹結構,加速區域的查找。這兩個數據結構都是面向同一份vm_area_struct實例,只是組織形式不同。

再考慮一下vm_area_struct和頁表的關係,vm_area_struct本質上是一段用戶進程的虛擬地址,而我們知道虛擬地址和頁表數組的索引是對應的,頁表數組的最後一級PTE數組的數組項存放著物理記憶體頁的頁號,這樣就建立了虛擬記憶體地址到物理記憶體地址的對應關係。

  1. 有一種情況是先有虛擬地址,再由訪問虛擬地址引起缺頁異常去載入物理記憶體,再更新頁表建立虛擬地址,頁表,物理記憶體三者的聯繫

  2. 有一種情況(mmap)是先從設備載入文件,建立address_space,頁緩存(物理記憶體),再創建vm_area_struct結構,更新頁表,返回虛擬地址。

image

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實例的信息

image

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

image

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

mmap的4種類型

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

  1. 後備文件的共用映射,多個進程的vm_area_struct指向同一個物理記憶體區域,一個進程對文件內容的修改,會被其他進程可見。對文件內容的修改會被寫回到後備文件。

  2. 後備文件的私有映射,多個進程的vm_area_struct指向同一個物理記憶體區域,採用寫時拷貝的方式,當一個進程對文件內容做修改,不會被其他進程看到。另外對文件內的修改也不會被寫回到後備文件。當記憶體不夠需要進行頁回收時,私有映射的頁被交換到交換區。一般用在載入共用代碼庫

  3. 匿名文件的共用映射,內核創建一個初始都是0的物理記憶體區域,然後多個進程的vm_area_struct指向這個共用的物理記憶體區域,對該區域內容的修改對所有進程可見。匿名文件在頁回收時被交換到交換區

  4. 匿名文件的私有映射,內核創建一個初始都是0的物理記憶體區域,對該區域內容的修改只對創建者進程可見。匿名文件在頁回收時被交換到交換區。malloc()底層是用了匿名文件的私有映射來分配大塊記憶體。

比如下麵的例子,mmap會涉及到物理記憶體的變化(載入後備文件到頁緩存,或者分配都是0的物理記憶體塊),創建vm_area_struct虛擬記憶體區域實例,更新頁表


// 後備文件的共用映射
fd = open("/home/xxx/a.txt", O_RDWR)
addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARD, fd, 0)
 
// 匿名文件的私有映射
fd = open("/dev/zero", O_RDWR)
addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)

記憶體映射的用途很多,比如

  1. 後備文件的共用映射可以用作記憶體映射IO來對大文件進行操作,比普通IO減少一次複製。需要註意的是記憶體映射IO涉及到內核的很多操作,比如vm_area_struct的創建,頁表的修改等等,比普通IO的操作更複雜。小文件的讀寫使用普通IO更合適

  2. 後備文件的私有映射可以用作共用庫二進位文件代碼段,數據段的載入

  3. 匿名文件的共用映射可以用作fork時讓父子進程共用匿名映射分配的記憶體

  4. 匿名文件的私有映射可以用作進程的私有記憶體分配

內核對堆空間的管理

實際上從內核的管理用戶進程虛擬地址空間的角度來說,記憶體映射是管理用戶進程虛擬地址空間的主要手段,通過建立vm_area_struct來分配虛擬記憶體區域。內核對堆空間的分配主要是brk系統調用,brk系統調用本質上也是利用了匿名文件私有映射,分配初始化0的物理記憶體頁,建立vm_area_struct,然後更新頁表結構。brk系統調用分配的記憶體最小單位是頁,需要按頁對齊,從start_brk位置向上擴展,也就是說從內核的角度來說,每次對堆空間的分配最小就是一頁,更細粒度的位元組記憶體空間分配由C語言標準庫實現的。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在C#中可以使用MemoryStream類、BinaryFormatter類等來操作圖片,將圖片讀取到二進位數據流中,最終轉成二進位數據流進行調用,詳細的實現如下方法所示。 備註:原文轉載自C#將圖片轉換為二進位流調用_IT技術小趣屋。 ...
  • C#操作MySQL的類 C#操作MySQL的類 [C#cāozuò MySQL de lèi] C# operation MySQL class C#操作MySQL的類 [C#cāozuò MySQL de lèi] C# operation MySQL class C#操作MySQL的類 [C#c ...
  • 內核編譯丶sed丶awk Linux:單內核 模塊化:動態 /lib/modules lsmod,modinfo,modprobe,insmod,,modprobe -r ,rmmod dep文件:模塊的依賴關係 sysbols:符號映射 depmod:用來生成模塊依賴關係 kernel文件夾下 a ...
  • 第一章 操作系統的定義:控制和管理電腦軟硬體資源、合理組織電腦工作流程,以方便用戶使用電腦的程式的集合。 操作系統的目標:方便性、有效性、可擴充性、開放性 操作系統的五個基本功能:存儲管理、處理機管理、設備管理、文件管理、用戶介面 操作系統的發展過程: 未配置操作系統的電腦系統:人工操作方式 ...
  • 1. 創建shell腳本 2. 給shell腳本添加執行許可權 3. 給腳本添加定時任務 crontab文件的說明: 用戶創建的crontab文件中,每一行都代表一項定時任務,每行的每個欄位代表一項設置,它的格式每行共分為六個欄位,前五段是時間設定欄位,第六段是要執行的命令欄位。 格式如下:minut ...
  • 樹狀目錄結構: 以下是對這些目錄的解釋: /bin:bin是Binary的縮寫, 這個目錄存放著最經常使用的命令。 /boot:這裡存放的是啟動Linux時使用的一些核心文件,包括一些連接文件以及鏡像文件。 /dev :dev是Device(設備)的縮寫, 該目錄下存放的是Linux的外部設備,在L ...
  • 基本信息記錄我在使用win10過程中遇到的一些問題我所使用的兩個win10系統Win10 企業版 1607(家裡電腦)Win10 專業版 1806(公司電腦)win10 開啟Sets請問您在開始-設置-系統-多任務中是否看到Sets的相關設置。如果沒有請您嘗試將時區和地區設置成美國後查看有沒有相關設... ...
  • 我們在前面的章節中已經詳細介紹了堆在進程中的地址空間是如何分佈的,對於程式來說,堆空間只是程式向操作系統申請划出來的一大塊地址空間。而程式在通過 malloc申請 記憶體空間時的大小卻是不一定的,從數個字到數個GB都是有可能的。於是我們必須將堆空間管理起來,將它分塊地按照用戶需求出售給最終的程式,並且 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...