一文讀懂 Linux mmap

文章目錄

  • 1.簡介
  • 2.實現原理
  • 3.相關函數
  • 4.mmap和常規文件操作的區別
  • 5.作用
  • 參考文獻

1.簡介

mmap(memory map)即內存映射,用于將一個文件或設備映射到進程的地址空間。

實現這樣的映射關系后,進程虛擬地址空間中一段內存地址將與文件磁盤地址一一對應,進程就可以采用指針的方式讀寫操作這段內存,系統會自動回寫臟頁面到對應的文件磁盤上。
在這里插入圖片描述
上圖表示進程虛擬地址空間布局。從上圖可以看出,進程的虛擬地址空間分為多個區域,每個區域存放不同類型的數據。內存映射區域處在堆與棧之間。

Linux 內核使用 vm_area_struct 結構來表示一個獨立的虛擬內存區域,由于每個不同質的虛擬內存區域功能和內部機制都不同,因此一個進程使用多個 vm_area_struct 結構來分別表示不同類型的虛擬內存區域。各個 vm_area_struct 結構使用鏈表或者樹形結構鏈接,方便進程快速訪問,如下圖所示:

在這里插入圖片描述
vm_area_struct 結構中包含區域起始和終止地址以及其他相關信息,同時也包含一個 vm_ops 指針,其內部可引出所有針對這個區域可以使用的系統調用函數。這樣,進程對某一虛擬內存區域的任何操作需要用要的信息,都可以從 vm_area_struct 中獲得。mmap 函數就是要創建一個新的 vm_area_struct 結構,并將其與文件的物理磁盤地址相連。

2.實現原理

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

(1)進程啟動映射過程,并在虛擬地址空間中為映射創建虛擬映射區域。

  1. 進程在用戶空間調用庫函數 mmap(2)。

  2. 在當前進程的虛擬地址空間中,尋找一段空閑的滿足要求的連續的虛擬地址。

  3. 為此虛擬區分配一個vm_area_struct結構,接著對這個結構的各個域進行了初始化。

  4. 將新建的虛擬區結構(vm_area_struct)插入進程的虛擬地址區域鏈表或樹中。

(2)調用內核空間的系統調用函數 mmap(不同于用戶空間函數),實現文件物理地址和進程虛擬地址的一一映射關系。

  1. 為映射分配了新的虛擬地址區域后,通過待映射的文件指針,在文件描述符表中找到對應的文件描述符,通過文件描述符,鏈接到內核“已打開文件集”中該文件的文件結構體(struct file),每個文件結構體維護著和這個已打開文件相關各項信息。

  2. 通過該文件的文件結構體,鏈接到file_operations模塊,調用內核函數 mmap,其原型為 int mmap(struct file *filp, struct vm_area_struct *vma),不同于用戶空間庫函數。

  3. 內核 mmap 函數通過虛擬文件系統 inode 模塊定位到文件磁盤物理地址。

  4. 通過 remap_pfn_range 函數建立頁表,即實現了文件地址和虛擬地址區域的映射關系。此時,這片虛擬地址并沒有任何數據關聯到主存中。

(3)進程發起對這片映射空間的訪問,引發缺頁異常,實現文件內容到物理內存的拷貝。

前兩個階段僅在于創建虛擬區間并完成地址映射,但是并沒有將任何文件數據的拷貝至主存。真正的文件讀取是當進程發起讀或寫操作時。

  1. 進程的讀或寫操作訪問虛擬地址空間這一段映射地址,通過查詢頁表,發現這一段地址并不在物理頁面上。因為目前只建立了地址映射,真正的硬盤數據還沒有拷貝到內存中,因此引發缺頁異常。

  2. 缺頁異常進行一系列判斷,確定無非法操作后,內核發起請求調頁過程。

  3. 調頁過程先在交換緩存空間(swap cache)中尋找需要訪問的內存頁,如果沒有則調用 nopage 函數把所缺的頁從磁盤載入主存。

  4. 之后進程即可對這片主存進行讀寫,如果寫操作改變了其內容,一定時間后系統會自動回寫臟頁面到對應磁盤地址,也即完成了寫入到文件的過程。

