[轉]mmap和madvise的使用


1.    madvise的簡介
     
     madvise可以設置內存的分配方式或者說是分配的細節方式。具體參見linux man madvise。
#include <sys/types.h>
#include <sys/mman.h>
int madvise(caddr_t addr, size_t len, int advice);
madvise() 函數提供了以下標志,這些標志影響 lgroup 之間線程內存的分配方式:

MADV_ACCESS_DEFAULT
此標志將指定范圍的內核預期訪問模式重置為缺省設置。

MADV_ACCESS_LWP
此標志通知內核,移近指定地址范圍的下一個 LWP 就是將要訪問此范圍次數最多的 LWP。內核將相應地為此范圍和 LWP 分配內存和其他資源。

MADV_ACCESS_MANY
此標志建議內核,許多進程或 LWP 將在系統內隨機訪問指定的地址范圍。內核將相應地為此范圍分配內存和其他資源。

madvise() 函數可以返回以下值:

EAGAIN
指定地址范圍(從 addr 到 addr+len)中的部分或所有映射均已鎖定進行 I/O 操作。

EINVAL
addr 參數的值不是sysconf(3C) 返回的頁面大小的倍數,指定地址范圍的長度小於或等於零或者建議無效。

EIO
讀寫文件系統時發生 I/O 錯誤。

ENOMEM
指定地址范圍中的地址不在進程的有效地址空間范圍內,或者指定地址范圍中的地址指定了一個或多個未映射的頁面。

ESTALE
NFS 文件句柄過時。

  

2.  mmap和madvise的使用

     mmap的作用是將硬盤文件的內容映射到內存中,采用閉鏈哈希建立的索引文件非常適合利用mmap的方式進行內存映射,利用mmap返回的地址指針就是索引文件在內存中的首地址,這樣我們就可以放心大膽的訪問這些內容了。

      使用過mmap映射文件的同學會發現一個問題,search程序訪問對應的內存映射時,處理query的時間會有latecny會陡升,究其原因是因為mmap只是建立了一個邏輯地址,linux的內存分配測試都是采用延遲分配的形式,也就是只有你真正去訪問時采用分配物理內存頁,並與邏輯地址建立映射,這也就是我們常說的缺頁中斷。  

      缺頁中斷分為兩類,一種是內存缺頁中斷,這種的代表是malloc,利用malloc分配的內存只有在程序訪問到得時候,內存才會分配;另外就是硬盤缺頁中斷,這種中斷的代表就是mmap,利用mmap映射后的只是邏輯地址,當我們的程序訪問時,內核會將硬盤中的文件內容讀進物理內存頁中,這里我們就會明白為什么mmap之后,訪問內存中的數據延時會陡增。

       出現問題解決問題,上述情況出現的原因本質上是mmap映射文件之后,實際並沒有加載到內存中,要解決這個文件,需要我們進行索引的預加載,這里就會引出本文講到的另一函數madvise,這個函數會傳入一個地址指針,已經是一個區間長度,madvise會向內核提供一個針對於於地址區間的I/O的建議,內核可能會采納這個建議,會做一些預讀的操作。例如MADV_SEQUENTIAL這個就表明順序預讀。

      如果感覺這樣還不給力,可以采用read操作,從mmap文件的首地址開始到最終位置,順序的讀取一遍,這樣可以完全保證mmap后的數據全部load到內存中。

3. 舉個栗子

  測試時同時運行30個播放程序讀取30個不同的mpg文件,程序起初運行畫面播放非常流暢,幾分鍾過后,內存剩下15MB左右時,mmap()就開始不停 的進行頁面置換,將新的數據讀入內存,老的數據置換出去,這時的磁盤利用率不到1%,但CPU耗在iowait上的時間卻有90%多 。

各位大俠我該怎么辦,如果不用內存映射還有沒有其他的辦法處理大文件???

開個4G的swap分區掛上去試試看 
或者,把文件分段mmap(),如100M的文件做10次mmap(),並且要求在播放1段完前做好下段准備工作

你掛mmap不釋放怎么行. 
你mmap一個大文件, 要在這個大文件播放完后才能釋放. 
要把大文件分小

你可以分段映射試試看,比如一次映射2M,並跟蹤程序在這個映射內的使用情況,如果這此映射的數據快用完時,就提前映射下一段.在上一段用完后,就釋放掉其映射.

今天又做了一下測試,先映射整個文件,在使用過程中一段一段釋放,還是阻塞在IOWait,另外掛載4G交換分區的方法也試了,效果更差 
      在國外論壇上看了些IOWait的東東,有很多都說紅帽企業版+Xeons處理器+磁盤陣列容易發生IOWait,和我現在的配置一模一樣,用單獨的大文件拷貝就能測得出來,明天下個新內核編譯一下試試看

問題原因: 
    調用mmap()時內核只是建立了邏輯地址到物理地址的映射表,並沒有映射任何數據到內存。 
    在你要訪問數據時內核會檢查數據所在分頁是否在內存,如果不在,則發出一次缺頁中斷,linux默認分頁為4K,可以想象讀一個將近2G的電影文件要發生多少次中斷,I can't bear it!!! 

解決辦法: 
    將madvise()和mmap()搭配起來使用,在使用數據前告訴內核這一段數據我要用,將其一次讀入內存,現在程序可以並發150個數據流了,每秒最高可讀70MB數據


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM