新聞中心
面試和工作中可能會用到mmap內(nèi)存映射,今天就來聊一聊
1、mmap基礎概念
- mmap 即 memory map,也就是內(nèi)存映射;
- mmap 是一種內(nèi)存映射文件的方法,即將一個文件或者其它對象映射到進程的地址空間,實現(xiàn)文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關系;
- 實現(xiàn)這樣的映射關系后,進程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫臟頁面到對應的文件磁盤上;
- 即完成了對文件的操作而不必再調用 read、write 等系統(tǒng)調用函數(shù)。相反,內(nèi)核空間對這段區(qū)域的修改也直接反映用戶空間,從而可以實現(xiàn)不同進程間的文件共享;
mmap 具有如下的特點:
- mmap 向應用程序提供的內(nèi)存訪問接口是內(nèi)存地址連續(xù)的,但是對應的磁盤文件的 block 可以不是地址連續(xù)的;
- mmap 提供的內(nèi)存空間是虛擬空間(虛擬內(nèi)存),而不是物理空間(物理內(nèi)存),因此完全可以分配遠遠大于物理內(nèi)存大小的虛擬空間(例如 16G 內(nèi)存主機分配 1000G 的 mmap 內(nèi)存空間);
- mmap 負責映射文件邏輯上一段連續(xù)的數(shù)據(jù)(物理上可以不連續(xù)存儲)映射為連續(xù)內(nèi)存,而這里的文件可以是磁盤文件、驅動假造出的文件(例如 DMA 技術)以及設備;
- mmap 由操作系統(tǒng)負責管理,對同一個文件地址的映射將被所有線程共享,操作系統(tǒng)確保線程安全以及線程可見性;
- mmap 的設計很有啟發(fā)性?;诖疟P的讀寫單位是 block(一般大小為 4KB),而基于內(nèi)存的讀寫單位是地址(雖然內(nèi)存的管理與分配單位是 4KB)。換言之,CPU 進行一次磁盤讀寫操作涉及的數(shù)據(jù)量至少是 4KB,但是進行一次內(nèi)存操作涉及的數(shù)據(jù)量是基于地址的,也就是通常的 64bit(64 位操作系統(tǒng))。mmap 下進程可以采用指針的方式進行讀寫操作,這是值得注意的;
2、mmap內(nèi)存映射原理
mmap內(nèi)存映射的實現(xiàn)過程,總的來說可以分為三個階段:
2.1進程啟動映射過程,并在虛擬地址空間中為映射創(chuàng)建虛擬映射區(qū)域;
- 進程在用戶空間調用庫函數(shù)mmap,原型:void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
- 在當前進程的虛擬地址空間中,尋找一段空閑的滿足要求的連續(xù)的虛擬地址
- 為此虛擬區(qū)分配一個vm_area_struct結構,接著對這個結構的各個域進行了初始化
- 將新建的虛擬區(qū)結構(vm_area_struct)插入進程的虛擬地址區(qū)域鏈表或樹中
2.2調用內(nèi)核空間的系統(tǒng)調用函數(shù)mmap(不同于用戶空間函數(shù)),實現(xiàn)文件物理地址和進程虛擬地址的一一映射關系
- 為映射分配了新的虛擬地址區(qū)域后,通過待映射的文件指針,在文件描述符表中找到對應的文件描述符,通過文件描述符,鏈接到內(nèi)核“已打開文件集”中該文件的文件結構體(struct file),每個文件結構體維護著和這個已打開文件相關各項信息;
- 通過該文件的文件結構體,鏈接到file_operations模塊,調用內(nèi)核函數(shù)mmap,其原型為:int mmap(struct file *filp, struct vm_area_struct *vma),不同于用戶空間庫函數(shù);
- 內(nèi)核mmap函數(shù)通過虛擬文件系統(tǒng)inode模塊定位到文件磁盤物理地址;
- 通過remap_pfn_range函數(shù)建立頁表,即實現(xiàn)了文件地址和虛擬地址區(qū)域的映射關系。此時,這片虛擬地址并沒有任何數(shù)據(jù)關聯(lián)到主存中;
2.3進程發(fā)起對這片映射空間的訪問,引發(fā)缺頁異常,實現(xiàn)文件內(nèi)容到物理內(nèi)存(主存)的拷貝
- 前兩個階段僅在于創(chuàng)建虛擬區(qū)間并完成地址映射,但是并沒有將任何文件數(shù)據(jù)的拷貝至主存。真正的文件讀取是當進程發(fā)起讀或寫操作時;
- 進程的讀或寫操作訪問虛擬地址空間這一段映射地址,通過查詢頁表,發(fā)現(xiàn)這一段地址并不在物理頁面上。因為目前只建立了地址映射,真正的硬盤數(shù)據(jù)還沒有拷貝到內(nèi)存中,因此引發(fā)缺頁異常;
- 缺頁異常進行一系列判斷,確定無非法操作后,內(nèi)核發(fā)起請求調頁過程。
- 調頁過程先在交換緩存空間(swap cache)中尋找需要訪問的內(nèi)存頁,如果沒有則調用nopage函數(shù)把所缺的頁從磁盤裝入到主存中;
- 1之后進程即可對這片主存進行讀或者寫的操作,如果寫操作改變了其內(nèi)容,一定時間后系統(tǒng)會自動回寫臟頁面到對應磁盤地址,也即完成了寫入到文件的過程;
- 修改過的臟頁面并不會立即更新回文件中,而是有一段時間的延遲,可以調用msync()來強制同步, 這樣所寫的內(nèi)容就能立即保存到文件里了;
3、mmap函數(shù)實例分析
3.1mmap函數(shù)的原型
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
參數(shù)addr:指定映射的起始地址,通常設為NULL,由內(nèi)核來分配
參數(shù)length:代表將文件中映射到內(nèi)存的部分的長度。
參數(shù)prot:映射區(qū)域的保護方式??梢詾橐韵聨追N方式的組合:
- PROT_EXEC 映射區(qū)域可被執(zhí)行
- PROT_READ 映射區(qū)域可被讀取
- PROT_WRITE 映射區(qū)域可被寫入
- PROT_NONE 映射區(qū)域不能存取
參數(shù)flags:映射區(qū)的特性標志位,常用的兩個選項是:
- MAP_SHARD:寫入映射區(qū)的數(shù)據(jù)會復制回文件,且運行其他映射文件的進程共享
- MAP_PRIVATE:對映射區(qū)的寫入操作會產(chǎn)生一個映射區(qū)的復制,對此區(qū)域的修改不會寫會原文件
參數(shù)fd:要映射到內(nèi)存中的文件描述符,有open函數(shù)打開文件時返回的值。
參數(shù)offset:文件映射的偏移量,通常設置為0,代表從文件最前方開始對應,offset必須是分頁大小的整數(shù)倍。
函數(shù)返回值:實際分配的內(nèi)存的起始地址
3.2munmap函數(shù)
與mmap函數(shù)成對使用的是munmap函數(shù),它是用來解除映射的函數(shù);
int munmap(void *start, size_t length)
- 參數(shù)start:映射的起始地址
- 參數(shù)length:文件中映射到內(nèi)存的部分的長度
- 返回值:解除成功返回0,失敗返回-1
3.3實例
下面是一個mmap使用的實例代碼
//打開文件
fd = open("testdata",O_RDWR);
//創(chuàng)建mmap
start = (char *)mmap(NULL,128,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
//讀取文件
strcpy(buf,start);
printf("%s\n",buf);
//寫入文件
strcpy(start,"Write to file!\n");
munmap(start,128);
close(fd);
這段代碼實現(xiàn)了將測試文件testdata打開,并用mmap函數(shù)將文件映射到虛擬內(nèi)存中,通過指針start對文件進行讀寫。在終端中可看到由文件讀取的數(shù)據(jù)。程序結束后,可以查看testdata文件,來查看寫入的數(shù)據(jù)
文章題目:Mmap內(nèi)存映射的原理以及實現(xiàn)
網(wǎng)頁路徑:http://m.fisionsoft.com.cn/article/dhoipio.html


咨詢
建站咨詢