注:修改過的臟頁面并不會立即更新回文件中,而是有一段時間的延遲,可以調用 msync(2) 來強制同步,這樣所寫的內容就能立即保存到文件里了。

3.相關函數

創建映射函數:

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

成功執行時,mmap() 返回被映射區的指針。失敗時,mmap() 返回 MAP_FAILED,其值為 (void *)-1,errno 被設為以下的某個值:

EACCES	訪問出錯
EAGAIN	文件已被鎖定,或者太多的內存已被鎖定
EBADF	不是有效的文件描述詞
EINVAL	一個或者多個參數無效
ENFILE	已達到系統對打開文件的限制
ENODEV	指定文件所在的文件系統不支持內存映射
ENOMEM	內存不足,或者進程已超出最大內存映射數量
EPERM	權能不足,操作不允許
ETXTBSY	已寫的方式打開文件,同時指定MAP_DENYWRITE標志
SIGSEGV	試著向只讀區寫入
SIGBUS	試著訪問不屬于進程的內存區

入參 addr 表示要映射到的內存區域的起始地址,通常用 NULL,表示由內核指定該內存地址。

length 表示映射區的長度,單位字節。

prot 參數描述了映射所需的內存保護(并且不得與文件的打開模式沖突)。 它是 PROT_NONE 或以下多個標志的按位或組合:

PROT_EXEC 頁面可以被執行
PROT_READ 頁面可以被讀取
PROT_WRITE 頁面可以被寫入
PROT_NONE 頁面不能被訪問

flags 指定映射對象的類型,映射選項和映射頁是否可以共享。它的值可以是一個或多個以下位的組合體:

MAP_FIXED //使用指定的映射起始地址,如果由start和len參數指定的內存區重疊于現存的映射空間,重疊部分將會被丟棄。如果指定的起始地址不可用,操作將會失敗。并且起始地址必須落在頁的邊界上。
MAP_SHARED //與其它所有映射這個對象的進程共享映射空間。對共享區的寫入,相當于輸出到文件。直到msync()或者munmap()被調用,文件實際上不會被更新。
MAP_PRIVATE //建立一個寫入時拷貝的私有映射。內存區域的寫入不會影響到原文件。這個標志和以上標志是互斥的,只能使用其中一個。
MAP_DENYWRITE //這個標志被忽略。
MAP_EXECUTABLE //同上
MAP_NORESERVE //不要為這個映射保留交換空間。當交換空間被保留,對映射區修改的可能會得到保證。當交換空間不被保留,同時內存不足,對映射區的修改會引起段違例信號。
MAP_LOCKED //鎖定映射區的頁面,從而防止頁面被交換出內存。
MAP_GROWSDOWN //用于堆棧,告訴內核VM系統,映射區可以向下擴展。
MAP_ANONYMOUS //匿名映射,映射區不與任何文件關聯。
MAP_ANON //MAP_ANONYMOUS 的別稱,不再被使用。
MAP_FILE //兼容標志,被忽略。
MAP_32BIT //將映射區放在進程地址空間的低2GB,MAP_FIXED指定時會被忽略。當前這個標志只在x86-64平臺上得到支持。
MAP_POPULATE //為文件映射通過預讀的方式準備好頁表。隨后對映射區的訪問不會被頁違例阻塞。
MAP_NONBLOCK //僅和MAP_POPULATE一起使用時才有意義。不執行預讀,只為已存在于內存中的頁面建立頁表入口。

fd 有效的文件描述詞。如果 MAP_ANONYMOUS 被設定,為了兼容問題,其值應為 -1。

offset 被映射對象內容的偏移。

解除映射函數:

#include <sys/mman.h>int munmap(void *addr, size_t length);

成功執行時,munmap()返回0。失敗時,munmap返回-1,errno 返回標志和 mmap 一致。

