《VxWorks7編程指南》筆記(一)——內存管理

目錄

1.VxWorks7內存管理模型

2.物理內存映射庫

3.VxWorks內存分配

4.RTP堆與內存區域管理

5.VxWorks內核堆與內存區域管理

6.內存分配優化

7.內存池

8.POSIX內存管理

9.內存映射機制

10.內核虛擬內存管理


1.VxWorks7內存管理模型

VxWorks7爲執行在內核態與用戶態的所有代碼都提供了內存管理機制。對於32位與64位CPU,VxWorks7所提供的內存管理機制是相同的。

內核上下文不是一一映射的,也就是說虛擬內存地址與物理地址不是一一映射的。虛擬內存被分區管理,每個分區具有專門的用處和相應的分配機制。

VxWorks7內存模型用於:

  • 使系統能夠支持更大容量的RAM;
  • 使系統能夠支持RAM中不連續的塊空間;
  • 更快、更高效的系統調用內存驗證;
  • 減少了虛擬地址空間碎片;
  • 可以根據需求實現內存與I/O空間的動態映射(而不是使用靜態配置的代碼);
  • 簡化使用標準ABI開發共享庫的過程,該過程需要基於預定義的虛擬內存佈局實現標準的重疊地址空間管理;

顯示內存佈局信息

shell中的adrSpaceShow()函數(針對C解釋器)或adrsp info命令(針對命令解釋器),可以用於顯示當前地址空間使用的概況。這兩者分貝包含在INCLUDE_ADR_SAPCE_SHOW與INCLUDE_ADR_SPACE_SHELL_CMD兩個組件中。

虛擬內存區域

VxWorks定義了多個虛擬內存區域。這些區域是根據處理器架構進行地址和大小劃分的,具有特定的作用。虛擬內存區域如下圖所示。

(1)內核系統虛擬內存區

內核系統虛擬內存區包含了內核系統內存。從中可以定位到內核鏡像(text、data、bss)、內核臨接堆(kernel proximity heap,即靠近內核內存的對空間),等等。

(2)內核虛擬內存池區

內核虛擬內存池用於在內核中實現內存的動態管理。該區域用於按需分配虛擬內存,如創建和擴展內核應用程序、內存映射設備、DMA內存、用戶保留內存和一致性內存等需求。有關內核虛擬內存池更詳細的信息可以通過adrSpaceShow()函數獲取。

(3)內核保留內存區

該內核區域供VxWorks內部使用而保留,例如可以用於管理MMU頁表結構體。

(4)共享用戶虛擬內存區

共享用戶虛擬內存區用於爲共享映射分配虛擬內存,如共享數據區、共享庫、使用mmap()的MAP_SHARED選項進行內存映射等等。更詳細的信息可以adrSpaceShow()函數獲取。

(5)RTP私有虛擬內存區

RTP私有虛擬內存區用於創建RTP的私有映射:代碼與數據段、RTP堆空間以及使用mmap()的MAP_PRIVATE選項進行內存映射等等。在系統中的所有RTP都可以訪問整個RTP私有內存區。所以,RTP使用重疊地址空間管理。

 

全局RAM內存池

全局RAM內存池適用於動態分配RAM空間的內部分配機制。VxWorks使用該內存池用於創建或擴充:內核通用堆(kernel common heap)、RTP私有內存與共享內存。全局RAM內存池也爲如下對象提供內存:VxWorks內核鏡像、用戶保留內存、持久內存、DMA32堆空間,等等。

 

內核內存映射關係

下圖展示了內核內存映射關係(不包括保留區)。

(1)內核系統內存(kernel system memory)

內核系統內存位於內核系統(虛擬)內存區,其中包括內核代碼段、中斷棧、用於初始化VxWorks任務的內存、內核鄰接堆,還有用於存放啓動參數區和異常消息區的特殊存儲區域。內核系統內存在虛擬與物理空間都是一個連續的塊空間。虛擬內存的起始地址由LOCAL_MEM_LOCAL_ADRS定義。其大小爲其中存放內容的大小之和,其中包括了內核鏡像、內核鄰接堆等等。用於內核系統內存的RAM空間由BSP配置。

(2)內核通用堆(kernel common heap)

內核通用堆是供內核與內核應用程序進行動態內存分配的內存區域。物理內存是從全局RAM內存池中分配,虛擬內存是從內核虛擬內存池區分配。

(3)DMA32 Heap

DMA32堆是供設備驅動使用的內存區域。其中,這些設備不具備訪問4GB以上物理地址空間的能力(如32位PCI設備)。虛擬地址是從內核虛擬內存池區中動態分配。DMA32堆的物理地址和大小由BSP提供。

