在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

一文詳細了解mmap內存映射

xCb1_yikoulinux ? 來源:一口Linux ? 作者:一口Linux ? 2022-05-05 15:32 ? 次閱讀

mmap基礎概念

mmap是一種內存映射的方法,這一功能可以用在文件的處理上,即將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系。在編程時可以使某個磁盤文件的內容看起來像是內存中的一個數組。如果文件由記錄組成,而這些記錄又能夠用結構體來描述的話,可以通過訪問結構體來更新文件的內容。

實現這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內存,而系統會自動回寫到頁面到對應的文件磁盤上,即完成了對文件的操作而不必再調用read,write等系統調用函數。內核空間對這段區域的修改也直接反映用戶空間,從而可以實現不同進程間的文件共享。如圖所示:

09d572bc-cb60-11ec-bce3-dac502259ad0.jpg

進程的虛擬地址空間,由多個虛擬內存區域構成。虛擬內存區域是進程的虛擬地址空間中的一個同質區間,即具有同樣特性的連續地址范圍。上圖中所示的text數據段(代碼段)、初始數據段、BSS數據段、堆、棧和內存映射,都是一個獨立的虛擬內存區域。而為內存映射服務的地址空間處在堆棧之間的空余部分。

內核為系統中的每個進程維護一個單獨的任務結構(task_struct)。任務結構中的元素包含或者指向內核運行該進程所需的所有信息(PID、指向用戶棧的指針、可執行目標文件的名字、程序計數器等)。Linux內核使用vm_area_struct結構來表示一個獨立的虛擬內存區域,由于每個不同質的虛擬內存區域功能和內部機制都不同,因此一個進程使用多個vm_area_struct結構來分別表示不同類型的虛擬內存區域。各個vm_area_struct結構使用鏈表或者樹形結構鏈接,方便進程快速訪問,如下圖所示:

09f4e8e0-cb60-11ec-bce3-dac502259ad0.jpg

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

mm_struct:描述了虛擬內存的當前狀態。pgd指向一級頁表的基址(當內核運行這個進程時,
pgd會被存放在CR3控制寄存器,也就是頁表基址寄存器中),mmap指向一個vm_area_structs
的鏈表,其中每個vm_area_structs都描述了當前虛擬地址空間的一個區域。
vm_starts    指向這個區域的起始處。
vm_end       指向這個區域的結束處。
vm_prot      描述這個區域內包含的所有頁的讀寫許可權限。
vm_flags     描述這個區域內的頁面是與其他進程共享的,還是這個進程私有的以及一些其他信息。
vm_next      指向鏈表的下一個區域結構。

mmap內存映射原理

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

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

1. 進程在用戶空間調用庫函數mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

mmap 示例代碼

mmap (內存映射)函數的作用是建立一段可以被兩個或更多個程序讀寫的內存。一個程序對它所做出的修改可以被其他程序看見。這要通過使用帶有特殊權限集的虛擬內存段來實現。對這類虛擬內存段的讀寫會使操作系統去讀寫磁盤文件中與之對應的部分。mmap 函數創建一個指向一段內存區域的指針,該內存區域與可以通過一個打開的文件描述符訪問的文件的內容相關聯。mmap 函數原型如下:

#include 
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

可以通過傳遞 off 參數來改變共享內存段訪問的文件中數據的起始偏移值。打開的文件描述符由 fildes 參數給出。可以訪問的數據量(即內存段的長度)由 len 參數設置。

可以通過 addr 參數來請求使用某個特定的內存地址。如果它的取值是零,結果指針就將自動分配。這是推薦的做法,否則會降低程序的可移植性,因為不同系統上的可用地址范圍是不一樣的。

prot 參數用于設置內存段的訪問權限。它是下列常數值的按位或的結果:

PROT_READ       內存段可讀。
PROT_WRITE      內存段可寫。
PROT_EXEC       內存段可執行。
PROT_NONE       內存段不能被訪問。

flags 參數控制程序對該內存段的改變所造成的影響:

0a0b46d0-cb60-11ec-bce3-dac502259ad0.jpg

msync 函數的作用是:把在該內存段的某個部分或整段中的修改寫回到被映射的文件中(或者從被映射文件里讀出)。