該調用在進程地址空間中解除一個映射關系,addr是調用mmap()時返回的地址,len是映射區的大小。

當映射關系解除后,對原來映射地址的訪問將導致段錯誤發生。

同步函數:

int msync(void *addr, size_t len, int flags)

一般說來,進程在映射空間的對共享內容的改變并不直接寫回到磁盤文件中,往往在調用 munmap() 后才執行該操作。

可以通過調用msync()實現磁盤上文件內容與共享內存區的內容一致。

4.mmap和常規文件操作的區別

我們首先簡單回顧一下常規文件系統操作(調用read/fread等類函數)中,函數的調用過程:

1.進程發起讀文件請求。

2.內核通過查找進程文件符表,定位到內核已打開文件集上的文件信息,從而找到此文件的 inode。

3.inode在address_space上查找要請求的文件頁是否已經緩存在頁緩存中。如果存在,則直接返回這片文件頁的內容。

4.如果不存在,則通過inode定位到文件磁盤地址,將數據從磁盤復制到頁緩存。之后再次發起讀頁面過程,進而將頁緩存中的數據發給用戶進程。

總結來說,常規文件操作為了提高讀寫效率和保護磁盤,使用了頁緩存機制。這樣造成讀文件時需要先將文件頁從磁盤拷貝到頁緩存中,由于頁緩存處在內核空間,不能被用戶進程直接尋址,所以還需要將頁緩存中數據頁再次拷貝到用戶空間內存。這樣,通過了兩次數據拷貝,才能完成進程對文件內容的獲取任務。寫操作也是一樣,待寫入的buffer在內核空間不能直接訪問,必須要先拷貝至內核空間內存,再寫回磁盤中(延遲寫回),也是需要兩次數據拷貝。

而使用 mmap 操作文件,創建新的虛擬內存區域和建立文件磁盤地址和虛擬內存區域映射這兩步,沒有任何文件拷貝操作。而之后訪問數據時發現內存中并無數據而發起的缺頁異常過程,可以通過已經建立好的映射關系,只使用一次數據拷貝,就從磁盤中將數據傳入內存的用戶空間中,供進程使用。

總而言之,常規文件操作需要從磁盤到頁緩存再到用戶主存的兩次數據拷貝。而 mmap 操作文件,只需要從磁盤到用戶主存的一次數據拷貝,效率更高。

5.作用

mmap 主要有兩點作用:

  • 文件映射,減少數據拷貝,提高 IO 效率。

將文件映射到進程的地址空間。這使得進程可以通過直接讀寫內存來訪問文件內容,而不必使用 read 和 write 等系統調用。對文件的讀寫跨過了內核頁緩存,減少數據拷貝次數,提高了文件讀寫效率。

  • 進程間通信。

不管是父子進程還是無親緣關系的進程,都可以將自身地址空間映射到同一個文件或匿名映射到同一片內存區域,從而通過各自對映射區域的改動,實現進程間通信。


參考文獻

認真分析mmap:是什么為什么怎么用- 胡瀟
Linux source code (v6.0) - Elixir Bootlin

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/160980.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/160980.shtml
英文地址,請注明出處:http://en.pswp.cn/news/160980.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TorchScript C++ 自定義運算符 cpucuda

參考 在 C 中注冊調度運算符 使用自定義 C 運算符擴展 TorchScript 環境&#xff1a; NVIDIA Driver Version : 545.23.08CUDA Version: 12.1Python Version: 3.11Pytorch Version: 2.1Cmake version : 3.18.1工作目錄&#xff1a;workspace/test 一、 C 自定義運算符 創建…

逐字節講解 Redis 持久化(RDB 和 AOF)的文件格式

前言 相信各位對 Redis 的這兩種持久化機制都不陌生&#xff0c;簡單來說&#xff0c;RDB 就是對數據的全量備份&#xff0c;AOF 則是增量備份&#xff0c;而從 4.0 版本開始引入了混合方式&#xff0c;以 7.2.3 版本為例&#xff0c;會生成三類文件&#xff1a;RDB、AOF 和記…

