#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數據