無MMU記憶體對映支援¶
在無MMU條件下(例如uClinux環境中),核心對記憶體對映的支援是有限的。從使用者空間的角度來看,記憶體對映與mmap()系統呼叫、shmat()呼叫和execve()系統呼叫一起使用。從核心的角度來看,execve()對映實際上由binfmt驅動程式執行,這些驅動程式會回撥mmap()例程來完成實際工作。
記憶體對映行為還涉及fork()、vfork()、clone()和ptrace()的工作方式。在uClinux下沒有fork(),而clone()必須提供CLONE_VM標誌。
MMU和無MMU情況下的行為相似,但並不相同;在後者的情況下,其限制也更多
匿名對映,MAP_PRIVATE
在MMU情況下:由任意頁面支援的VM區域;跨fork進行寫時複製。
在無MMU情況下:由任意連續頁面執行支援的VM區域。
匿名對映,MAP_SHARED
它們與私有對映的行為非常相似,不同之處在於它們在MMU情況下跨fork()或clone()(不帶CLONE_VM)共享。由於無MMU情況不支援這些,因此其行為與MAP_PRIVATE相同。
檔案,MAP_PRIVATE,PROT_READ / PROT_EXEC,!PROT_WRITE
在MMU情況下:由從檔案讀取的頁面支援的VM區域;對底層檔案的更改反映在對映中;跨fork複製。
在無MMU情況下
如果存在,核心將重用對同一檔案的同一段的現有對映(如果其具有相容的許可權),即使此對映是由另一個程序建立的。
如果可能,檔案對映將直接在支援裝置上進行,如果支援裝置具有NOMMU_MAP_DIRECT功能和適當的對映保護功能。Ramfs、romfs、cramfs和mtd都可能允許這樣做。
如果支援裝置不能或不允許直接共享,但確實具有NOMMU_MAP_COPY功能,那麼檔案的相應部分將被讀取到一段連續的記憶體中,並且超出EOF的任何多餘空間將被清除
對檔案的寫入不影響對映;對對映的寫入在其他程序中可見(沒有MMU保護),但不應該發生。
檔案,MAP_PRIVATE,PROT_READ / PROT_EXEC,PROT_WRITE
在MMU情況下:類似於非PROT_WRITE情況,不同之處在於相關頁面在實際寫入發生之前被複制。從那時起,對該頁面底層檔案的寫入不再反映到對映的後端頁面中。該頁面則由交換空間支援。
在無MMU情況下:與非PROT_WRITE情況非常相似,但始終進行復制且從不共享。
常規檔案/塊裝置,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE
在MMU情況下:由從檔案讀取的頁面支援的VM區域;對頁面的更改寫回檔案;對檔案的寫入反映到支援對映的頁面中;跨fork共享。
在無MMU情況下:不支援。
記憶體支援的常規檔案,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE
在MMU情況下:與普通常規檔案相同。
在無MMU情況下:提供記憶體支援檔案的檔案系統(如ramfs或tmpfs)可能會選擇透過提供連續的頁面序列進行對映來遵守開放、截斷、mmap序列。在這種情況下,共享可寫記憶體對映將成為可能。其工作方式與MMU情況相同。如果檔案系統不提供任何此類支援,則對映請求將被拒絕。
記憶體支援的塊裝置,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE
在MMU情況下:與普通常規檔案相同。
在無MMU情況下:與記憶體支援的常規檔案相同,但塊裝置必須能夠提供連續的頁面執行,而無需呼叫截斷。如果ramdisk驅動程式預先分配所有記憶體作為連續陣列,則可以實現這一點。
記憶體支援的字元裝置,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE
在MMU情況下:與普通常規檔案相同。
在無MMU情況下:如果字元裝置驅動程式提供可以直接訪問的記憶體或準記憶體,則可以選擇透過提供對底層裝置的直接訪問來遵守mmap()。此類示例包括幀緩衝器和快閃記憶體裝置。如果驅動程式不提供任何此類支援,則對映請求將被拒絕。
無MMU MMAP的進一步說明¶
對檔案的私有對映請求可能會返回一個非頁面對齊的緩衝區。這是因為可能發生XIP,並且資料在後端儲存中可能未進行分頁對齊。
匿名對映的請求將始終是頁面對齊的。如果可能,請求的大小應該是2的冪,否則一些空間可能會被浪費,因為核心必須分配2的冪的粒度,但只有在適當配置時才會丟棄多餘的部分,因為這會影響碎片化。
根據Linux手冊頁(2.22版或更高版本),匿名對映請求分配的記憶體通常會在返回之前由核心清除。
在MMU情況下,這可以以合理的效能實現,因為區域由虛擬頁面支援,內容僅在特定頁面發生寫入時才對映到已清除的物理頁面(在此之前,頁面實際上對映到全域性零頁面,可以從中讀取)。這分散了初始化頁面內容所需的時間 - 取決於對映的寫入使用情況。
然而,在無MMU情況下,匿名對映由物理頁面支援,並且整個對映在分配時就被清除。這可能在使用者空間malloc()期間導致顯著延遲,因為C庫執行匿名對映,然後核心對整個對映進行memset操作。
但是,對於不需要預清除的記憶體(例如malloc()返回的記憶體),mmap()可以接受MAP_UNINITIALIZED標誌,以指示核心在返回記憶體之前不需要清除它。請注意,必須啟用CONFIG_MMAP_ALLOW_UNINITIALIZED才能允許這樣做,否則該標誌將被忽略。
uClibc使用此功能來加速malloc(),而ELF-FDPIC binfmt使用此功能來分配brk和棧區域。
在無MMU模式下,可以透過/proc/maps檢視系統上所有私有複製和匿名對映的列表。
在無MMU模式下,可以透過/proc/<pid>/maps檢視程序使用的所有對映的列表。
提供MAP_FIXED或請求特定對映地址將導致錯誤。
私有對映的檔案通常必須由驅動程式或檔案系統提供讀取方法,以便如果mmap()選擇不直接對映後端裝置,則可以將內容讀取到分配的記憶體中。如果它們不提供,則將導致錯誤。這種情況最常出現在字元裝置檔案、管道、FIFO和套接字中。
Futexes¶
如果架構支援,Futexes 在 NOMMU 模式下受支援。如果傳遞給 Futex 系統呼叫的地址超出了程序建立的對映範圍,或者地址所在的對映不支援 Futexes(例如 I/O 字元裝置對映),則會報錯。
無MMU mremap¶
mremap()函式部分受支援。它可能會改變對映的大小,並且如果指定了MREMAP_MAYMOVE,並且對映的新大小超過了當前被對映所引用記憶體佔用的slab物件的大小,或者可以使用更小的slab物件,則可能會移動它[1]。
MREMAP_FIXED不受支援,但如果地址沒有改變且物件不需要移動,則會忽略它。
共享對映不能移動。可共享對映也不能移動,即使它們當前未共享。
mremap()函式必須提供與先前對映物件的基地址和大小完全匹配的引數。它不能用於在現有對映中建立空洞、移動現有對映的一部分或調整對映的一部分大小。它必須作用於完整的對映。
調整頁面修剪行為¶
NOMMU mmap在執行分配時會自動向上舍入到最接近的2的冪次的頁數。這可能會對記憶體碎片化產生不利影響,因此,它被配置為可調節的。預設行為是積極地修剪分配並將任何多餘的頁面返回給頁面分配器。為了更好地控制碎片化,此行為可以完全停用,或者提高到更高的頁面水印以開始修剪。
頁面修剪行為可透過sysctl vm.nr_trim_pages 進行配置。