(4)用戶保留內存(user-reserved memory)

用戶保留內存是RAM中一個可選的、由內核映射的(但不是直接由內核管理)區域。它由運行在內核中的應用程序管理。用戶保留內存的虛擬空間由內核虛擬內存池區分配。可以通過userReservedMem()函數獲取用戶保留內存的起始地址、大小等信息。

(5)持久內存(persistent memory)

持久內存是RAM中的一個可選的、在熱復位時不會被清空的區域。持久內存的虛擬空間由內核虛擬內存池分配。持久內存由pmLib API管理。持久內存主要用於如下內核服務:錯誤檢測與報告服務、core dump服務。

 

內核通用堆(kernel common heap)

內核通用堆是一個供內核與內核應用程序進行動態內存分配的內存區域。該堆空間由標準的ANSI內存分配函數malloc()、free()等進行管理。

內核通用堆也用於爲從主機系統下載的內核模塊分配內存。默認情況下,下載的內核模塊的內存空間由內核鄰接堆分配。

該堆的初始值由KERNEL_COMMON_HEAP_INIT_SIZE內核配置參數指定(單位爲字節)。內核通用堆支持自動增長。如果當前堆空間不能滿足分配需求,操作系統將自動從內核虛擬內存池區和全局RAM內存池分配更多的內存。堆空間增長的大小應該是KERNEL_COMMON_HEAP_INCR_SIZE內核配置參數的整數倍。如果將該參數設置爲0,則將關閉自動增長功能。

廣義上將,對該區域中的分段沒有數量限制。狹義上,堆確保了麼誒一個分配的塊的內存在物理上是連續的,所以如果基於全局RAM內存池中可用的連續物理內存,內核通用堆的分段是受系統限制的。段大小決定了可以從該區域分配的最大內存塊。需要注意的是mmap()函數使用的內存區域在物理上是不連續的。

 

內核鄰接堆(kernel proximity heap)

內核鄰接堆是在內核系統中創建的一個內存區域,使用kProHeapLib API進行管理。

內核鄰接堆也可用於(默認情況下)爲從主機系統下載的內核模塊分配內存。該模塊必須按照內核代碼模型進行編譯。也可以從內核通用堆中爲內核模塊分配內存。

此外,內核鄰接堆還可以用於爲中斷調用函數或其他包含可執行代碼的內容分配空間。

內核鄰接堆是第一個被初始化的內存分配機制。在系統啓動的早期(內核通用堆創建之前),VxWorks庫使用內核鄰接堆進行常規的內存分配。這些過程不受代碼模型需求所影響。

內核鄰接堆從內核系統RAM的空閒區域創建(不再用於內核代碼)。該堆的大小有KERNEL_PROXIMITY_HEAP_SIZE參數指定。

 

DMA32堆

DMA32堆是供設備驅動使用的內存區域,但是這些設備不具備訪問大於4GB物理地址空間的能力(如32位PCI設備)。DMA32堆由cacheDMA32Lib API管理。

通常,該堆的物理內存位於RAM中的低4GB空間,由BSP配置。虛擬內存則由內核虛擬內存池區分配。

2.物理內存映射庫

VxWorks使用pmapBaseLib庫實現內核與RTP上下文的物理地址映射(map)或關閉映射(unmap)。

使用該庫可以允許驅動或其他模塊,如圖像層,使用標準的接口實現對物理內存的map和unmap。爲了在VxWorks鏡像中使用物理地址映射庫,需要在VIP項目中添加INCLUDE_PMAP_LIB組件。更對信息參考pmapBaseLib API。

3.VxWorks內存分配

VxWorks內存分區、內存池和私有mmap()機制可用作多種用途,且各具優勢。如下所示:

4.RTP堆與內存區域管理

VxWorks爲RTP提供了堆與內存分區管理支持。默認情況下,堆被實現爲進程的一個內存分區。

堆是在進程初始階段自動創建的。初始化大小和自增大小由環境變量HEAP_INITAL_SIZE、HEAP_SIZE與HEAP_MAX_SIZE設定。環境變量僅在應用程序啓動時有效,應用程序不能修改這些值。

內存分區是一些連續的內存區域,用於供應用程序動態地分配內存。引用程序可以創建自己的分區,並從這些分區分配和釋放內存。

由進程創建的堆和任何分區都是進程私有的,也就是說只有該進程允許從這些分區分配或釋放內存。

 

可選的堆管理器

VxWorks的進程堆實現可以由自定義的函數代替,僅需要在應用程序中鏈接代替的函數即可(代替的函數庫在鏈接時必須放在vxlib.a之前)。