2014年5月28日 Go生態洞察:GopherCon 2014大會回顧

&#x1f337;&#x1f341; 博主貓頭虎&#xff08;&#x1f405;&#x1f43e;&#xff09;帶您 Go to New World?&#x1f341; &#x1f984; 博客首頁——&#x1f405;&#x1f43e;貓頭虎的博客&#x1f390; &#x1f433; 《面試題大全專欄》 &#x1f995; 文章圖文…

Java面試附答案:掌握關鍵技能,突破面試難題!

問題&#xff1a;什么是大O表示法&#xff1f;它在Java中的應用是什么&#xff1f; 回答&#xff1a; 大O表示法是一種用來衡量算法復雜度的方法&#xff0c;它描述了算法的時間復雜度和空間復雜度的增長速度。它使用符號O(n)來表示算法的漸進時間復雜度&#xff0c;其中n表示…

如何讓Python2與Python3共存

安裝 首先分別安裝Py2和Py3&#xff0c;我都安裝到C盤根目錄里了&#xff0c;然后分別將Py2和Py3都配置到系統環境變量中去&#xff1a;C:\Python36\Scripts\;C:\Python36\;C:\Python27\;C:\Python27\Scripts; 配置 修改兩個版本的可執行文件名字 驗證 重新配置一下pip …

Ubuntu刪除應用圖標

刪除用戶下的圖標 sudo nautilus ~/.local/share/applications刪除系統下的圖標 sudo nautilus /usr/share/applications

大數據-之LibrA數據庫系統告警處理(ALM-25500 KrbServer服務不可用)

告警解釋 系統按30秒周期性檢測組件KrbServer的服務狀態。當檢測到組件KrbServer服務異常時產生該告警。 當檢測到組件KrbServer服務恢復時告警恢復。 告警屬性 告警ID 告警級別 可自動清除 25500 致命 是 告警參數 參數名稱 參數含義 ServiceName 產生告警的服務…

解決MySQL中某列數據過長無法入庫的問題-Details:data too long for column `xxx` at row 1

問題描述&#xff1a; 我在將軌跡的經緯度轉換為字符串入庫時&#xff0c;遇到寫入問題 Mysql數據入庫報錯&#xff1a; Caused by:java.long.exception:寫入數據庫表失敗.Details:data too long for column xxx at row 1&#xff0c;我的xxx字段類型是string,在mysql庫表中…

加速CI構建,實現高效流水線——CloudBees CI發布工作區緩存功能

加速軟件交付流程能夠更快接觸到客戶&#xff0c;獲得競爭優勢。然而&#xff0c;識別這一過程中存在的瓶頸可能頗具挑戰。讓我們從審查構建和測試階段開始著手。例如&#xff0c;當CI作業執行時間較長時&#xff0c;它會延遲開發人員的反饋循環&#xff0c;從而可能導致發布延…

使用Python解析CAN總線

緣起 在新能源車輛的開發和維護中&#xff0c;經常需要對CAN總線數據進行分析。CANOE等總線軟件雖然方便&#xff0c;但功能有限&#xff0c;難以滿足數據分析的要求。Matlab的Vehicle Network Toolbox可以方便的進行數據解析和分析&#xff0c;它是閉源且收費的。因此&#x…

SpringBoot啟動順序

前言 每次有人問起SpringBoot的啟動順序是不是又來翻博客了&#xff1f;其實只需要稍微查看Spring的源碼即可 步驟 SpringBoot的入口org.springframework.boot.SpringApplication#run(String... args), 這里面實現了SpringBoot程序啟動的所有步驟 啟動事件的順序可以看監聽器…

uni-app 使用uni.getLocation獲取經緯度配合騰訊地圖api獲取當前地址

