無MMU記憶體對映支援

在無MMU條件下(例如uClinux環境中),核心對記憶體對映的支援是有限的。從使用者空間的角度來看,記憶體對映與mmap()系統呼叫、shmat()呼叫和execve()系統呼叫一起使用。從核心的角度來看,execve()對映實際上由binfmt驅動程式執行,這些驅動程式會回撥mmap()例程來完成實際工作。

記憶體對映行為還涉及fork()、vfork()、clone()和ptrace()的工作方式。在uClinux下沒有fork(),而clone()必須提供CLONE_VM標誌。

MMU和無MMU情況下的行為相似,但並不相同;在後者的情況下,其限制也更多

  1. 匿名對映,MAP_PRIVATE

    在MMU情況下:由任意頁面支援的VM區域;跨fork進行寫時複製。

    在無MMU情況下:由任意連續頁面執行支援的VM區域。

  2. 匿名對映,MAP_SHARED

    它們與私有對映的行為非常相似,不同之處在於它們在MMU情況下跨fork()或clone()(不帶CLONE_VM)共享。由於無MMU情況不支援這些,因此其行為與MAP_PRIVATE相同。

  3. 檔案,MAP_PRIVATE,PROT_READ / PROT_EXEC,!PROT_WRITE

    在MMU情況下:由從檔案讀取的頁面支援的VM區域;對底層檔案的更改反映在對映中;跨fork複製。

    在無MMU情況下

    • 如果存在,核心將重用對同一檔案的同一段的現有對映(如果其具有相容的許可權),即使此對映是由另一個程序建立的。

    • 如果可能,檔案對映將直接在支援裝置上進行,如果支援裝置具有NOMMU_MAP_DIRECT功能和適當的對映保護功能。Ramfs、romfs、cramfs和mtd都可能允許這樣做。

    • 如果支援裝置不能或不允許直接共享,但確實具有NOMMU_MAP_COPY功能,那麼檔案的相應部分將被讀取到一段連續的記憶體中,並且超出EOF的任何多餘空間將被清除

    • 對檔案的寫入不影響對映;對對映的寫入在其他程序中可見(沒有MMU保護),但不應該發生。

  4. 檔案,MAP_PRIVATE,PROT_READ / PROT_EXEC,PROT_WRITE

    在MMU情況下:類似於非PROT_WRITE情況,不同之處在於相關頁面在實際寫入發生之前被複制。從那時起,對該頁面底層檔案的寫入不再反映到對映的後端頁面中。該頁面則由交換空間支援。

    在無MMU情況下:與非PROT_WRITE情況非常相似,但始終進行復制且從不共享。

  5. 常規檔案/塊裝置,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE

    在MMU情況下:由從檔案讀取的頁面支援的VM區域;對頁面的更改寫回檔案;對檔案的寫入反映到支援對映的頁面中;跨fork共享。

    在無MMU情況下:不支援。

  6. 記憶體支援的常規檔案,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE

    在MMU情況下:與普通常規檔案相同。

    在無MMU情況下:提供記憶體支援檔案的檔案系統(如ramfs或tmpfs)可能會選擇透過提供連續的頁面序列進行對映來遵守開放、截斷、mmap序列。在這種情況下,共享可寫記憶體對映將成為可能。其工作方式與MMU情況相同。如果檔案系統不提供任何此類支援,則對映請求將被拒絕。

  7. 記憶體支援的塊裝置,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE

    在MMU情況下:與普通常規檔案相同。

    在無MMU情況下:與記憶體支援的常規檔案相同,但塊裝置必須能夠提供連續的頁面執行,而無需呼叫截斷。如果ramdisk驅動程式預先分配所有記憶體作為連續陣列,則可以實現這一點。

  8. 記憶體支援的字元裝置,MAP_SHARED,PROT_READ / PROT_EXEC / PROT_WRITE

    在MMU情況下:與普通常規檔案相同。

    在無MMU情況下:如果字元裝置驅動程式提供可以直接訪問的記憶體或準記憶體,則可以選擇透過提供對底層裝置的直接訪問來遵守mmap()。此類示例包括幀緩衝器和快閃記憶體裝置。如果驅動程式不提供任何此類支援,則對映請求將被拒絕。

無MMU MMAP的進一步說明

  1. 對檔案的私有對映請求可能會返回一個非頁面對齊的緩衝區。這是因為可能發生XIP,並且資料在後端儲存中可能未進行分頁對齊。

  2. 匿名對映的請求將始終是頁面對齊的。如果可能,請求的大小應該是2的冪,否則一些空間可能會被浪費,因為核心必須分配2的冪的粒度,但只有在適當配置時才會丟棄多餘的部分,因為這會影響碎片化。

  3. 根據Linux手冊頁(2.22版或更高版本),匿名對映請求分配的記憶體通常會在返回之前由核心清除。

    在MMU情況下,這可以以合理的效能實現,因為區域由虛擬頁面支援,內容僅在特定頁面發生寫入時才對映到已清除的物理頁面(在此之前,頁面實際上對映到全域性零頁面,可以從中讀取)。這分散了初始化頁面內容所需的時間 - 取決於對映的寫入使用情況。

    然而,在無MMU情況下,匿名對映由物理頁面支援,並且整個對映在分配時就被清除。這可能在使用者空間malloc()期間導致顯著延遲,因為C庫執行匿名對映,然後核心對整個對映進行memset操作。

    但是,對於不需要預清除的記憶體(例如malloc()返回的記憶體),mmap()可以接受MAP_UNINITIALIZED標誌,以指示核心在返回記憶體之前不需要清除它。請注意,必須啟用CONFIG_MMAP_ALLOW_UNINITIALIZED才能允許這樣做,否則該標誌將被忽略。

    uClibc使用此功能來加速malloc(),而ELF-FDPIC binfmt使用此功能來分配brk和棧區域。

  4. 在無MMU模式下,可以透過/proc/maps檢視系統上所有私有複製和匿名對映的列表。

  5. 在無MMU模式下,可以透過/proc/<pid>/maps檢視程序使用的所有對映的列表。

  6. 提供MAP_FIXED或請求特定對映地址將導致錯誤。

  7. 私有對映的檔案通常必須由驅動程式或檔案系統提供讀取方法,以便如果mmap()選擇不直接對映後端裝置,則可以將內容讀取到分配的記憶體中。如果它們不提供,則將導致錯誤。這種情況最常出現在字元裝置檔案、管道、FIFO和套接字中。