用作堆空間的內存可以按照如下方式獲取:

一個靜態創建的數組變量。該方案簡單,但只能創建一個固定大小的堆;

使用動態內存映射函數mmap()。使用mmap()可以實現對空間大小的自增。然而需要重點注意的是,mmap()之後的調用不能保證能夠提供連續的內存塊空間。

靜態創建數組變量

char heapMem[HEAP_SIZE];

如果應用程序自動鏈接了libc.so(其中默認包括memLib.o),那麼將自動創建由memLib提供的默認堆。爲了避免創建默認堆,可以創建一個自定義的不包含memlib.o的lic.so。

5.VxWorks內核堆與內存區域管理

原理同上,略。

6.內存分配優化

供內核與RTP使用的memPartCacheLib庫可以用於內核空間擴展和用戶對空間管理,從而提升經常進行動態內存分配的任務的執行速度。

加速的方式是通過減少對堆管理器關鍵區的競爭實現的,這些競爭將由一個互斥信號量控制。該方法依賴於任務級私有數據結構,從而使任務可以再不適用鎖的情況下,頻繁地分配和釋放內存塊。從概念上講,這就好比內存的緩衝cache。分配給每個任務的內存被分爲各種塊,每個塊代表了一個給定的分配大小。memPartCacheLib通過malloc()與free()函數實現最大512字節的內存分配與釋放。分配大於512字節的操作由內存分區函數直接執行。內存塊的大小是經過劃分的,小的塊由16字節大,大點的有32字節,最大的空間爲64字節。

該特性同時支持SMP和單核配置。通過避免多線程或多核競爭同一個內存分區鎖,實現了性能提升。同時,通過更快的算法,實現了快速重用已釋放的內存塊空間。

 

內核任務配置

爲了使能系統堆的任務級緩存,需要配置INCLUDE_MEM_PART_CACHE組件。

默認情況下MEM_PART_CACHE_GLOBL_ENABLE配置參數是設置爲FALSE的,它爲每個任務提供了任務級緩存。除非一個任務調用了memPartCacheCreate()函數,否則該功能不會使能。

如果這個參數設置爲TRUE,那麼將使能對所有任務的任務級緩存。當任何任務創建之後,任務級緩存將自動使能。當一個任務被刪除,任何處於緩存中的內容都將釋放。

 

RTP任務實現

爲了使能進程堆的任務級緩存,RTP應用程序必須鏈接memPartCacheLib庫,且在運行時必須使能cache。爲了在應用程序中鏈接memPartCacheLib,需要使用鏈接選項-uincludeMemCache。或者,可以在應用程序中添加如下代碼行:

extern BOOL includeMemCache;includeMemCache=TRUE;

之後爲了給在RTP中的所有任務使能內存緩存,需要在RTP啓動時將環境變量MEM_PART_CACHE_GLOBAL_ENABLE設置爲TRUE。

如果環境變量MEM_PART_CACHE_GLOBL_ENABLE沒有設置,還可以使用memPartCcheCreate()函數實現獨立任務的內存管理優化。如果需要,可以調用memPartCacheDelete()函數關閉一個任務的緩存。當任務調用memPartCacheDelete()函數時,由該任務分配和使用的內存依舊有效,可以在之後釋放。只有未被使用的且已經緩存的內存塊會被自動返回到堆分區。

7.內存池

內存池是由一系列具有靜態大小的內存空間組成的一個動態集合。通過最小化從內存分區分配的大小,內存池提供了一個快速、高效的內存管理機制。應用程序通過內存池獲取大量的、確切大小的內存空間。使用內存池也可以減少因頻繁分配釋放內存導致的內存碎片。

系統可以使用內存池進行頻繁的、固定大小的內存分配與釋放,如消息系統、數據庫等。內存池系統的大小是動態的,如果需要還可以通過從內存分區或堆空間分配用戶自定義的新空間實現自動增長。

8.POSIX內存管理

VxWorks提供各種POSIX內存管理功能。包括:

  • 使用calloc()、malloc()、realloc()和free()實現動態內存分配;
  • POSIX內存映射文件(_POSIX_MAPPED_FILES選項);
  • POSIX共享內存對象(_POSIX_SHARED_MEMORY_OBJECTS選項);
  • POSIX內存保護(_POSIX_MEMORY_PROTECTION選項);
  • POSIX內存鎖(_POSIX_MEMLOCK和_POSIX_MEMLOCK_RANGE選項);

此外,VxWorks還支持匿名內存映射,該功能不是POSIX標準中的功能,而是作爲一個擴展功能實現的。

POSIX內存管理API

由VxWorks提供的POSIX內存管理API確保了應用程序的可移植性。需要注意的是,VxWorks的POSIX內存管理功能是基於實時操作系統實現的,需要具備確定性、佔用內存空間小、可擴展性的特點。在通用操作系統中常見的功能,如按需分頁、寫時拷貝等,在VxWorks中都不支持。這就確保了系統具有確定的內存訪問與執行時間,但是也意味着系統內存將限制一個進程可以映射的虛擬內存大小。有些函數僅爲了可移植性而提供,被沒有進行具體的實現。

MamLib與shmLib中的函數如下所示:

9.內存映射機制

VxWorks爲內存映射文件、共享內存對象、匿名內存映射、設備內存和共享數據區域,提供了內存映射機制。

這些機制包括:

  • 內存映射文件:使用mmanLib的POSIX機制;
  • 共享內存對象:使用mmanLib和shmLib的POSIX機制;
  • 匿名內存映射:VxWorks對使用mmanLib的POSIX內存映射的擴展,需要使用匿名標誌;
  • 設備內存:VxWorks使用mmanLib、devMemLib的POSIX內存映射的擴展;
  • 共享數據區域:使用sdLib的VxWorks機制。

下表對基於POSIX(mmanLib)的功能與擴展、專有的(sdLib)內存功能進行了比較。它們在功能上有重疊。

 

POSIX內存映射文件

VxWorks提供POSIX內存映射文件支持。爲了實現內存映射文件,字啊一個POSIX兼容的文件系統中打開一個常規文件時,需要將文件描述符傳遞給mmap()函數。同時支持共享與私有的映射。在VxWorks中需要配置INCLUDE_POSIX_MAPPED_FILES組件,以使用該功能。

對於內存映射文件,系統不會自動進行同步。同時對於mmap()和文件系統,並不存在統一的緩衝區。這就意味着應用程序需要使用msync()函數去同步文件存儲介質中的映射鏡像。唯一的例外就是顯示地使用munmap()函數,或者在進程結束時隱式地關閉映射。在這種關閉映射的過程中,將會自動執行同步過程。

POSIX共享內存對象

VxWorks提供POSIX共享內存對象支持。通過這種類型的映射,mmap()所使用的文件描述符由shm_open()函數提供。同時支持共享和私有映射。這種類型的映射由兩種內核組件提供:一個虛擬文件系統shmFs,提供了shm_open()、shm_unlink()函數;用於映射文件的mmap()擴展。這兩個機制分別由INCLUDE_POSIX_SHM_和INCLUDE_POSIX_MAPPED_FILES組件提供。

共享文件系統爲共享內存對象提供了名字空間。它是一個虛擬文件系統,不支持讀寫操作。共享內存對象的內容可以通過它們的內存映射鏡像進行修改。

匿名內存映射

匿名內存映射是POSIX內存映射API的一個VxWorks擴展。它爲進程提供了一個簡單的獲取和釋放額外內存頁的方法,而不需要將映射與一個命名的文件系統對象相關聯。匿名內存映射僅支持私有映射。

當VxWorks中提供了RTP支持時,將自動引入匿名內存映射。必須使用INCLUDE_MMP組件,以提供內核支持。

下面的RTP應用示例展示瞭如何使用匿名標誌(MAP_ANONYMOUS)的mmap()、mprotect()和unmap()函數。內核應用程序也應該使用相同的代碼,只是main()函數應該使用其他名字。

 

設備內存對象

VxWorks爲內存映射文件機制提供了一個非POSIX的擴展,它允許獲取設備內存,可以用於任何設備驅動(串口、網絡、圖像等等)。該機制由INCLUDE_DEVMEM組件提供。

內核函數devMemCreate()、devMemOpen()、devMemUnlink()可以用於創建、打開和刪除一個設備內存對象。mmap()函數用於在設備內存對象創建或打開後執行內存映射。所以供mmap()使用的文件描述符由devMemOpen()函數提供。

共享數據區

專有的共享數據區機制爲RTP應用程序互相共享一個內存區域提供了一種方法。

10.內核虛擬內存管理

VxWorks可以針對處理器的MMU配置架構獨立的接口,以提供虛擬內存支持。

該支持包括以下特性:

  • 在啓動時設置內核內存環境;
  • 將虛擬空間的頁映射到物理空間;
  • 基於頁空間設置cache屬性;
  • 基於頁空間設置保護屬性;
  • 設置一個頁映射爲有效或無效;
  • 爲內存頁鎖定或解除TLB條目;
  • 使能頁優化;