前言 最近在開發中需要根據經緯度獲取當前位置信息&#xff0c;傳遞給后端&#xff0c;用來回顯顯示當前位置 查閱uni-app文檔&#xff0c;發現uni.getLocation () 可以獲取到經緯度&#xff0c;但是在小程序環境沒有地址信息 思考怎么把經緯度換成地址&#xff0c;如果經緯度…

buildadmin+tp8表格操作(1)----表頭上方添加按鈕和自定義按鈕

buildAdmin 的表頭上添加一些按鈕&#xff0c;并實現功能 添加按鈕 <template><!-- buttons 屬性定義了 TableHeader 本身支持的頂部按鈕&#xff0c;僅需傳遞按鈕名即可 --><!-- 這里的框架自帶的 頂部按鈕 分別有 刷新 &#xff0c; 添加&#xff0c; 編輯&…

C++ 問題 怎么在C++11標準語法中調用C++20的類

一. 問題 在工作中,因為一個算法功能需要跟別的部門對接,他們提供了該算法的頭文件.h,靜態庫.lib,動態庫.dll。但是頭文件中使用了C++20才有的新特性,如#include等,而本地使用的vs2015開發環境,只支持C++11標準語法,這種情況下,該怎么把該算法集成到本地項目中呢? …

寫單元測試,沒你想得那么簡單!

前言 單元測試是什么我們就簡單介紹一下&#xff1a; 單元測試是針對程序模塊&#xff08;軟件設計的最小單位&#xff09;來進行正確性檢驗的測試工作。程序單元是應用的最小可測試部件。 接下來是本人對單元測試的理解和實踐。里面沒有廢話&#xff0c;希望每句話能說到你心…

YOLOv8改進實戰 | 更換主干網絡Backbone(六)之輕量化模型VanillaNet進階篇

前言 輕量化網絡設計是一種針對移動設備等資源受限環境的深度學習模型設計方法。下面是一些常見的輕量化網絡設計方法: 網絡剪枝:移除神經網絡中冗余的連接和參數,以達到模型壓縮和加速的目的。分組卷積:將卷積操作分解為若干個較小的卷積操作,并將它們分別作用于輸入的不…

每日一題(LeetCode)----鏈表--分隔鏈表

每日一題(LeetCode)----鏈表–分隔鏈表 1.題目&#xff08;86. 分隔鏈表&#xff09; 給你一個鏈表的頭節點 head 和一個特定值 x &#xff0c;請你對鏈表進行分隔&#xff0c;使得所有 小于 x 的節點都出現在 大于或等于 x 的節點之前。 你應當 保留 兩個分區中每個節點的初…

關于LORA的優勢以及常見應用產品領域

lora的優勢&#xff1a; 1 低功耗 2 傳輸距離遠 3 信號穿透性好 4 靈敏度高&#xff0c;適合可靠性要求高的領域 5 低成本 Lora 產品領域 &#xff1a; 一、智慧城市 1 智能停車&#xff1a;在較大的停車場&#xff0c;通過Lora技術&#xff0c;采集車位信息&#xff0…

問題解決:Ubuntu18.04下nvcc -V指令可用,/usr/local/下卻沒有cuda文件夾,原因分析及卸載方法

問題描述 今天要運行一個程序&#xff0c;需要CUDA版本高于10.0&#xff0c;我的電腦無法運行&#xff0c;于是開始檢查 首先使用nvidia-smi與nvcc -V指令 能夠看出來&#xff0c;當前顯卡驅動適合的CUDA版本為12.1&#xff0c;而本機安裝的版本是9.1.85&#xff0c;那么就需…

實驗7設計建模工具的使用(三)

二&#xff0c;實驗內容與步驟 1. 百度搜索1-2張狀態圖&#xff0c;請重新繪制它們&#xff0c;并回答以下問題&#xff1a; 1&#xff09;有哪些狀態&#xff1b; 2&#xff09;簡要描述該圖所表達的含義&#xff1b; 要求&#xff1a;所繪制的圖不得與本文中其它習題一樣…