程序間共享記憶體

SYSV IPC SHM 共享記憶體和 POSIX 共享記憶體均在 NOMMU 模式下受支援。前者透過常規機制,後者透過在 ramfs 或 tmpfs 掛載點上建立檔案。

Futexes

如果架構支援,Futexes 在 NOMMU 模式下受支援。如果傳遞給 Futex 系統呼叫的地址超出了程序建立的對映範圍,或者地址所在的對映不支援 Futexes(例如 I/O 字元裝置對映),則會報錯。

無MMU mremap

mremap()函式部分受支援。它可能會改變對映的大小,並且如果指定了MREMAP_MAYMOVE,並且對映的新大小超過了當前被對映所引用記憶體佔用的slab物件的大小,或者可以使用更小的slab物件,則可能會移動它[1]

MREMAP_FIXED不受支援,但如果地址沒有改變且物件不需要移動,則會忽略它。

共享對映不能移動。可共享對映也不能移動,即使它們當前未共享。

mremap()函式必須提供與先前對映物件的基地址和大小完全匹配的引數。它不能用於在現有對映中建立空洞、移動現有對映的一部分或調整對映的一部分大小。它必須作用於完整的對映。

提供可共享字元裝置支援

為了提供可共享的字元裝置支援,驅動程式必須提供一個file->f_op->get_unmapped_area()操作。mmap()例程將呼叫此操作以獲取對映的建議地址。如果它不想遵守對映(因為對映太長、偏移量異常、標誌組合不受支援等等),它可能會返回錯誤。

驅動程式還應提供帶有功能集的後端裝置資訊,以指示此類裝置上允許的對映型別。預設假設為可讀寫、不可執行,並且只能直接共享(不能複製)。

file->f_op->mmap()操作將被呼叫以實際啟動對映。此時可以拒絕它。如果指定了NOMMU_MAP_COPY,返回ENOSYS錯誤將導致對映被複制。

當字元裝置上的最後一個對映被移除時,將呼叫vm_ops->close()例程。現有對映將被共享,無論部分還是全部,如果可能且無需通知驅動程式。

file->f_op->get_unmapped_area()操作也允許返回-ENOSYS。這意味著此操作只是不想處理它,儘管它有一個操作。例如,它可能會嘗試將呼叫定向到輔助驅動程式,而該驅動程式未能實現它。幀緩衝驅動程式的情況就是如此,它嘗試將呼叫定向到特定於裝置的驅動程式。在這種情況下,如果未指定NOMMU_MAP_COPY,對映請求將被拒絕,否則將對映一份副本。

重要

某些型別的裝置在特定模式下可能會呈現不同的外觀。快閃記憶體晶片就可能如此;例如,如果它們處於程式設計或擦除模式,您可能會在對映中看到狀態而非資料。

在這種情況下,必須小心,以免在驅動程式忙於控制裝置時,使用者空間看到顯示此類資訊的共享或私有對映。尤其要記住:在某些情況下,私有可執行對映可能仍然直接從裝置對映!

提供可共享記憶體支援的檔案支援

在記憶體支援的檔案上提供共享對映類似於提供對共享對映字元裝置的支援。主要區別在於,提供服務的 檔案系統 可能會分配一組連續的頁面,並允許在其上進行對映。

建議對這類檔案應用截斷操作(如果檔案為空且增加檔案大小),應視為請求收集足夠的頁面來支援對映。這是支援 POSIX 共享記憶體所必需的。

記憶體支援的裝置透過對映的後端裝置資訊中設定 memory_backed 標誌來指示。

提供可共享塊裝置支援

在塊裝置檔案上提供共享對映與字元裝置完全相同。如果底層沒有實際裝置,則驅動程式應分配足夠的連續記憶體來支援任何受支援的對映。

調整頁面修剪行為

NOMMU mmap在執行分配時會自動向上舍入到最接近的2的冪次的頁數。這可能會對記憶體碎片化產生不利影響,因此,它被配置為可調節的。預設行為是積極地修剪分配並將任何多餘的頁面返回給頁面分配器。為了更好地控制碎片化,此行為可以完全停用,或者提高到更高的頁面水印以開始修剪。

頁面修剪行為可透過sysctl vm.nr_trim_pages 進行配置。