mmap技術(shù)研究及應(yīng)用

一、簡介:

mmap是一種內(nèi)存映射文件的方法。即將一個文件或者其它對象映射到進程的地址空間,實現(xiàn)文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關(guān)系。實現(xiàn)這樣的映射關(guān)系后,進程就可以采用指針的方式讀寫操作這一段內(nèi)存,而系統(tǒng)會自動回寫臟頁面到對應(yīng)的文件磁盤上,即完成了對文件的操作而不必再調(diào)用read,write等系統(tǒng)調(diào)用函數(shù)。相反,內(nèi)核空間對這段區(qū)域的修改也直接反映用戶空間,從而可以實現(xiàn)不同進程間的文件共享

二、BIO(常規(guī)文件操作或傳統(tǒng)IO)和mmap區(qū)別:

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

image.png

三、優(yōu)缺點

優(yōu)點:(高性能,操作文件就像操作內(nèi)存一下,適合對較大文件的讀寫)
①對文件的讀寫操作跨國也頁緩存,減少數(shù)據(jù)的拷貝次數(shù),用內(nèi)存讀寫取代IO流讀寫,提高了文件讀寫效率(Andorid加載.dex文件也通過使用此技術(shù));
②實現(xiàn)用戶空間和內(nèi)核空間的高效交互方式;
③提供進程間共享內(nèi)存及相互通信的方式。不管是父子進程還是無親緣關(guān)系的進程,都可以將自身用戶空間映射到同一個文件或匿名映射到同一片區(qū)域。從而通過各自對映射區(qū)域的改動,達到進程間通信和進程間共享的目的。
④實現(xiàn)高效的大規(guī)模數(shù)據(jù)傳輸。內(nèi)存空間不足,是制約大數(shù)據(jù)操作的一個方面,解決方案往往是借助磁盤空間協(xié)助操作,補充內(nèi)存的不足。但是進一步會照成大量的文件I/O操作,極大影響效率。這個問題可以通過mmap映射很好解決,需要用磁盤空間替代內(nèi)存的時候,mmap都可以發(fā)揮其功效;

缺點:①文件如果很小,比如小于4K的,比如60bytes,由于在內(nèi)存當中的組織都是按頁組織的,將文件調(diào)入到內(nèi)存當中是一個頁4K,相當于4096-60=4036bytes的內(nèi)存空間浪費掉了;②文件無法完成拓展,因為mmap到內(nèi)存的時候,你所能操作的范圍就已經(jīng)確定了,無法增加文件長度。
使用場景:
①對同一塊區(qū)域頻繁讀寫操作;
②用戶日志、數(shù)據(jù)上報等,微信開源mars框架中的xlog模塊就是基于mmap特性實現(xiàn);
③跨進程同步的時候,mmap是個不錯的選擇,Android跨進程通信有自己獨有的Binder機制,內(nèi)部使用mmap實現(xiàn);
Java層面使用:MappedByteBuffer已經(jīng)封裝好
C++代碼實現(xiàn):mmap

四、內(nèi)存映射原理

  • 進程啟動映射過程,并在虛擬地址空間中為映射創(chuàng)建虛擬映射區(qū)域
  • 調(diào)用內(nèi)核空間的系統(tǒng)調(diào)用函數(shù)mmap(不同于用戶空間函數(shù)),實現(xiàn)文件物理地址和進程虛擬地址的一一映射關(guān)系
  • 進程發(fā)起對這片映射空間的訪問,引發(fā)缺頁異常,實現(xiàn)文件內(nèi)容到物理內(nèi)存(主存)的拷貝

五、mmap相關(guān)函數(shù)

①建立映射關(guān)系函數(shù):
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
成功執(zhí)行時,mmap()返回被映射區(qū)的指針地址。失敗時,mmap()返回MAP_FAILED[其值為(void *)-1]
②解除映射關(guān)系函數(shù):
int munmap( void * addr, size_t len )
成功執(zhí)行時,munmap()返回0。失敗時,munmap返回-1,error返回標志和mmap一致;
addr是調(diào)用mmap()時返回的地址,len是映射區(qū)的大??;
③實時同步寫入:
int msync( void *addr, size_t len, int flags )
一般說來,進程在映射空間的對共享內(nèi)容的改變并不直接寫回到磁盤文件中,往往在調(diào)用munmap()后才執(zhí)行該操作。
可以通過調(diào)用msync()實現(xiàn)磁盤上文件內(nèi)容與共享內(nèi)存區(qū)的內(nèi)容一致。

