linux 內存管理(15) - mmap

  • 瞭解mmap機制。

1.概述

  mmap 即地址的映射, 是一種內存映射文件的方法,將一個文件或者其它對象映射到進程的地址空間,實現文件磁盤地址和進程虛擬地址空間中一段虛擬地址的一一對映關係。mmap()系統調用使得進程之間通過映射同一個普通文件實現共享內存。普通文件被映射到進程地址空間後,進程可以向訪問普通內存一樣對文件進行訪問,不必再調用read(),write()等操作。

  Linux通過內存映像機制來提供用戶程序對內存直接訪問的能力。內存映像的意思是把內核中特定部分的內存空間映射到用戶級程序的內存空間去。也就是說,用戶空間和內核空間共享一塊相同的內存。這樣做的直觀效果顯而易見:內核在這塊地址內存儲變更的任何數據,用戶可以立即發現和使用,根本無須數據拷貝。舉個例子理解一下,使用mmap方式獲取磁盤上的文件信息,只需要將磁盤上的數據拷貝至那塊共享內存中去,用戶進程可以直接獲取到信息,而相對於傳統的write/read IO系統調用, 必須先把數據從磁盤拷貝至到內核緩衝區中(頁緩衝),然後再把數據拷貝至用戶進程中。兩者相比,mmap會少一次拷貝數據,這樣帶來的性能提升是巨大的。

  使用內存訪問來取代read()和write()系統調用能夠簡化一些應用程序的邏輯。在一些情況下,它能夠比使用傳統的I/O系統調用執行文件I/O這種做法提供更好的性能。原因是:

  • 正常的read()或write()需要兩次傳輸:一次是在文件和內核高速緩衝區之間,另一次是在高速緩衝區和用戶空間緩衝區之間。使用mmap()就不需要第二次傳輸了。對於輸入來講,一旦內核將相應的文件塊映射進內存之後,用戶進程就能夠使用這些數據了;對於輸出來講,用戶進程僅僅需要修改內核中的內容,然後可以依靠內核內存管理器來自動更新底層的文件。
  • 除了節省內核空間和用戶空間之間的一次傳輸之外,mmap()還能夠通過減少所需使用的內存來提升性能。當使用read()或write()時,數據將被保存在兩個緩衝區中:一個位於用戶空間,另個一位於內核空間。當使用mmap()時,內核空間和用戶空間會共享同一個緩衝區。此外,如果多個進程正在同一個文件上執行I/O,那麼它們通過使用mmap()就能夠共享同一個內核緩衝區,從而又能夠節省內存的消耗。

2.函數

void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset)

  • 描述:mmap()系統調用把fd文件描述符對象從offset位置開始(字節爲單位),映射到以addr爲起始地址的頁面。

2.1.參數解釋

  • start:映射區的開始地址,一般爲空,選址工作交給內核
  • length:映射區的長度
  • prot:期望的內存保護標誌, 有以下取值
    • PROT_NONE: 頁面不允許訪問
    • PROT_READ: 頁面可讀
    • PROT_WRITE: 頁面可寫
    • PROT_EXEC: 頁面可執行
      通常可寫意味着可讀,可讀意味着可執行,因此不應該完全依賴於flag決定讀寫模式
  • flags:自定映射對象的類型,映射選項以及對被映射對象的修改是否只對進程可見(copy on write)。
    • MAP_ANONYOUS: 匿名映射,不指定文件名
    • MAP_ANON: 和上一個是同義詞
    • MAP_FILE: 從普通文件映射而來(默認選型)
    • MAP_FIXED: 要求addr必須是頁面大小的整數倍,如果不是,調用失敗。不推薦使用
    • MAP_PRIVATE: 對被映射對象的修改盡進程自己可見(Copy on Write)
    • MAP_SHARED: 修改對其他進程都可見
  • fd:要被映射的文件描述符(可以是普通文件,管道,連接等),一般由open返回, -1表示匿名映射。
  • offset:被映射對象內容的起始地址

2.2.返回值

  一旦調用成功,mmap返回被映射區域的指針,進程可以對這個指針進行讀寫。根據flags指定的模式,這些讀寫有可能被其他進程覺察到,由此實現基於共享內存的進程通信。

2.3.使用mmap用於共享內存的兩種方式

  • 匿名映射:把fd置爲-1,指涉特殊文件,flags置爲MAP_ANON,適用於具有親緣關係的進程間通信
  • 有名映射:fd大於0,指涉普通文件,適用於任何進程間的通信。

2.4.msync(void* addr, size_t len, int flags)

  • 一般來說,進程對地址空間中共享內容的修改不會立即寫入磁盤,而是等待munmap的調用後才執行寫磁盤操作,而msync允許我們手工指定同步行爲的發生。

2.5.int munmap(void* addr, size_t len)

  • 解除進程空間某個地址(就是mmap返回的那個地址)和內核某個文件(就是mmap中的那個fd)之間的映射關係。

3.mmap系統調用

  mmap屬於系統調用,用戶控件間接通過swi指令觸發軟中斷,進入內核態(各種環境的切換),進入內核態之後,便可以調用內核函數進行處理。 調用流程如下:

  • mmap->sys_mmap2-> sys_mmap_pgoff -> ksys_mmap_pgoff-> vm_mmap_pgoff->do_mmap_pgoff

3.1.sys_mmap2
include/uapi/asm-generic/unistd.h:

#define __NR_mmap __NR3264_mmap
#define __NR_mmap2 __NR3264_mmap
645 #define __NR3264_mmap 222
646 __SC_3264(__NR3264_mmap, sys_mmap2, sys_mmap)

sys_mmap2:

 arch/arm/kernel/entry-common.S:
410 sys_mmap2:                                                                                               
411         str r5, [sp, #4]
412         b   sys_mmap_pgoff
413 ENDPROC(sys_mmap2)

3.2.sys_mmap_pgoff:

kernel/mm/mmap.c:
 1618 SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,
  1619         unsigned long, prot, unsigned long, flags,                                                    
  1620         unsigned long, fd, unsigned long, pgoff)
  1621 {
  1622     return ksys_mmap_pgoff(addr, len, prot, flags, fd, pgoff);
  1623 }

refer to

  • https://www.jianshu.com/p/755338d11865
  • https://www.jianshu.com/p/71c9b73d788e
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章