#include 
int msync(void *addr, size_t len, int flags);

內存段需要修改的部分由作為參數傳遞過來的起始地址 addr 和長度 len 確定。flags 參數控制著執行修改的具體方式,可以使用的選項如下:

MS_ASYNC                    采用異步寫方式
MS_SYNC                     采用同步寫方式
MS_INVALIDATE               從文件中讀回數據

munmap 函數的作用是釋放內存段:

#include 
int munmap(void *addr, size_t length);

示例代碼:

(1) 定義一個 RECORD 數據結構,然后創建出 NRECORDS 每個記錄,每個記錄中保存著它們各自的編號。然后把這些記錄都追加到文件 records.dat 里去。

(2) 接著,把第 43 記錄中的整數值由 43 修改為 143,并把它寫入第 43 條記錄中的字符串。

(3) 把這些記錄映射到內存中,然后訪問第 43 條記錄,把它的整數值修改為 243 (同時更新該記錄中的字符串),使用的還是內存映射的方法。

可以將上述 (2) (3) 分別編寫程序驗證結果。

#include 
#include 
#include 
#include 
#include 
typedef struct{
 int integer;
 char string[24];
}RECORD;
#define NRECORDS (100)
int main()
{
RECORD record, *mapped;
 int i, f;
FILE *fp;
fp = fopen("records.dat", "w+");
 for( i = 0; i < NRECORDS; i++)
{
record.integer = i;
 sprintf(record.string, "[RECORD-%d]", i);
 fwrite(&record, sizeof(record), 1, fp);
}
 fclose(fp);
fp = fopen("records.dat", "r+");
 fseek(fp, 43 * sizeof(record), SEEK_SET);
 fread(&record, sizeof(record), 1, fp);
record.integer = 143;
 sprintf(record.string, "[RECORD-%d]", record.integer);
 fseek(fp, 43 * sizeof(record), SEEK_SET);
 fwrite(&record, sizeof(record), 1, fp);
 fclose(fp);
f = open("records.dat", O_RDWR);
mapped = (RECORD*)mmap(0, NRECORDS * sizeof(record), 
                        PROT_READ | PROT_WRITE, MAP_SHARED, f, 0);
 printf("f:[%d]
", f);
 //open是系統調用,返回文件描述符。fopen是庫函數,返回指針。
mapped[43].integer = 243;
 sprintf(mapped[43].string, "[RECORD-%d]", mapped[43].integer);
 msync((void *) mapped, NRECORDS * sizeof(record), MS_ASYNC);
 munmap((void *)mapped, NRECORDS * sizeof(record));
 close(f);
 return 0;
}

mmap 和常規文件操作的區別

使用系統調用,函數的調用過程:

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

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

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

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

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

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

總而言之,常規文件操作需要從磁盤到頁緩存再到用戶主存的兩次數據拷貝。而mmap操控文件,只需要從磁盤到用戶主存的一次數據拷貝過程。說白了,mmap的關鍵點是實現了用戶空間和內核空間的數據直接交互而省去了空間不同、數據不通的繁瑣過程。因此mmap效率更高。

由上文討論可知,mmap優點共有一下幾點:

1. 對文件的讀取操作跨過了頁緩存,減少了數據的拷貝次數,用內存讀寫取代I/O讀寫,提高了文件讀取效率。

2. 實現了用戶空間和內核空間的高效交互方式。兩空間的各自修改操作可以直接反映在映射的區域內,從而被對方空間及時捕捉。

3. 提供進程間共享內存及相互通信的方式。不管是父子進程還是無親緣關系的進程,都可以將自身用戶空間映射到同一個文件或匿名映射到同一片區域。從而通過各自對映射區域的改動,達到進程間通信和進程間共享的目的。

同時,如果進程A和進程B都映射了區域C,當A第一次讀取C時通過缺頁從磁盤復制文件頁到內存中;但當B再讀C的相同頁面時,雖然也會產生缺頁異常,但是不再需要從磁盤中復制文件過來,而可直接使用已經保存在內存中的文件數據。

4. 可用于實現高效的大規模數據傳輸。內存空間不足,是制約大數據操作的一個方面,解決方案往往是借助硬盤空間協助操作,補充內存的不足。但是進一步會造成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好的解決。換句話說,但凡是需要用磁盤空間代替內存的時候,mmap都可以發揮其功效。

mmap 使用的細節

1. 使用mmap需要注意的一個關鍵點是,mmap映射區域大小必須是物理頁大小(page_size)的整倍數(32位系統中通常是4k字節)。原因是,內存的最小粒度是頁,而進程虛擬地址空間和內存的映射也是以頁為單位。為了匹配內存的操作,mmap從磁盤到虛擬地址空間的映射也必須是頁。

2. 內核可以跟蹤被內存映射的底層對象(文件)的大小,進程可以合法的訪問在當前文件大小以內又在內存映射區以內的那些字節。也就是說,如果文件的大小一直在擴張,只要在映射區域范圍內的數據,進程都可以合法得到,這和映射建立時文件的大小無關。

3. 映射建立之后,即使文件關閉,映射依然存在。因為映射的是磁盤的地址,不是文件本身,和文件句柄無關。同時可用于進程間通信的有效地址空間不完全受限于被映射文件的大小,因為是按頁映射。

作者:極致Linux

原文標題:都22年了,還有人不懂mmap內存映射詳解?收藏保留

文章出處:【微信公眾號:一口Linux】歡迎添加關注!文章轉載請注明出處。

審核編輯:湯梓紅

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 原理
    +關注

    關注

    4

    文章

    550

    瀏覽量

    44898
  • 代碼
    +關注

    關注

    30

    文章

    4788

    瀏覽量

    68612
  • 內存映射
    +關注

    關注

    0

    文章

    14

    瀏覽量

    7416

原文標題:都22年了,還有人不懂mmap內存映射詳解?收藏保留

文章出處:【微信號:yikoulinux,微信公眾號:一口Linux】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    拆解mmap內存映射的本質!

    mmap 內存映射里所謂的內存其實指的是虛擬內存,在調用 mmap 進行匿名
    的頭像 發表于 01-24 14:30 ?1746次閱讀
    拆解<b class='flag-5'>mmap</b><b class='flag-5'>內存</b><b class='flag-5'>映射</b>的本質!

    Linux的mmap文件內存映射機制

    Linux的mmap文件內存映射機制在講述文件映射的概念時, 不可避免的要牽涉到虛存(SVR 4的VM). 實際上, 文件映射是虛存的中心概
    發表于 03-08 09:54

    dma_alloc_coherent申請內存的訪問速度,請問有什么辦法能加快訪問mmap的DMA內存

    使用dma_alloc_coherent申請了內存,然后使用mmap映射到用戶空間。然后,我用千兆網卡(CPSW驅動)進行發送(UDP方式),測量到的速度僅有12.5MB/s。 我
    發表于 06-04 07:47

    mmap()函數映射內存中出現bus error的錯誤

    在2440開發板上將副BMP圖片顯示到LCD上(不用GUI),我的做法是將BMP圖片用mmap()函數映射內存中,在將其讀到Framebuffer設備中顯示(frambuffer有
    發表于 02-25 12:42

    詳細了解下ups的相關計算

    關于ups方面的計算有很多,ups無論是接空開,還是連接電纜,以及選擇電池,都可能需要計算它的電流或功率等,那么今天我們來詳細了解下ups的相關計算。、UPS電源及電流、高頻ups與工頻ups
    發表于 11-16 09:08

    詳細了解下STM32F1的具體電路參數

    最近筆者在使用STM32時,需要詳細了解下F1的具體電路參數。于是查看其官方數據手冊,結果記錄如下。絕對最大額度值般工作條件表中的FT指5V 耐壓。可以在引腳定義表格中看到。I/O端口特性(邏輯電平)在最后
    發表于 01-18 07:07

    在arm里怎樣實現mmap編寫驅動和應用共享內存

    ② 確定屬性:是否使用 cache、buffer③ 建立映射關系在file_operation里面建立mmap進行mmap的函數編寫這樣在驅動程序的內存空間就被建立了
    發表于 05-17 09:59

    mmap作為Linux內存管理的關鍵之

    mmap個文件或者其它對象映射內存。文件被映射到多個頁上,如果文件的大小不是所有頁的大小之和,最后
    發表于 04-28 17:16 ?616次閱讀
    <b class='flag-5'>mmap</b>作為Linux<b class='flag-5'>內存</b>管理的關鍵之<b class='flag-5'>一</b>

    linux drivers中的mmap實現

    將設備驅動內核空間的內存映射到用戶空間里,可以通過用戶空間中的mmap系統調用代替系統調用write和read。目的是提高讀寫效率。
    發表于 05-15 10:31 ?1604次閱讀

    Linux的mmap文件內存映射機制

    VM_LOCKED(映射區不被換出)方式映射, 則發出缺頁請求, 把映射頁面讀入內存中.  munmap(void * start, size_t length):  該調用可以看作是
    發表于 04-02 14:35 ?442次閱讀

    詳細了解實時反射內存網絡

    實時反射內存網絡是種為使多個獨立的計算機進行數據共享而特別設計的共享內存系統。實時反射內存網絡在系統中的每個節點放置個共享
    的頭像 發表于 04-02 12:38 ?4532次閱讀

    詳細了解OpenHarmony新圖形框架

    3月30日,OpenHarmony v3.1 Release版本正式發布了。此版本為大家帶來了全新的圖形框架,實現了UI框架顯示、多窗口、流暢動畫等基礎能力,夯實了OpenHarmony系統能力基座。下面就帶大家詳細了解新圖形框架。
    的頭像 發表于 04-27 13:21 ?2232次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b><b class='flag-5'>詳細了解</b>OpenHarmony新圖形框架

    mmap原理詳解

    句話概括mmap mmap的作用,在應用這層,是讓你把文件的某段,當作內存
    的頭像 發表于 11-09 14:59 ?710次閱讀
    <b class='flag-5'>mmap</b>原理詳解

    帶您詳細了解IEEE802.3bt(PoE++)的有關特點

    Hqst華強盛(盈盛電子)導讀:帶您詳細了解IEEE802.3bt(PoE++)的有關特點,讓我們對IEEE802.3bt(PoE++)協議有更具體的了解
    的頭像 發表于 01-04 11:26 ?2189次閱讀
    帶您<b class='flag-5'>一</b>起<b class='flag-5'>詳細了解</b>IEEE802.3bt(PoE++)的有關特點

    帶你詳細了解工業電腦

    扇設計、承受振動和惡劣環境的能力、輕松配置、全面的I/O選項、延長生命周期、耐用的組件。了解如何為您的應用選擇工業電腦對提高設施的生產力和效率至關重要。詳細了解
    的頭像 發表于 06-12 14:24 ?411次閱讀
    <b class='flag-5'>一</b><b class='flag-5'>文</b>帶你<b class='flag-5'>詳細了解</b>工業電腦
    主站蜘蛛池模板: 亚洲一区区| 亚洲一区二区三区麻豆| 亚洲国产欧美视频| 久久国产精品99精品国产987| 色视频在线看| 国产二区三区| 爱综合网| 又粗又大又猛又爽免费视频| 婷婷 综合网站| bt天堂在线www中文在线| 天天添天天操| 69pao强力打造免费高清| 色老头一区二区三区在线观看| www.色av.com| 黄色大视频| 亚洲色妞| 美女18黄| 免费无毒片在线观看| 国产成人无精品久久久久国语| 猫色网站| 精品乱人伦一区二区三区| 亚洲一区二区三区电影| 国产精品美女自在线观看免费| 国产精品午夜国产小视频| 男女做性无遮挡免费视频| 毛片毛片| japanese日本护士xx亚洲| 色综合 成人| 天天摸天天做天天爽天天弄| 福利片午夜| 福利片免费一区二区三区| 免费免播放器在线视频观看 | 四虎影院永久免费观看| 又粗又硬又爽又黄毛片| 欧美一卡2卡三卡4卡5卡免费观看 欧美一卡2卡三卡四卡五卡 | 色在线视频网站| 天天久久| 性欧美黑人xxxx| 五月天狠狠| 亚洲第二色| 天天骑天天射|