VM支持的可編程元素有vmBaseLib庫提供。

配置虛擬內存管理

如下組件提供了基本的虛擬內存管理功能。

配置內核虛擬內存環境

內核虛擬內存環境將在啓動時根據BSP中提供的配置參數自動創建。

通常不需要修改默認配置,然而如果類似以下情況則可以進行修改:

  • 系統中添加了新的設備或驅動;
  • 某個條目的保護或Cache屬性必須修改。例如,VxWorks從未向Flash寫入數據,那麼flash存儲空間可以設置爲只讀。然而,如果使用Flash驅動(如TFFS),那麼保護屬性需要設置爲可寫。
  • 在表中存在未使用的條目。通常,最好僅保留真正描述系統的條目,因爲每個條目都可能需要額外的系統RAM空間。內存塊映射的越多,頁表所需的內存也就越多。

採用編程的方法管理虛擬內存

 

修改頁狀態

每個虛擬內存頁(通常4KB)都有與之相關的狀態屬性。一個頁可以是valid/invalid,readable,writeable,executable,或cacheable、non-cacheable。

頁的狀態可以使用vmStateSet()函數修改。下表中描述了可供vmStateSet()使用的頁狀態常量與頁狀態掩碼。頁狀態掩碼必須用於描述哪些標記位被修改了。可以使用邏輯或操作符操作頁狀態和掩碼,以定義映射保護與cache屬性。

 

將內存設置爲不可寫

內存區域可以使用vmStateSet()函數設置爲寫保護,從而避免無意的訪問。比如,可以用於限制對某個函數中的一個數據的修改。如果一個數據對象是全局的且只讀,那麼任務只能讀取它而不能修改它。任何必須要修改這個對象的任務,需要調用相關的函數執行修改操作。在這些函數中,數據將設置爲可寫,當函數退出後,該內存將被設置爲MMU_ARRT_PROT_SUP_READ。

不可寫內存示例

在示例代碼中,一個任務調用dataModify()去修改由pData指針指向的數據結構。該函數首先使該內存可寫,然後修改數據,最後將其設置爲不可寫。如果一個任務隨後嘗試直接修改數據,而不使用dataModify()函數,那麼將產生一個數據訪問異常。

 

使內存頁無效

使用vmStateSet()函數,可以基於頁使內存無效。任何對無效頁的訪問都將產生異常,無論是讀或寫操作。爲了再使頁生效,同樣使用vmStateSet()函數。例如:

vmStateSet (NULL,address,len,MMU_ATTR_VALLID_MSK,MMU_ATTR_VALID_NOT);
vmStateSet (NULL,address,len,MMU_ATTR_VALLID_MSK,MMU_ATTR_VALID);

 

查看TLB條目

有些處理器可以將TLB中的某些條目強制永久保留在TLB中。如果特定架構的MMU庫支持該特性,那麼就可以使用vmPageLock()函數將這些頁條目上鎖,使用vmPageUnlock()函數解鎖。

INCLUDE_LOCK_TEXT_SECTION組件提供了TLB鎖機制。當VxWorks中包含了該組件,則系統啓動時將自動把內核鏡像的代碼段鎖定。

該功能可以用於類似cache鎖定的性能優化。當常用的頁條目被鎖定在TLB中,TLB缺失的次數將會減少。需要注意的是,所有處理器類型對TLB條目的數量都要進行限定,否則如果鎖定的條目太多,則會導致對其他動態使用的TLB條目的競爭。

頁大小優化

對於一些處理器,可以使用比默認頁大小(由VM_PAGE_SIZE定義)更大的大小,用於具有相同內存屬性的大的、連續的內存塊,並滿足虛擬與物理地址對齊需求。使用該優化具有如下好處:

  • 減少用於映射內存的頁表項,從而降低內存使用;
  • 更高效地使用TLB,減少TLB缺失,從而提高性能;

對於32位VxWorks,可以通過配置INCLUDE_PAGE_SIZE_OPTMIZATION組件,實現在啓動時完成對整個內核內存空間(包括I/O塊)的優化。也可以在運行時使用vmPageOptimize()函數完成頁大小優化。64位VxWorks與32位類似。

在ISR中設置頁狀態

對於多數處理器,vmStateSet()函數是一個非阻塞函數,因此可以在ISR中調用。然而,在某些情況下可能導致阻塞,如支持頁大小優化的處理器。

爲了確保ISR能夠針對特定的頁而調用vmStateSet()函數,這些頁必須設置MMU_ATTR_NO_BLOCK屬性。示例代碼如下:

 

問題定位

如下函數與命令可以用於虛擬內存問題定位:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章