當前位置:編程學習大全網 - 源碼下載 - [原創] 深入剖析mmap原理 - 從三個關鍵問題說起

[原創] 深入剖析mmap原理 - 從三個關鍵問題說起

對於mmap,您是否能從原理上解析以下三個問題:

要解決這些疑問,可能還需要在操作系統層面多了解。本文將嘗試通過這些問題深入剖析,希望通過這篇文章,能使大家對mmap有較深入的認識,也能在存儲引擎的設計中,有所參考。

最近在研發分布式日誌存儲系統,這是壹個基於Raft協議的自研分布式日誌存儲系統,Logstore則是底層存儲引擎。

Logstore中,使用mmap對數據文件進行讀寫。Logstore的存儲結構簡化如下圖:

Logstore使用了Segments Files + Index Files的方式存儲Log,Segment File是存儲主體,用於存儲Log數據,使用定長的方式,默認每個512M,Index File主要用於Segment File的內容檢索。

Logstore使用mmap的方式讀寫Segment File,Segments Files的個數,主要取決於磁盤空間或者業務需求,壹般情況下,Logstore會存儲1T~5T的數據。

我們先看看什麽是mmap。

在<<深入理解計算機系統>>這本書中,mmap定義為:Linux通過將壹個虛擬內存區域與壹個磁盤上的對象(object)關聯起來,以初始化這個虛擬內存區域的內容,這個過程稱為內存映射(memory mapping)。

在Logstore中,mapping的對象是普通文件(Segment File)。

我們先來簡單看壹下mapping壹個文件,mmap做了什麽事情。如下圖所示:

假設我們mmap的文件是FileA,在調用mmap之後,會在進程的虛擬內存分配地址空間,創建映射關系。

這裏值得註意的是, mmap只是在虛擬內存分配了地址空間 ,舉個例子,假設上述的FileA是2G大小

在mmap之後,查看mmap所在進程的maps描述,可以看到

由上可以看到,在mmap之後,進程的地址空間7f35eea8d000-7f366ea8d000被分配,並且map到FileA,7f366ea8d000減去7f35eea8d000,剛好是2147483648(ps: 這裏是整個文件做mapping)

在Linux中,VM系統通過將虛擬內存分割為稱作虛擬頁(Virtual Page,VP)大小固定的塊來處理磁盤(較低層)與上層數據的傳輸,壹般情況下,每個頁的大小默認是4096字節。同樣的,物理內存也被分割為物理頁(Physical Page,PP),也為4096字節。

上述例子,在mmap之後,如下圖:

在mmap之後,並沒有在將文件內容加載到物理頁上,只上在虛擬內存中分配了地址空間。當進程在訪問這段地址時(通過mmap在寫入或讀取時FileA),若虛擬內存對應的page沒有在物理內存中緩存,則產生"缺頁",由內核的缺頁異常處理程序處理,將文件對應內容,以頁為單位(4096)加載到物理內存,註意是只加載缺頁,但也會受操作系統壹些調度策略影響,加載的比所需的多,這裏就不展開了。

(PS: 再具體壹些,進程在訪問7f35eea8d000這個進程虛擬地址時,MMU通過查找頁表,發現對應內容未緩存在物理內存中,則產生"缺頁")

缺頁處理後,如下圖:

我認為從原理上,mmap有兩種類型,壹種是有backend,壹種是沒有backend。

這種模式將普通文件做memory mapping(非MAP_ANONYMOUS),所以在mmap系統調用時,需要傳入文件的fd。這種模式常見的有兩個常用的方式,MAP_SHARED與MAP_PRIVATE,但它們的行為卻不相同。

1) MAP_SHARED

這個方式我認為可以從兩個角度去看:

2) MAP_PRIVATE

這是壹個copy-on-write的映射方式。雖然他也是有backend的,但在寫入數據時,他會在物理內存copy壹份數據出來(以頁為單位),而且這些數據是不會被回寫到文件的。這裏就要註意,因為更新的數據是壹個副本,而且不會被回寫,這就意味著如果程序運行時不主動釋放,若更新的數據超過可用物理內存+swap space,就會遇到OOM Killer。

無backend通常是MAP_ANONYMOUS,就是將壹個區域映射到壹個匿名文件,匿名文件是由內核創建的。因為沒有backend,寫入/更新的數據之後,若不主動釋放,這些占用的物理內存是不能被釋放的,同樣會出現OOM Killer。