六、mmap使用細節(jié)

mmap映射區(qū)域大小必須是物理頁大?。╬age_size)的倍數(shù)(32位系統(tǒng)中通常是4k字節(jié)),原因是內(nèi)存的最小粒度是頁,而進程虛擬地址空間和內(nèi)存的映射也是以頁為單位;
②內(nèi)核可以跟蹤被內(nèi)存映射的底層對象(文件)的大小,進程可以合法的訪問在當前文件大小以內(nèi)又在內(nèi)存映射區(qū)以內(nèi)的那些字節(jié)。
③映射建立之后,即使文件關(guān)閉,映射依然存在。因為映射的是磁盤的地址,不是文件本身,和文件句柄無關(guān)。

七、使用:

①C++使用mmap讀寫文件:

//-------------------------------------------讀取文件----------------------------------
// 打開文件
int fd = open("input.txt", O_RDONLY);  
// 讀取文件長度
int len = lseek(fd,0,SEEK_END);  
// 建立內(nèi)存映射
char *addr = (char *) mmap(NULL, len, PROT_READ, MAP_PRIVATE,fd, 0);      
close(fd);
// data用于保存讀取的數(shù)據(jù)
char* data; 
// 復(fù)制過來
memcpy(data, addr, len);
// 解除映射
munmap(addr, len)

//-------------------------------------------寫入文件----------------------------------
//假設(shè)寫入的數(shù)據(jù)放在char* data中
int len = data.length();
// 打開文件
int fd=open("output.txt", O_RDWR|O_CREAT, 00777);
// lseek將文件指針往后移動file_size-1位
lseek(fd,len-1,SEEK_END);  
// 從指針處寫入一個空字符;mmap不能擴展文件長度,這里相當于預(yù)先給文件長度,準備一個空架子
write(fd, "", 1);
// 使用mmap函數(shù)建立內(nèi)存映射
char* addr = (char*)mmap(NULL, len, PROT_READ|PROT_WRITE,MAP_SHARED, fd, 0);
// 內(nèi)存映射建立好了,此時可以關(guān)閉文件了
close(fd);
// 把data復(fù)制到addr里
memcpy(addr, data, len);
// 解除映射
munmap(addr, len)

①Java使用NIO庫中的MappedByteBuffer實現(xiàn)mmap讀寫文件:

try {
           byte[] originalByte = "你好奧,我是好人".getBytes();
           RandomAccessFile raf = new RandomAccessFile(file, "rw");
           ////position映射文件的起始位置,size映射文件的大小
           MappedByteBuffer map = raf.getChannel().map(FileChannel.MapMode.READ_WRITE, 0, 1024);
           //寫入數(shù)據(jù)
           map.put(originalByte);

           byte[] newData= new byte[originalByte.length];
           //獲取數(shù)據(jù)
           map.get(newData);
           Log.d(TAG, "data:" + String.valueOf(newData));
       } catch (FileNotFoundException e) {
           e.printStackTrace();
       } catch (IOException e) {
           e.printStackTrace();
       }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Linux進程通信實現(xiàn)機制有很多,也有各自優(yōu)缺點和適用場景,關(guān)于她們之間的對比,等各種通信機制一一介紹后,再來一個...
    batbattle閱讀 4,224評論 3 13
  • ?通信是Android開發(fā)必不可少的一部分,不管是我們做應(yīng)用App開發(fā),還是Android系統(tǒng),都使用了大量的通信...
    子者不語閱讀 541評論 0 1
  • 1.目前很多博客說的五種模型都是從read角度來描述的。 2.我們也常會說Direct IO,或者其他文件IO。他...
    簡書徐小耳閱讀 1,722評論 0 5
  • C++虛函數(shù): 多態(tài): 靜態(tài)多態(tài)(重載)、動態(tài)多態(tài)(虛函數(shù)) 虛函數(shù) 虛函數(shù)表:編譯器為每個類創(chuàng)建了一個虛函數(shù)表...
    Supreme_DJK閱讀 986評論 0 0
  • 前言 看這篇文章之前需要知道一個概念 虛擬內(nèi)存系統(tǒng)通過將虛擬內(nèi)存分割為稱作虛擬頁(Virtual Page,VP)...
    叫我不矜持閱讀 64,445評論 7 29

友情鏈接更多精彩內(nèi)容