視訊連結: htt ps://v .qq.com/x/p age/c0963p6x4f3.html
mmap使用與性能優化
什麼是mmap?
mmap是一種記憶體映射檔案的方法,即将一個檔案或者其它對象映射到程序的位址空間,實作檔案磁盤位址和程序虛拟位址空間中一段虛拟位址的一一對映關系。
特點:實作這樣的映射關系後,程序就可以采用指針的方式讀寫操作這一段記憶體,即完成了對檔案的操作而不必再調用read,write等系統調用函數。
mmap函數介紹
函數原型:
void *mmap(void *adrr, size_t length, int prot, int flags, int fd, off_t offset);
傳回值:成功傳回建立的映射區首位址;失敗:MAP_FAILED宏
參數:
addr: 建立映射區的首位址,由Linux核心指定。使用時,直接傳遞NULL
length:欲建立映射區的大小
prot: 映射區權限(非檔案本身權限)PROT_READ、PROT_WRITE、PROT_READ|PROT_WRITE
flags:标志位參數(常用于設定更新實體區域、設定共享、建立匿名映射區)
MAP_SHARED: 會将映射區所做的操作反映到實體裝置(磁盤)上。
MAP_PRIVATE: 映射區所做的修改不會反映到實體裝置。
fd:用來建立映射區的檔案描述符
offset:映射檔案的偏移(4k的整數倍)(可以映射整個檔案也可以隻映射一部分)
對于更多詳細的介紹可以參考man手冊。
同malloc函數申請記憶體空間類似的,mmap建立的映射區在使用結束後也應調用類似free的函數來釋放。
int munmap(void *addr, size_t length); 成功:0;失敗:-1
mmap與read/write的性能比較
對一個存有n個整數的檔案進行讀取與加1寫入操作。改變n值,在read/write與mmap下分别操作,對比兩者性能。(給出兩者關鍵代碼,為篇幅省去錯誤判斷)
read/write核心代碼:
/*read/write*/gettimeofday( &tv1, NULL );fd = open( "mmap_test", O_RDWR);read( fd, (void *)array, sizeof(int)*MAX );gettimeofday(&tv2, NULL);printf( "Time of read: %dms\n", tv2.tv_usec-tv1.tv_usec );gettimeofday( &tv1, NULL );for( i=0; i ++array[ i ];write( fd, (void *)array, sizeof(int)*MAX );close( fd );gettimeofday( &tv2, NULL );printf( "Time of write: %dms\n", tv2.tv_usec-tv1.tv_usec );pause();
mmap核心代碼:
gettimeofday( &tv1, NULL );fd = open( "mmap_mmap", O_RDWR );array = mmap( NULL, sizeof(int)*MAX, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 );for( i=0; i ++array[ i ];munmap( array, sizeof(int)*MAX );msync( array, sizeof(int)*MAX, MS_SYNC );close( fd );gettimeofday( &tv2, NULL );printf( "Time of mmap: %dms\n", tv2.tv_usec-tv1.tv_usec );pause();
read/write執行結果:
mmap執行結果:
通過對比不難看出檔案越大,read/write耗時越長,是以,對于一些大檔案mmap效率更高。(這也可以聯想到malloc小于128kb用brk實作,大于用mmap實作)
為什麼mmap效率要高于read/write?
先簡單的回顧一下正常檔案系統操作(調用read/write等類函數)中,函數的調用過程:
1、程序發起讀檔案請求。
2、核心通過查找程序檔案符表,定位到核心已打開檔案集上的檔案資訊,進而找到此檔案的inode。
3、inode在address_space上查找要請求的檔案頁是否已經緩存在頁緩存中。如果存在,則直接傳回這片檔案頁的内容。
4、如果不存在,則通過inode定位到檔案磁盤位址,将資料從磁盤複制到頁緩存。之後再次發起讀頁面過程,進而将頁緩存中的資料發給使用者程序。
總結來說,正常檔案操作為了提高讀寫效率和保護磁盤,使用了頁緩存機制。這樣造成讀檔案時需要先将檔案頁從磁盤拷貝到頁緩存中,由于頁緩存處在核心空間,不能被使用者程序直接尋址,是以還需要将頁緩存中資料頁再次拷貝到記憶體對應的使用者空間中。這樣,通過了兩次資料拷貝過程,才能完成程序對檔案内容的擷取任務。寫操作也是一樣,待寫入的buffer在核心空間不能直接通路,必須要先拷貝至核心空間對應的主存,再寫回磁盤中(延遲寫回),也是需要兩次資料拷貝。
而使用mmap操作檔案中,建立新的虛拟記憶體區域和建立檔案磁盤位址和虛拟記憶體區域映射這兩步,沒有任何檔案拷貝操作。而之後通路資料時發現記憶體中并無資料而發起的缺頁異常過程,可以通過已經建立好的映射關系,隻使用一次資料拷貝,就從磁盤中将資料傳入記憶體的使用者空間中,供程序使用。
即正常檔案操作需要從磁盤到頁緩存再到使用者主存的兩次資料拷貝。而mmap操控檔案,隻需要從磁盤到使用者主存的一次資料拷貝過程。是以mmap效率更高。
mmap優點總結
1、對檔案的讀取操作跨過了頁緩存,減少了資料的拷貝次數,用記憶體讀寫取代I/O讀寫,提高了檔案讀取效率。
2、實作了使用者空間和核心空間的高效互動方式。兩空間的各自修改操作可以直接反映在映射的區域内,進而被對方空間及時捕捉。
3、提供程序間共享記憶體及互相通信的方式。不管是父子程序還是無親緣關系的程序,都可以将自身使用者空間映射到同一個檔案或匿名映射到同一片區域。進而通過各自對映射區域的改動,達到程序間通信和程序間共享的目的。
同時,如果程序A和程序B都映射了區域C,當A第一次讀取C時通過缺頁從磁盤複制檔案頁到記憶體中;但當B再讀C的相同頁面時,雖然也會産生缺頁異常,但是不再需要從磁盤中複制檔案過來,而可直接使用已經儲存在記憶體中的檔案資料。
4、可用于實作高效的大規模資料傳輸。記憶體空間不足,是制約大資料操作的一個方面,解決方案往往是借助硬碟空間協助操作,補充記憶體的不足。但是進一步會造成大量的檔案I/O操作,極大影響效率。這個問題可以通過mmap映射很好的解決。換句話說,但凡是需要用磁盤空間代替記憶體的時候,mmap都可以發揮其功效。
補充細節:mmap函數使用注意事項
1.建立映射區的過程中,隐含着一次對映射檔案的讀操作(即原檔案必須擁有讀權限)。
2.當MAP_SHARED時,要求:映射區的權限應 <=檔案打開的權限(出于對映射區的保護)。而MAP_PRIVATE則無所謂,因為mmap中的權限是對記憶體的限制。
3.映射區的釋放與檔案關閉無關。隻要映射建立成功,檔案可以立即關閉。
4.特别注意,當映射檔案大小為0時,不能建立映射區。是以:用于映射的檔案必須要有實際大小! mmap使用時常常會出現總線錯誤,通常是由于共享檔案存儲空間大小引起的。
5.munmap傳入的位址一定是mmap的傳回位址。堅決杜絕指針++操作。
6.檔案偏移量必須為4K的整數倍(0,1,2,3....)
7.mmap建立映射區出錯機率非常高,一定要檢查傳回值,確定映射區建立成功再進行後續操作。