到這裏,這個問題就比較好解析了。我們可以將此問題分離為:

-- 虛擬內存是否會出問題:

回到上述的"mmap在進程虛擬內存做了什麽",我們知道mmap會在進程的虛擬內存中分配地址空間,比如1G的文件,則分配1G的連續地址空間。那究竟可以maping多少呢?在64位操作系統,尋址範圍是2^64 ,除去壹些內核、進程數據等地址段之外,基本上可以認為可以mapping無限大的數據(不太嚴謹的說法)。

-- 物理內存是否會出問題

回到上述"mmap的分類",對於有backend的mmap,而且是能回寫到文件的,映射比內存+swap空間大是沒有問題的。但無法回寫到文件的,需要非常註意,主動釋放。

MAP_NORESERVE是mmap的壹個參數,MAN的說明是"Do not reserve swap space for this mapping. When swap space is reserved, one has the guarantee that it is possible to modify the mapping."。

我們做個測試:

場景A:物理內存+swap space: 16G,映射文件30G,使用壹個進程進行mmap,成功後映射後持續寫入數據

場景B:物理內存+swap space: 16G,映射文件15G,使用兩個進程進行mmap,成功後映射後持續寫入數據

從上述測試可以看出,從現象上看,NORESERVE是繞過mmap的校驗,讓其可以mmap成功。但其實在RESERVE的情況下(序列4),從測試結果看,也沒有保障。

mmap的性能經常與系統調用(write/read)做對比。

我們將讀寫分開看,先嘗試從原理上分析兩者的差異,然後再通過測試驗證。

我們先來簡單講講write系統調用寫文件的過程:

再來簡單講講使用mmap時,寫入文件流程:

系統調用會對性能有影響,那麽從理論上分析:

下面我們對兩者進行性能測試:

場景:對2G的文件進行順序寫入(go語言編寫)

每次寫入大小 | mmap 耗時 | write 耗時

--------------- | ------- | -------- | --------

| 1 byte | 22.14s | >300s

| 100 bytes | 2.84s | 22.86s

| 512 bytes | 2.51s | 5.43s

| 1024 bytes | 2.48s | 3.48s

| 2048 bytes | 2.47s | 2.34s

| 4096 bytes | 2.48s | 1.74s

| 8192 bytes | 2.45s | 1.67s

| 10240 bytes | 2.49s | 1.65s

可以看到mmap在100byte寫入時已經基本達到最大寫入性能,而write調用需要在4096(也就是壹個page size)時,才能達到最大寫入性能。

從測試結果可以看出,在寫小數據時,mmap會比write調用快,但在寫大數據時,反而沒那麽快(但不太確認是否go的slice copy的性能問題,沒時間去測C了)。

測試結果與理論推導吻合。

我們還是來簡單分析read調用與mmap的流程:

從圖中可以看出,read調用確實比mmap多壹次copy。因為read調用,進程是無法直接訪問kernel space的,所以在read系統調用返回前,內核需要將數據從內核復制到進程指定的buffer。但mmap之後,進程可以直接訪問mmap的數據(page cache)。

從原理上看,read性能會比mmap慢。

接下來實測壹下性能區別:

場景:對2G的文件進行順序讀取(go語言編寫)

(ps: 為了避免磁盤對測試的影響,我讓2G文件都緩存在pagecache中)

每次讀取大小 | mmap 耗時 | write 耗時

--------------- | ------- | -------- | --------

| 1 byte | 8215.4ms | > 300s

| 100 bytes | 86.4ms | 8100.9ms

| 512 bytes | 16.14ms | 1851.45ms

| 1024 bytes | 8.11ms | 992.71ms

| 2048 bytes | 4.09ms | 636.85ms

| 4096 bytes | 2.07ms | 558.10ms

| 8192 bytes | 1.06ms | 444.83ms

| 10240 bytes | 867.88?s | 475.28ms

由上可以看出,在read上面,mmap比write的性能差別還是很大的。測試結果與理論推導吻合。

對mmap的深入了解,能幫助我們在設計存儲系統時,更好地進行決策。

比如,假設需要設計壹個底層的數據結構是B+ Tree,node操作以Page單位的單機存儲引擎,根據上述推論,寫入使用系統調用,而讀取使用mmap,可以達到最優的性能。而LMDB就是如此實現的。

  • 上一篇:求問手遊年終跨服龍虎賽常見問題匯總。
  • 下一篇:公司app制作需要多少錢?
  • copyright 2024編程學習大全網