小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

vxWorks內(nèi)核解讀-5

 立志德美 2019-04-22

本篇博文,我們?cè)撜劦絎ind內(nèi)核的內(nèi)存管理模塊了,嵌入式操作系統(tǒng)中, 內(nèi)存的管理及分配占據(jù)著極為重要的位置, 因?yàn)樵谇度胧较到y(tǒng)中, 存儲(chǔ)容量極為有限, 而且還受到體積、成本的限制, 更重要的是其對(duì)系統(tǒng)的性能、可靠性的要求極高, 所以深入剖析嵌入式操作系統(tǒng)的內(nèi)存管理, 對(duì)其進(jìn)行優(yōu)化及有效管理, 具有十分重要的意義。在嵌入式系統(tǒng)開發(fā)中, 對(duì)內(nèi)存的管理有很高的要求。概括地說(shuō), 它必須滿足以下三點(diǎn)要求:

  • 實(shí)時(shí)性, 即在內(nèi)存分配過(guò)程中要盡可能快地滿足要求。因此, 嵌入式操作系統(tǒng)中不可能采取通用操作系統(tǒng)中的一些復(fù)雜而完備的內(nèi)存分配策略, 而是要采用簡(jiǎn)單、快速的分配策略, 比如我們現(xiàn)在討論的VxWorks 操作系統(tǒng)中就采用了“ 首次適應(yīng)”的分配策略。當(dāng)然了具體的分配也因具體的實(shí)時(shí)性要求而各異。

  • 可靠性, 即在內(nèi)存分配過(guò)程中要盡可能地滿足內(nèi)存需求。嵌入式系統(tǒng)應(yīng)用千變?nèi)f化, 對(duì)系統(tǒng)的可靠性要求很高, 內(nèi)存分配的請(qǐng)求必須得到滿足, 如果分配失敗, 則會(huì)帶來(lái)災(zāi)難性的后果。

  • 高效性, 即在內(nèi)存分配過(guò)程中要盡可能地減少浪費(fèi)。在嵌入式系統(tǒng)中, 對(duì)體積成本的要求較高, 所以在有限的內(nèi)存空間內(nèi), 如何合理的配置及管理, 提高使用效率顯的尤為重要

實(shí)時(shí)嵌入式系統(tǒng)開發(fā)者通常需要根據(jù)系統(tǒng)的要求在RTOS提供的內(nèi)存管理之上實(shí)現(xiàn)特定的內(nèi)存管理,本章研究 VxWorks的Wind內(nèi)核內(nèi)存管理機(jī)制。

5.1 VxWorks內(nèi)存管理概述

5.1.1 VxWorks內(nèi)存布局

VxWorks5.5 版本提供兩種虛擬內(nèi)存支持(Virtual MemorySupport):基本級(jí)(Basic Level)和完整級(jí)(Full Level)?;炯?jí)虛擬內(nèi)存以Cache 功能為基礎(chǔ)。當(dāng)DMA 讀取內(nèi)存前必須確保數(shù)據(jù)以更新到內(nèi)存中而不是仍緩存在Catch 中。對(duì)于完整級(jí)需要購(gòu)買可選組件VxVMI,其功能為:提供對(duì)CPU 的內(nèi)存管理單元的編程接口,同時(shí)提供內(nèi)存保護(hù),它在功能上覆蓋了基本級(jí)虛存。VxWorks的內(nèi)存配置宏如下:

INCLUDE_MMU_BASIC:基本級(jí)虛存管理,無(wú)需VxVMI;

INCLUDE_MMU_FULL:完整級(jí)虛存管理,需組件VxVMI;

INCLUDE_PROTECT_TEXT:寫保護(hù)text,需組件VxVMI;

INCLUDE_PROTECT_VEC_TABLE:寫保護(hù)異常向量表,需組件VxVMI;


在VxVMI的最小配置中,它寫保護(hù)了幾個(gè)關(guān)鍵資源,其中包括VxWorks程序代碼體、異常向量表、以及通過(guò)VxWorks裝載器下載的應(yīng)用程序代碼體。保護(hù)特性讓開發(fā)人員集中精力編寫自己的程序,無(wú)需擔(dān)心無(wú)意中修改關(guān)鍵代碼段或引發(fā)耗時(shí)的系統(tǒng)錯(cuò)誤。這在開發(fā)階段是很有用的,因?yàn)樗?jiǎn)化了對(duì)致命性錯(cuò)誤的診斷。在產(chǎn)品的定型階段也是如此,因?yàn)樗岣吡讼到y(tǒng)可靠性。VxVMI提供的其它工具主要用于修改這些被保護(hù)的區(qū)域,如修改異常表或者插入斷點(diǎn)。

以上配置可在Tornado 開發(fā)環(huán)境中MMU 配置管理工具中改變,也可在BSP 的config.h 中完成。內(nèi)存頁(yè)表的劃分和屬性配置,包括BSP 的config.h 中VM_PAGE_SIZE 和sysLib.h 中的sysPhysMemDesc 定義。本篇博文只考慮VxWorks基本級(jí)內(nèi)部保護(hù),即INCLUDE_MMU_BASIC配置模塊。

VxWorks 的內(nèi)存管理函數(shù)存在于2 個(gè)庫(kù)中 :memPartLib (緊湊的內(nèi)存分區(qū)管理器) 和memLib (完整功能的內(nèi)存分區(qū)管理器)。memPartLib 提供的工具用于從內(nèi)存分區(qū)中分配內(nèi)存塊。該庫(kù)包含兩類程序, 一類是通用工具memPartXXX(),包含創(chuàng)建和管理內(nèi)存分區(qū)并從這些分區(qū)中分配和管理內(nèi)存塊;另一類是標(biāo)準(zhǔn)的malloc/free內(nèi)存分配接口。系統(tǒng)內(nèi)存分區(qū)(其ID為memSysPartId 是一個(gè)全局變量,關(guān)于它的定義在memLib.h 中)在內(nèi)核初始化kernelInit() 是由usrRoot() (包含在usrConfig.c 中) 調(diào)用memInit 創(chuàng)建。其開始地址為RAM 中緊接著VxWorks 的BSS段之后的地址,大小為所有空閑內(nèi)存。

VxWorks 5.5在目標(biāo)板上有兩個(gè)內(nèi)存池(Memmory Pool)用于動(dòng)態(tài)內(nèi)存分配:系統(tǒng)內(nèi)存池(System Memory Pool)和WDB內(nèi)存池。對(duì)于VxWorks上程序的設(shè)計(jì)者來(lái)說(shuō),需要關(guān)注的是系統(tǒng)內(nèi)存池的動(dòng)態(tài)內(nèi)存管理機(jī)制。嵌入式系統(tǒng)的動(dòng)態(tài)內(nèi)存管理實(shí)際上就是需要盡量避免動(dòng)態(tài)分配和釋放內(nèi)存,以最大程度地保證系統(tǒng)的穩(wěn)定性,VxWorks5.5的內(nèi)存布局如圖5.1所示。

VxWorks內(nèi)核解讀-5

5.1 VxWorks內(nèi)存布局

如上圖所示,VxWorks 5.5的內(nèi)存按照裝載內(nèi)容不同從內(nèi)存的低地址開始依次分為低端內(nèi)存區(qū)、VxWorks內(nèi)存區(qū)、WDB內(nèi)存池、系統(tǒng)內(nèi)存池和用戶保留區(qū)五部分。各部分在內(nèi)存中的位置由一些宏參數(shù)來(lái)決定,內(nèi)存區(qū)域的劃分及相關(guān)參數(shù)的定義與CPU的體系結(jié)構(gòu)相關(guān),這里只介紹VxWorks 5.5的典型內(nèi)存布局。

整個(gè)目標(biāo)板內(nèi)存的起始地址是LOCAL_MEM_LOCAL_ADRS,大小是LOCAL_MEM_SIZE,sysPhysMemTop()一般返回內(nèi)存的物理最高地址。各部分的內(nèi)容及參數(shù)如下:

(1)低端內(nèi)存區(qū)

在低端內(nèi)存區(qū)中通常包含了中斷向量表、bootline(系統(tǒng)引導(dǎo)配置)和exception message(異常信息)等信息。

LOCAL_MEM_LOCAL_ADRS是低端內(nèi)存區(qū)的起始地址,典型設(shè)置是0,但是在Pentium平臺(tái)一般設(shè)置位0x100000(即1M)。RAM_LOW_ADRS是低端內(nèi)存區(qū)最高地址,也即vxWorks系統(tǒng)映像加載到內(nèi)存中的地址,在Pentium平臺(tái)一般為0x308000。

(2)VxWorks區(qū)域

VxWorks區(qū)存放操作系統(tǒng)映像,其中依次是VxWorks image的代碼段、數(shù)據(jù)段和BSS段。

由RAM_LOW_ADRS和FREE_MEM_ADRS決定該區(qū)的大小和位置。

在Pentium平臺(tái)RAM_LOW_ADRS為0x308000,F(xiàn)REE_MEM_ADRS通過(guò)鏈接腳本中的全部變量end指定,一般緊隨VxWorks系統(tǒng)映像BSS段末尾。

(3)系統(tǒng)可用內(nèi)存

系統(tǒng)可用內(nèi)存主要是提供給VxWorks內(nèi)存池用于動(dòng)態(tài)內(nèi)存的分配(如malloc())、任務(wù)的堆棧和控制塊及VxWorks運(yùn)行時(shí)需要的內(nèi)存。這部分內(nèi)存有VxWorks管理,開銷位于目標(biāo)板上。系統(tǒng)內(nèi)存池在系統(tǒng)啟動(dòng)時(shí)初始化,它的大小是整個(gè)內(nèi)存減去其他區(qū)的大小。在啟動(dòng)后可以通過(guò)函數(shù)memAddToPool()向系統(tǒng)內(nèi)存池中增加內(nèi)存,sysMemTop()返回系統(tǒng)內(nèi)存池(System Memory Pool)的最高地址。

(4)WDB內(nèi)存池

又叫Target Server內(nèi)存池,是在目標(biāo)板上為Tornado工具(如Wind Debugger)保留的一個(gè)內(nèi)存池,主要用于動(dòng)態(tài)下載目標(biāo)模塊、傳送參數(shù)等。這個(gè)內(nèi)存池由Target Server管理,管理的開銷位于宿主機(jī)。Target Server內(nèi)存池必要時(shí)(如Target Server內(nèi)存池中內(nèi)存不夠)可以從系統(tǒng)內(nèi)存池中分配內(nèi)存。

其起始地址是WDB_POOL_BASE,通常等于FREE_RAM_ADRS,初始大小由WDB_POOL_SIZE定義,默認(rèn)值是系統(tǒng)內(nèi)存池的1/16,在installDir/target/config/all/configAll.h中定義:

#define WDB_POOL_SIZE((sysMemTop()-FREE_RAM_ADRS)/16)系統(tǒng)中不一定包含Target Server內(nèi)存池.只有當(dāng)系統(tǒng)配置中包含組件INCLUDE_WDB時(shí)才需要Target Server內(nèi)存池。

(5)用戶保留區(qū)

用戶保留區(qū)是用戶為特定應(yīng)用程序保留的內(nèi)存。該區(qū)的大小由USER_RESERVED_MEM決定,默認(rèn)值為0。以上所涉及的大部分宏定義和函數(shù)在目標(biāo)板的BSP或configAll.h中定義。

5.1.2 VxWorks內(nèi)存分配策略

嵌入式系統(tǒng)中為任務(wù)分配內(nèi)存空間有兩種方式,靜態(tài)分配和動(dòng)態(tài)分配。靜態(tài)分配為系統(tǒng)提供了最好的可靠性與實(shí)時(shí)性,如可以通過(guò)修改USER_RESERVED_MEM 分配內(nèi)存給應(yīng)用程序的特殊請(qǐng)求用。對(duì)于那些對(duì)實(shí)時(shí)性和可靠性要求極高的需求,只能采用靜態(tài)分配方式。但采用靜態(tài)分配必然會(huì)使系統(tǒng)失去靈活性,因此必須在設(shè)計(jì)階段考慮所有可能的情況,并對(duì)所有的需求做出相應(yīng)的空間分配,一旦出現(xiàn)沒(méi)有考慮到的情況,系統(tǒng)就無(wú)法處理。此外靜態(tài)分配方式也必然導(dǎo)致很大的浪費(fèi),因?yàn)楸仨毎凑兆顗那闆r進(jìn)行最大的配置,而在實(shí)際運(yùn)行中可能只用到其中的一小部分。因此一般系統(tǒng)中只有1 個(gè)內(nèi)存分區(qū),即系統(tǒng)分區(qū),所有任務(wù)所需要的內(nèi)存直接調(diào)用malloc()從其中分配。分配采用First-Fit算法(空閑內(nèi)存塊按地址大小遞增排列,對(duì)于要求分配的分區(qū)容量size,從頭開始比較,直至找到滿足大小≥size 的塊為止,并從鏈表相應(yīng)塊中分配出相應(yīng)size 大小的塊指針),通過(guò)free釋放的內(nèi)存將被聚合以形成更大的空閑塊。這就是VxWorks的動(dòng)態(tài)內(nèi)存分配機(jī)理。但是使用動(dòng)態(tài)內(nèi)存分配malloc/free時(shí)要注意到以下幾個(gè)方面的限制:

  • 因?yàn)橄到y(tǒng)內(nèi)存分區(qū)是一種臨界資源,由信號(hào)量保護(hù),使用malloc 會(huì)導(dǎo)致當(dāng)前調(diào)用掛起,所以它不能用于中斷服務(wù)程序;

  • 因?yàn)檫M(jìn)行內(nèi)存分配需要執(zhí)行查找算法,其執(zhí)行時(shí)間與系統(tǒng)當(dāng)前的內(nèi)存使用情況相關(guān),是不確定的,所以對(duì)于有規(guī)定時(shí)限的操作它是不適宜的;

  • 采用簡(jiǎn)單的最先匹配算法,容易導(dǎo)致系統(tǒng)中存在大量的內(nèi)存碎片,降低內(nèi)存使用效率和系統(tǒng)性能。

一般在系統(tǒng)設(shè)計(jì)時(shí)采用靜態(tài)分配與動(dòng)態(tài)分配相結(jié)合的方法。也就是說(shuō),系統(tǒng)中的一部分任務(wù)有嚴(yán)格的時(shí)限要求,而另一部分只是要求完成得越快越好。按照RMS(RateMonotonic Scheduling)理論,所有硬實(shí)時(shí)任務(wù)總的CPU 時(shí)間應(yīng)小于70%,這樣的系統(tǒng)必須采用搶先式任務(wù)調(diào)度;而在這樣的系統(tǒng)中,就可以采用動(dòng)態(tài)內(nèi)存分配來(lái)滿足那一部分可靠性和實(shí)時(shí)性要求不那么高的任務(wù)。

VxWorks采用最先適應(yīng)法來(lái)動(dòng)態(tài)分配內(nèi)存,

優(yōu)點(diǎn):

  • 滿足嵌入式系統(tǒng)對(duì)實(shí)時(shí)性的要求;

  • 盡可能的利用低地址空間,從而保證高地址空間有較大的空閑來(lái)放置要求內(nèi)存較多的任務(wù);

缺點(diǎn):

VxWorks沒(méi)有清除碎片的功能,只在內(nèi)存釋放時(shí),采用了上下空閑區(qū)融合的方法,即把相鄰的空閑內(nèi)存塊融合成一個(gè)空閑塊。

5.1.3 VxWorks對(duì)虛擬內(nèi)存的支持

VxWorks 5.5提供兩級(jí)虛擬內(nèi)存支持(Virtual Memory Support):基本級(jí)(Basic Level)和完整級(jí)(Full Level)。后者需要購(gòu)買可選組件VxVMI。在VxWorks 5.5中有關(guān)虛擬內(nèi)存的配置包括兩個(gè)部分。第一部分是對(duì)vxWorks虛擬內(nèi)支持級(jí)別和保護(hù)的配置,下表列出了這些選項(xiàng)。

表5.1 VxWorks虛擬內(nèi)存配置常量

VxWorks內(nèi)核解讀-5

以上配置一般在BSP的config.h中完成。

第二部分是內(nèi)存頁(yè)表的劃分和屬性配置。配置包括BSP的config.h中的VM_PAGE_SIZE和sysLib.c中的sysPhysMemDesc。

VM_PAGE_SIZE定義了CPU默認(rèn)的頁(yè)的大小。需參照CPU手冊(cè)的MMU部分定義該值。

sysPhysMemDesc用于初始化MMU的TLB表,它是以PHYS_MEM_DESC為元素的常量數(shù)組。PHYS_MEM_DESC在vmLib.h中定義,用于部分內(nèi)存的虛擬地址到物理地址的映射:

typedef struct phes_mem_desc

{

         void virtualAddr ;        /*虛擬內(nèi)存的物理地址*/

         void *physicalAddr ;   /*虛擬地址*/

         UNIT len ;                      /*這部分的大小*/

         UNIT initialStateMask ;

         UNIT initialState ;       /*設(shè)置這部分內(nèi)存的初始狀態(tài)*/

}PHYS_MEM_DESC;

sysPhysMemDesc中的值需要根據(jù)系統(tǒng)的實(shí)際配置進(jìn)行修改。sysPhysMemDesc中定義的內(nèi)存地址必須頁(yè)對(duì)齊,且必須跨越完整的頁(yè)。也就是說(shuō)PHYS_MEM_DESC結(jié)構(gòu)中的前三個(gè)域的值必須能被VM_PAGE_SIZE整除,否則會(huì)導(dǎo)致VxWorks初始化失敗。

基本級(jí)虛擬內(nèi)存庫(kù)vmBaseLib提供了系統(tǒng)中所需最低限度的MMU支持,其主要目的是為創(chuàng)建Cache-safe緩沖區(qū)提供支持。

5.2 VxWorks內(nèi)存分配算法

5.2.1 VxWorks核心數(shù)據(jù)結(jié)構(gòu)

VxWorks系統(tǒng)初始化時(shí)創(chuàng)建系統(tǒng)內(nèi)存分區(qū),VxWorks定義了全局變量memSysPartition來(lái)管理系統(tǒng)內(nèi)存分,用戶也可以使用memPartLib庫(kù)的函數(shù)實(shí)現(xiàn)自己的分區(qū)。用于分區(qū)中內(nèi)存管理的數(shù)據(jù)結(jié)構(gòu)是在memPartLib.h中定義的mem_part,包含對(duì)象標(biāo)記、保護(hù)該分區(qū)的信號(hào)量、一些分配統(tǒng)計(jì)信心(如當(dāng)前分配的塊數(shù))及分區(qū)中所有的空閑內(nèi)存快形成的一個(gè)雙向鏈表freeList(在VxWorks6.8中,空閑內(nèi)存塊采用平衡二叉樹來(lái)組織)。

具體定義如下:

typedef struct mem_part

{

    OBJ_CORE    objCore;         /* 對(duì)象標(biāo)識(shí) */

    DL_LIST     freeList;              /* 空閑鏈表 */

    SEMAPHORE   sem;            /* 分區(qū)信號(hào)量,保護(hù)該分區(qū)互斥訪問(wèn) */

    unsigned    totalWords;     /* 分區(qū)中的字?jǐn)?shù)(一個(gè)字=兩個(gè)字節(jié)) */

    unsigned    minBlockWords;  /* 以字為單位的最小塊數(shù),包含頭結(jié)構(gòu) */

    unsigned    options;        /* 選項(xiàng),用于調(diào)試和統(tǒng)計(jì)*/

    /*分配統(tǒng)計(jì)信息*/

    unsigned curBlocksAllocated;    /*當(dāng)前分配的塊數(shù) */

    unsigned curWordsAllocated;     /* 當(dāng)前分配的字?jǐn)?shù) */

    unsigned cumBlocksAllocated;    /* 累積分配的塊數(shù) */

    unsigned cumWordsAllocated;     /* 累積分配的字?jǐn)?shù)*/

} PARTITION;

備注:需要注意的是內(nèi)存分區(qū)信號(hào)量sem是分區(qū)描述符中的一個(gè)成員,而不是指向動(dòng)態(tài)創(chuàng)建的信號(hào)量結(jié)構(gòu)的指針,采用靜態(tài)分配的方式,是從提高系統(tǒng)性能的角度考慮。

VxWorks在初始化的過(guò)程中,通過(guò)usrInit()->usrKernelInit()->kernelInit()將系統(tǒng)分配給vxWorks內(nèi)存池的基地址MEM_POOL_START和大小,通過(guò)usrRoot()的參數(shù)傳遞給memInit()函數(shù)。換句話說(shuō):vxWorks系統(tǒng)分區(qū)memSysPartition的初始化由usrRoot()->memInit()來(lái)完成,而其中內(nèi)存的布局則通過(guò)usrInit()->usrKernelInit()->KernelInit()傳遞的參數(shù)來(lái)確定,

usrKernelInit()傳遞給kernelInit()的參數(shù)如下:

kernelInit ((FUNCPTR) usrRoot, ROOT_STACK_SIZE, MEM_POOL_START,

                sysMemTop (), ISR_STACK_SIZE, INT_LOCK_LEVEL);

其中ROOT_STACK_SIZE為10000=0x2710,

MEM_POOL_START為end,這里我們假定為0x3c33a0

sysMemTop ()返回的是最大可用內(nèi)存,這里假定為32M,即sysMemTop ()返回值為0x200 0000。

ISR_STACK_SIZE值為1000=0x3e8

INT_LOCK_LEVEL值為0

我們假設(shè)系統(tǒng)可用內(nèi)存為32M,VxWorks的入口地址為0x30800c,end的值,即VxWorks內(nèi)核映像BSS段末尾地址;

sysMemTop ()返回的值為0x2000000,作為VxWorks內(nèi)存池的最高地址pMemPoolEnd。VxWorks在內(nèi)存中的布局如圖5.2所示。

VxWorks內(nèi)核解讀-5

圖5.2 VxWorks內(nèi)部布局

由于我們不配置WDB模塊,故沒(méi)有標(biāo)出WDB內(nèi)存池的分配。

我們?nèi)砸訮entium平臺(tái)為例,這里假設(shè)VxWorks內(nèi)核映像加載到內(nèi)存的地址RAW_LOW_ADRS為0x30 8000,LOCAL_MEM_LOCAL_ADDR定位為0x10 0000,即1M字節(jié)位置。

Pentium平臺(tái)的全局描述符表放在的0x10 0000+0x1000=1M+4K位置處的80字節(jié)內(nèi)存范圍。

中斷描述符表放置在0x10 0000開始的2K字節(jié)內(nèi)存范圍。

這樣中斷棧的棧底vxIntStackEnd=end=0x3c33a0,

中斷棧的棧基址為vxIntStackBase= end+ISR_STACK_SIZE=0x3c33a0+0x3e8=0x3c3788

將要分配給系統(tǒng)分區(qū)內(nèi)存基地址pMemPoolStart=vxIntStackBase=0x3c3788

用于初始任務(wù)tRootTask的內(nèi)存起始地址為:

pRootMemStart       =pMemPoolEnd-ROOT_STACK_SIZE

=pMemPoolEnd-10000

=0x2000000-0x2710

=0x1ff d8f0

用于初始任務(wù)tRootTask的任務(wù)棧大小為:

rootStackSize  = rootMemNBytes - WIND_TCB_SIZE - MEM_TOT_BLOCK_SIZE

=10000 – 416 –( (2 * MEM_BLOCK_HDR_SIZE) + MEM_FREE_BLOCK_SIZE)

=10000 – 416 – 32=9532=0x253c

用于tRootTask任務(wù)棧的棧頂:

pRootStackBase= pRootMemStart + rootStackSize + MEM_BASE_BLOCK_SIZE

=pRootMemStart+rootStackSize+(MEM_BLOCK_HDR_SIZE+MEM_FREE_BLOCK_SIZE)

=0x1ff d8f0+0x253c+0x8+0x20

= 0x1ff fe54

這樣32M的內(nèi)存就被分成了三個(gè)部分:

0x10 0000到0x3c33a0 用于存放vxWorks內(nèi)核映像的代碼段、數(shù)據(jù)段、BSS段;

0x3c33a0到0x3c3788 用作vxWorks內(nèi)核的中斷棧

0x3c3788(即end+1000) 到0x1ff d8f0(即0x200 0000-0x2710)用于VxWorks的內(nèi)存池;

0x1FFFE54-0x253c(即十進(jìn)制數(shù)9532)到0x1FFFE54用作tRootTask任務(wù)棧的棧底;

這樣的話,用于創(chuàng)建初始任務(wù)tRootTask的TCB控制塊構(gòu)架接口如下:

    taskInit (pTcb, "tRootTask", 0, VX_UNBREAKABLE | VX_DEALLOC_STACK,

               pRootStackBase, (int) rootStackSize, (FUNCPTR) rootRtn,

               (int) pMemPoolStart, (int)memPoolSize, 0, 0, 0, 0, 0, 0, 0, 0);

這里的pMemPoolStart就是圖中vxIntStackBase,即end+1000=0x3c 3788

內(nèi)存池的范圍從vxIntStackBase到pRootMemStart的內(nèi)存空間,

即0x1ff d8f0-0x3c 3788=1C3 A168,約為28.2M,這一段區(qū)域作為初始任務(wù)tRootTask執(zhí)行代碼usrRoot()的入口參數(shù)傳遞給memInit ()函數(shù)用于創(chuàng)建初始分區(qū)。

memInit()函數(shù)的最重要作用是創(chuàng)建內(nèi)存分區(qū)類memPartClass和內(nèi)存分區(qū)類的一個(gè)全局的系統(tǒng)對(duì)象memSysPartition,并用指定的內(nèi)存塊(pMemPoolStart, pMemPoolStart + memPoolSize)來(lái)初始化系統(tǒng)內(nèi)存對(duì)象memSysPartition的分區(qū)空閑鏈表。

這樣得到的VxWorks內(nèi)存分區(qū)邏輯圖如圖5.3所示。

VxWorks內(nèi)核解讀-5

圖5.3 VxWorks內(nèi)存分區(qū)邏輯圖

6.2.2 VxWorks核心分區(qū)初始化

usrInit()->usrKernelInit()->kernelInit()構(gòu)建并啟動(dòng)初始化任務(wù)tRootTask,執(zhí)行usrRoot()

usrRoot()-> memInit (pMemPoolStart, memPoolSize),我們從memInit()開始分析。

還是使用上面的示例:

pMemPoolStart =0x3c3788(即end+1000) 到0x1ff d8f0(即0x200 0000-0x2710)用于VxWorks的初始化內(nèi)存分區(qū)。

memPoolSize=0x1ff d8f0-0x3c3788=0x1C3 A168,約為28.2M,示意圖如圖5.2所示。

memInit()代碼如下:

STATUS memInit ( char *pPool, unsigned poolSize)

{

memLibInit ();           /*初始化化的內(nèi)存管理函數(shù) */

//初始化內(nèi)存對(duì)象

//初始化系統(tǒng)內(nèi)存分區(qū)

//將分配的(pMemPoolStart, memPoolSize)約28.2M加入系統(tǒng)內(nèi)存分區(qū)

    return (memPartLibInit (pPool, poolSize));

 }

6.2.2.1 memLibInit ()分析

STATUS memLibInit (void)

{

    if (!memLibInstalled)

         {

                  _func_valloc                          = (FUNCPTR) valloc;

                  _func_memalign                  = (FUNCPTR) memalign;

                  memPartBlockErrorRtn         = (FUNCPTR) memPartBlockError;

                  memPartAllocErrorRtn          = (FUNCPTR) memPartAllocError;

                  memPartSemInitRtn           = (FUNCPTR) memSemInit;

             memPartOptionsDefault          = MEM_ALLOC_ERROR_LOG_FLAG |

                                      MEM_BLOCK_ERROR_LOG_FLAG |

                                      MEM_BLOCK_ERROR_SUSPEND_FLAG |

                                      MEM_BLOCK_CHECK;

         memLibInstalled = TRUE;

         }

    return ((memLibInstalled) ? OK : ERROR);

}

分析:初始化VxWorks提供的更高層的內(nèi)存管理接口,例如:

_func_valloc初始化為虛擬地址按頁(yè)邊界對(duì)齊的從系統(tǒng)分區(qū)分配內(nèi)存的函數(shù)valloc;

_func_memalign初始化為安裝自定義對(duì)齊方式從系統(tǒng)分區(qū)分配內(nèi)存的函數(shù)memalign;

memPartSemInitRtn用于指定初始化系統(tǒng)分區(qū)memSysPartition的信號(hào)量sem的類型,這里使用具有優(yōu)先級(jí)隊(duì)列、安全刪除和防止優(yōu)先級(jí)翻轉(zhuǎn)的互斥信號(hào)量的初始化函數(shù)memSemInit;

另外兩個(gè)接口定義出錯(cuò)處理方式。

6.2.2.2 memPartLibInit (pPool, poolSize)分析

memPartLibInit()初始化了VxWorks的內(nèi)存對(duì)象類和內(nèi)存對(duì)象類的一個(gè)實(shí)例系統(tǒng)分區(qū),并將配置的28.2M的內(nèi)存塊加入到系統(tǒng)分區(qū)當(dāng)中。

STATUS memPartLibInit (char *pPool, unsigned poolSize )

{

    if ((!memPartLibInstalled) &&

           (classInit (memPartClassId, sizeof (PARTITION),

                       OFFSET (PARTITION, objCore), (FUNCPTR) memPartCreate,

                       (FUNCPTR) memPartInit, (FUNCPTR) memPartDestroy) == OK))

         {

                  memPartInit (&memSysPartition, pPool, poolSize);

                  memPartLibInstalled = TRUE;

         }

    return ((memPartLibInstalled) ? OK : ERROR);

}

分析:

我們?cè)诘?章概述中提到,vxWorks采用類和對(duì)象的思想將wind內(nèi)核的任務(wù)管理模塊、內(nèi)存管理模塊、消息隊(duì)列管理模塊、信號(hào)量管理模塊、以及看門狗管理模塊組織起來(lái),各個(gè)對(duì)象類都指向元類classClass,每個(gè)對(duì)象類只負(fù)責(zé)管理各自的對(duì)象,如圖5.4。

VxWorks內(nèi)核解讀-5

圖5.4 Wind內(nèi)核對(duì)象組織關(guān)系圖

本篇所分析的內(nèi)存分區(qū)memSysPartition就是內(nèi)存分區(qū)對(duì)象類memPartClass的一個(gè)實(shí)例,即系統(tǒng)分區(qū),其紅色部分的組織形式如圖5.3所示。

classInit (memPartClassId, sizeof (PARTITION),

OFFSET (PARTITION, objCore), (FUNCPTR) memPartCreate,

(FUNCPTR) memPartInit, (FUNCPTR) memPartDestroy)初始化圖中的內(nèi)存對(duì)象類memPartClass,其代碼如下:

STATUS classInit

(

    OBJ_CLASS   *pObjClass,    /* pointer to object class to initialize */

    unsigned    objectSize,     /* size of object */

    int         coreOffset,           /* offset from objCore to object start */

    FUNCPTR     createRtn,    /* object creation routine */

    FUNCPTR     initRtn,          /* object initialization routine */

    FUNCPTR     destroyRtn    /* object destroy routine */

)

{

    /* default memory partition is system partition */

    pObjClass->objPartId       = memSysPartId;     /* partition to allocate from */

    pObjClass->objSize           = objectSize;    /* record object size */

    pObjClass->objAllocCnt   = 0;            /* initially no objects */

    pObjClass->objFreeCnt    = 0;            /* initially no objects */

    pObjClass->objInitCnt      = 0;            /* initially no objects */

    pObjClass->objTerminateCnt  = 0;            /* initially no objects */

    pObjClass->coreOffset    = coreOffset;   /* set offset from core */

    /* initialize object methods */

    pObjClass->createRtn     = createRtn;    /* object creation routine */

    pObjClass->initRtn            = initRtn;          /* object init routine */

    pObjClass->destroyRtn   = destroyRtn;  /* object destroy routine */

    pObjClass->showRtn                 = NULL;    /* object show routine */

    pObjClass->instRtn           = NULL;             /* object inst routine */

    /* 初始化內(nèi)存對(duì)象類memPartClass為合法的對(duì)象類 */

//內(nèi)存對(duì)象類memPartClass指向其wind內(nèi)核的元類classClass

    objCoreInit (&pObjClass->objCore, classClassId);

    return (OK);

}

接著由memPartInit (&memSysPartition, pPool, poolSize)初始化內(nèi)存隊(duì)列類memPartClass的實(shí)例對(duì)象memSysPartition,并將配置的內(nèi)存池分配給系統(tǒng)分區(qū)memSysPartition。

6.2.2.3 memPartInit (&memSysPartition, pPool, poolSize)分析

memPartInit()將初始化系統(tǒng)分區(qū)memSysPartition,并將配置的內(nèi)存池分配給系統(tǒng)分區(qū)memSysPartition,是代碼實(shí)現(xiàn)如下:

void memPartInit ( FAST PART_ID partId, char *pPool, unsigned poolSize )

{

    /*初始化分區(qū)描述符 */

    bfill ((char *) partId, sizeof (*partId), 0);

    partId->options          = memPartOptionsDefault;

    partId->minBlockWords = sizeof (FREE_BLOCK) >> 1;

    /* initialize partition semaphore with a virtual function so semaphore

     * type is selectable.  By default memPartLibInit() will utilize binary

     * semaphores while memInit() will utilize mutual exclusion semaphores

     * with the options stored in _mutexOptionsMemLib.

     */

//通過(guò)調(diào)用一個(gè)函數(shù)指針memPartSemInitRtn,來(lái)初始化分區(qū)描述符的信號(hào)量,采用這種方

//式的好處是信號(hào)量類型的選擇是可選的。默認(rèn)情況下

//memPartLib庫(kù)中將其初始化二進(jìn)制信號(hào)量,但是memInit()將會(huì)使用存放在

//_mutexOptionsMemLib中的屬性值來(lái)初始化互斥信號(hào)量。

    (* memPartSemInitRtn) (partId);

    dllInit (&partId->freeList);                          /*初始化空閑鏈表 */

    objCoreInit (&partId->objCore, memPartClassId);          /* initialize core */

    (void) memPartAddToPool (partId, pPool, poolSize);

}

分析:

系統(tǒng)分區(qū)memSysPartition的屬性初始化為:

memPartOptionsDefault          =  MEM_ALLOC_ERROR_LOG_FLAG |

MEM_BLOCK_ERROR_LOG_FLAG |

MEM_BLOCK_ERROR_SUSPEND_FLAG |

MEM_BLOCK_CHECK;

memSysPartition的分區(qū)做小大小為sizeof (FREE_BLOCK)個(gè)字節(jié),在Pentium平臺(tái)為16個(gè)字節(jié)。

6.2.2.4 將初始內(nèi)存塊加入系統(tǒng)分區(qū)內(nèi)存池中

VxWorks調(diào)用函數(shù)memPartAddToPool (&memSysPartition, pPool, poolSize)來(lái)完成這一功能,我們來(lái)分析這一函數(shù)的具體實(shí)現(xiàn)過(guò)程:

我們要加入的內(nèi)存塊區(qū)域是從0x3c3788(即end+1000) 到0x1ff d8f0(即0x200 0000-0x2710)用于vxWorks的初始化內(nèi)存分區(qū),大小為0x1ff d8f0-0x3c3788=0x1C3A168=29598056字節(jié)

從0x3c3788開始的8個(gè)字節(jié)作為塊的開頭,其初始化代碼如下:

pHdrStart                   = (BLOCK_HDR *) pPool;// 0x3c3788

pHdrStart->pPrevHdr = NULL;

pHdrStart->free     = FALSE;

pHdrStart->nWords   = sizeof (BLOCK_HDR) >> 1;//nWorks=8>>1=4

從0x3c3790開始的8個(gè)字節(jié),其初始化代碼如下:

pHdrMid           = NEXT_HDR (pHdrStart);// 0x3c3788

pHdrMid->pPrevHdr   = pHdrStart;

pHdrMid->free       = TRUE;

pHdrMid->nWords     = (poolSize - 2 * sizeof (BLOCK_HDR)) >> 1;

//(29598056-2*8)/2=14799020=0xE1D0AC

由于pHdrMid->free       = TRUE,所以0x3c3790出的值為0x80e1d0ac。

由于通過(guò)BLOCK_HDR的結(jié)構(gòu)體中,nWords和free共用了4個(gè)字節(jié)的長(zhǎng)度,nWords占用了低0~30bit位,free占用了第31bit位。

typedef struct blockHdr            /* BLOCK_HDR */

{

struct blockHdr *     pPrevHdr;         /* pointer to previous block hdr */

unsigned           nWords : 31;    /* size in words of this block */

unsigned           free   : 1;       /* TRUE = this block is free */

} BLOCK_HDR;

所以其在內(nèi)存中的布局如圖5.5。

VxWorks內(nèi)核解讀-5

圖5.5 內(nèi)存塊頭在內(nèi)存中的布局

以上是內(nèi)存塊的頭部的設(shè)置,我們?cè)诳聪挛膊康脑O(shè)置,對(duì)要加入的內(nèi)存塊區(qū)域是從0x3c3788(即end+1000) 到0x1ff d8f0(即0x200 0000-0x2710)用于vxWorks的初始化內(nèi)存分區(qū),最后的8個(gè)字節(jié)表示的塊頭的初始化結(jié)果。

特別需要注意的塊頭地址0x1ffd8e8,是從初始化內(nèi)存分區(qū)尾端分出8個(gè)字節(jié),其初始化代碼如下:

pHdrEnd            = NEXT_HDR (pHdrMid);//指向的是0x373C90

pHdrEnd->pPrevHdr   = pHdrMid;

pHdrEnd->free       = FALSE;

pHdrEnd->nWords     = sizeof (BLOCK_HDR) >> 1;

其初始化的結(jié)構(gòu),正如圖5.6的內(nèi)存區(qū)域所示。

VxWorks內(nèi)核解讀-5

圖5.6 內(nèi)存塊尾部結(jié)構(gòu)布局

從上面的初始化過(guò)程,我們看到vxWorks把內(nèi)存塊區(qū)域是從0x3c3788(即end+1000) 到0x1ff d8f0(即0x200 0000-0x2710)用于vxWorks的初始化內(nèi)存分區(qū)加入的分區(qū)描述符memSysPartition.freeList中去時(shí),真正加入的區(qū)域從送0x3c3788+8到0x1ff d8f0-8這個(gè)區(qū)域,即從這個(gè)初始分區(qū)的開頭和末尾處分別劃出了一個(gè)8字節(jié)的區(qū)域填寫兩個(gè)內(nèi)存塊的頭結(jié)構(gòu)BLK_HDR。以表示與初始化分區(qū)相鄰的兩個(gè)內(nèi)存塊,只不過(guò)這兩個(gè)內(nèi)存塊只有頭部,數(shù)據(jù)部分為0。這樣做的目的是為了將來(lái)在釋放0x3c3788+8開始的內(nèi)存塊時(shí),可以0x3c3788位置的空白塊合并;以及在釋放以0x1ff d8f0-8結(jié)束的內(nèi)存塊時(shí)可以和0x1ff d8f0位置的空白內(nèi)存塊合并。以使得vxWorks分區(qū)內(nèi)存管理模塊對(duì)0x3c3788+8開始的內(nèi)存塊和0x1ff d8f0-8結(jié)束的內(nèi)存塊才操作進(jìn)行統(tǒng)一。此時(shí)sysMemPartition的 布局如圖5.7所示。

VxWorks內(nèi)核解讀-5

圖5.7 sysMemPartition的 內(nèi)部布局

通過(guò)上面的描述,memPartAddToPool (&memSysPartition, pPool, poolSize)的具體實(shí)現(xiàn)如下:

STATUS memPartAddToPool ( PART_ID partId,      char *pPool,   unsigned poolSize )

{

    FAST BLOCK_HDR *pHdrStart;

    FAST BLOCK_HDR *pHdrMid;

    FAST BLOCK_HDR *pHdrEnd;

    char *          tmp;

    int             reducePool;                 /*內(nèi)存池的實(shí)際減少量*/

    if (OBJ_VERIFY (partId, memPartClassId) != OK)//驗(yàn)證partId合法性

         return (ERROR);

    /*確保內(nèi)存池的實(shí)際開始地址是4字節(jié)對(duì)齊(假設(shè)是Pentium平臺(tái)) */

    tmp       = (char *) MEM_ROUND_UP (pPool);          /* 獲取實(shí)際的其實(shí)地址 */

    reducePool = tmp - pPool;

    if (poolSize >= reducePool)                       /* 調(diào)整內(nèi)存池的長(zhǎng)度*/

        poolSize -= reducePool;

    else

        poolSize = 0;

    pPool     = tmp;//調(diào)整內(nèi)存池的開始位置

    /*

*確保內(nèi)存池大小poolSize是4字節(jié)的整數(shù)倍,并且至少包含3個(gè)空閑內(nèi)存塊的塊頭和

*1個(gè)空閑內(nèi)存塊(僅有包含一個(gè)塊頭)

     */

    poolSize = MEM_ROUND_DOWN (poolSize);

    if (poolSize < ((sizeof (BLOCK_HDR) * 3) + (partId->minBlockWords * 2)))

         {

         errno = S_memLib_INVALID_NBYTES;

        return (ERROR);

         }

    /* 初始化化3個(gè)內(nèi)存塊頭 -

     * 內(nèi)存塊的開頭和結(jié)束各有一個(gè)塊頭,并且還有一個(gè)代碼真正的內(nèi)存塊 */

    pHdrStart                   = (BLOCK_HDR *) pPool;

    pHdrStart->pPrevHdr = NULL;

    pHdrStart->free     = FALSE;

    pHdrStart->nWords   = sizeof (BLOCK_HDR) >> 1;

    pHdrMid           = NEXT_HDR (pHdrStart);

    pHdrMid->pPrevHdr   = pHdrStart;

    pHdrMid->free       = TRUE;

pHdrMid->nWords     = (poolSize - 2 * sizeof (BLOCK_HDR)) >> 1;

//中間的內(nèi)存塊頭代碼真正的內(nèi)存塊,其大小應(yīng)除去開頭和結(jié)尾兩個(gè)內(nèi)存塊頭結(jié)構(gòu)占用

//的內(nèi)存

    pHdrEnd            = NEXT_HDR (pHdrMid);

    pHdrEnd->pPrevHdr   = pHdrMid;

    pHdrEnd->free       = FALSE;

    pHdrEnd->nWords     = sizeof (BLOCK_HDR) >> 1;

    semTake (&partId->sem, WAIT_FOREVER);

//從中我們可以看出sysMemPartition中的信號(hào)量是保證空閑塊鏈表必須被互斥訪問(wèn)

    dllInsert (&partId->freeList, (DL_NODE *) NULL, HDR_TO_NODE (pHdrMid));

    partId->totalWords += (poolSize >> 1);

    semGive (&partId->sem);

    return (OK);

}

分析:這里有必要分析一下將內(nèi)存塊插入系統(tǒng)分區(qū)sysMemPartition的空閑鏈表freeList的操作:

dllInsert (&partId->freeList, (DL_NODE *) NULL, HDR_TO_NODE (pHdrMid));

內(nèi)存池的第二個(gè)內(nèi)存塊塊(pHdrMid指向)插入freeList,這里存在一個(gè)強(qiáng)制類型轉(zhuǎn)換過(guò)程。

typedef struct blockHdr            /* 內(nèi)存塊頭BLOCK_HDR */

{

struct blockHdr *     pPrevHdr;/* pointer to previous block hdr */

unsigned           nWords : 31;    /* size in words of this block */

unsigned           free   : 1;       /* TRUE = this block is free */

} BLOCK_HDR;

空閑內(nèi)存塊頭結(jié)構(gòu)是在BLOCK_HDR的基礎(chǔ)上,加上DL_NODE類型的成員變量node。

typedef struct                    /* 空閑內(nèi)存塊FREE_BLOCK */

{

struct

{

struct blockHdr *   pPrevHdr;       /* pointer to previous block hdr */

unsigned      nWords : 31;/* size in words of this block */

unsigned      free   : 1;       /* TRUE = this block is free */

} hdr;

DL_NODE                   node;                 /* freelist links */

} FREE_BLOCK;

因此其加入sysMemPartition.freeList的過(guò)程如下:

dllInsert (&partId->freeList, (DL_NODE *) NULL, HDR_TO_NODE (pHdrMid));

#define HDR_TO_NODE(pHdr)        (& ((FREE_BLOCK *) pHdr)->node),其組合示意如圖5.8所示。

VxWorks內(nèi)核解讀-5

圖5.8 空閑塊頭示意圖

5.2.3 VxWorks內(nèi)存分配機(jī)制

VxWorks中的動(dòng)態(tài)內(nèi)存分配采用最先匹配(First-Fit)算法,即從空閑鏈表中查找內(nèi)存塊,然后從高地址開始查找,當(dāng)找到第一個(gè)滿足分配請(qǐng)求的空閑內(nèi)存塊時(shí),就分配所需內(nèi)存并修改該空閑塊的大小。空閑塊的剩余部分仍然保留在空閑鏈表中。當(dāng)從大的內(nèi)存塊中分配出新的內(nèi)存塊時(shí),需要將新內(nèi)存塊頭部的一塊空間(稱為“塊頭”)用來(lái)保存分配、回收和合并等操作的信息,因此,實(shí)際占用的內(nèi)存大小是分配請(qǐng)求的內(nèi)存大小與塊頭開銷之和。

6.2.3.1 malloc()分配內(nèi)存機(jī)制

分配函數(shù)malloc()返回的地址是分配請(qǐng)求所獲可用內(nèi)存的起始地址,為優(yōu)化性能,malloc()會(huì)自動(dòng)對(duì)齊邊界,分配的內(nèi)存的起始地址和大小都是邊界之的整數(shù)倍,因此有可能造成內(nèi)部碎片。塊頭的開銷與處理器的體系結(jié)構(gòu)有關(guān),對(duì)于X86體系結(jié)構(gòu)來(lái)說(shuō),,塊頭的開銷是8字節(jié),通常這部分會(huì)被自動(dòng)以4字節(jié)邊界對(duì)齊,以減少出現(xiàn)碎片的可能并優(yōu)化性能。

void *malloc (size_t nBytes )

 {

    return (memPartAlloc (&memSysPartition, (unsigned) nBytes));

 }

malloc()是memPartAlloc()的進(jìn)一步封裝,并且從系統(tǒng)內(nèi)存分區(qū)memSysPartition分配內(nèi)存。

memPartAlloc()函數(shù)如下:

void *memPartAlloc (PART_ID partId, unsigned nBytes)

{

    return (memPartAlignedAlloc (partId, nBytes, memDefaultAlignment));

}

memPartAlloc()又是memPartAlignedAlloc()的進(jìn)一步封裝,其中memDefaultAlignment在Pentium平臺(tái)是4字節(jié)。

memPartAlignedAlloc()從指定的系統(tǒng)分區(qū)memSysPartition中分配nBytes字節(jié)的內(nèi)存,分配的字節(jié)數(shù)必須要被memDefaultAlignment整除,其中memDefaultAlignment必須為2的冪數(shù)。

首次適應(yīng)算法就體現(xiàn)在函數(shù)memPartAlignedAlloc()的實(shí)現(xiàn)上:

void *memPartAlignedAlloc (PART_ID    partId, unsigned int nBytes, unsigned int alignment)

{

    FAST unsigned nWords;

    FAST unsigned nWordsExtra;

    FAST DL_NODE *      pNode;

    FAST BLOCK_HDR * pHdr;

    BLOCK_HDR *          pNewHdr;

    BLOCK_HDR            origpHdr;

    if (OBJ_VERIFY (partId, memPartClassId) != OK)

                  return (NULL);

    /* 實(shí)際分配的內(nèi)存大小為請(qǐng)求分配內(nèi)存大小+塊頭大小 */

    nWords = (MEM_ROUND_UP (nBytes) + sizeof (BLOCK_HDR)) >> 1;

    /*檢查是否溢出,如果溢出,設(shè)置errno,并返回NULL*/

    if ((nWords << 1) < nBytes)

         {

                  if (memPartAllocErrorRtn != NULL)

                      (* memPartAllocErrorRtn) (partId, nBytes);

                  errnoSet (S_memLib_NOT_ENOUGH_MEMORY);

//如果分區(qū)設(shè)置了MEM_ALLOC_ERROR_SUSPEND_FLAG,則當(dāng)前請(qǐng)求內(nèi)存的任務(wù)被阻塞

                  if (partId->options & MEM_ALLOC_ERROR_SUSPEND_FLAG)

             {

                      if ((taskIdCurrent->options & VX_UNBREAKABLE) == 0)

                                     taskSuspend (0);                         /* 阻塞自己*/

             }

                  return (NULL);

         }

//如果當(dāng)前請(qǐng)求分配的內(nèi)存小于分區(qū)允許的最小內(nèi)存(Pentium平臺(tái),8個(gè)字節(jié)),則以最小內(nèi)

//存為準(zhǔn)

    if (nWords < partId->minBlockWords)

         nWords = partId->minBlockWords;

    /* 獲取分區(qū)信號(hào)量,互斥范圍分區(qū)空閑塊鏈表,在這里如果信號(hào)量已經(jīng)被占用,則當(dāng)

*前請(qǐng)求分配內(nèi)存的任務(wù)將會(huì)被阻塞,等待該信號(hào)量釋放,這也是malloc()會(huì)導(dǎo)致被調(diào)用*任務(wù)阻塞的根源*/

    semTake (&partId->sem, WAIT_FOREVER);

    /* 首次適應(yīng)算法,就體現(xiàn)在下面的鏈表查找中 */

    pNode = DLL_FIRST (&partId->freeList);

/* 我們需分配一個(gè)空閑的塊,并帶有額外的空間用于對(duì)齊,最壞的情況是我們需要一*個(gè)對(duì)齊的額外字節(jié)數(shù)

      */

    nWordsExtra = nWords + alignment / 2;

    FOREVER

         {

                  while (pNode != NULL)

             {

//如果當(dāng)前塊大于包含額外對(duì)齊字節(jié)空間的請(qǐng)求字節(jié)數(shù),或者

//當(dāng)前塊和請(qǐng)求的字節(jié)數(shù)剛好一致,并且滿足對(duì)齊條件

//則當(dāng)前塊滿足要求

             if ((NODE_TO_HDR (pNode)->nWords > nWordsExtra) ||

                     ((NODE_TO_HDR (pNode)->nWords == nWords) &&

                     (ALIGNED (HDR_TO_BLOCK(NODE_TO_HDR(pNode)), alignment))))

                     break;

             pNode = DLL_NEXT (pNode);

             }

             //如果找不到滿足要求的當(dāng)前塊

         if (pNode == NULL)

         {

             semGive (&partId->sem);

             if (memPartAllocErrorRtn != NULL)

                            (* memPartAllocErrorRtn) (partId, nBytes);

             errnoSet (S_memLib_NOT_ENOUGH_MEMORY);

//默契情況下memLibInit()中設(shè)置memSysPartition.options如下表所示

// memPartOptionsDefault     = MEM_ALLOC_ERROR_LOG_FLAG |

//                                   MEM_BLOCK_ERROR_LOG_FLAG |

//                                   MEM_BLOCK_ERROR_SUSPEND_FLAG |

//                                   MEM_BLOCK_CHECK

             if (partId->options & MEM_ALLOC_ERROR_SUSPEND_FLAG)

                  {

                            if ((taskIdCurrent->options & VX_UNBREAKABLE) == 0)

                               taskSuspend (0);                         /* suspend ourselves */

                   }

             return (NULL);

          }

//找到滿足的內(nèi)存塊

         pHdr = NODE_TO_HDR (pNode);

         origpHdr = *pHdr;

//從當(dāng)前塊中分配出用戶指定的內(nèi)存,需要注意的是vxWorks從空閑塊的末端開始分配捏成

//這樣的好處是,余下的部分可以仍然在空閑鏈表中。如果memAlignedBlockSplit()返回為

//NULL,意味著將會(huì)使第一個(gè)內(nèi)存塊余下的部分調(diào)小,而不能放在空閑鏈表中,

//這種情況下,vxWorks會(huì)繼續(xù)嘗試使用下一個(gè)內(nèi)存塊

         pNewHdr =

             memAlignedBlockSplit (partId, pHdr, nWords, partId->minBlockWords,

                                       alignment);

         if (pNewHdr != NULL)

             {//找到并分配處用戶指定的內(nèi)存塊,退出循環(huán)

             pHdr = pNewHdr;                       /* give split off block */

             break;

             }

         //當(dāng)前塊不合適的話,繼續(xù)嘗試

         pNode = DLL_NEXT (pNode);

         }

    /*標(biāo)記分配出來(lái)的空閑塊為非空閑 */

    pHdr->free = FALSE;

    /* 更新memSysPartition分區(qū)的統(tǒng)計(jì)量 */

    partId->curBlocksAllocated++;

    partId->cumBlocksAllocated++;

    partId->curWordsAllocated += pHdr->nWords;

    partId->cumWordsAllocated += pHdr->nWords;

//是否信號(hào)量

    semGive (&partId->sem);

//跳過(guò)塊頭,返回可用內(nèi)存的地址

    return ((void *) HDR_TO_BLOCK (pHdr));

}

這里比較復(fù)雜的函數(shù)是,當(dāng)memPartAlignedAlloc()找到合適的塊后,從該空閑塊分割出用戶所需要內(nèi)存所考慮的情況,下面我們會(huì)分析這個(gè)分割函數(shù)。

6.2.3.2 內(nèi)存塊的分割

分配內(nèi)存時(shí),首先找到一個(gè)合適的空閑內(nèi)存塊,然后從該塊上分割出一個(gè)新塊(包含頭部)。最后把該新塊的內(nèi)存區(qū)域返回給用戶,因此頭部對(duì)用戶的透明的。

分配時(shí),有時(shí)為了對(duì)齊,會(huì)將一個(gè)塊一分為三,第一塊是申請(qǐng)后剩余空間構(gòu)成的塊,第二塊是申請(qǐng)塊的目標(biāo)塊,第三塊是用第二塊為了對(duì)齊而剩余的內(nèi)存空間構(gòu)成的塊。如果第三塊太小,則不將其獨(dú)立出來(lái),而是作為第二塊的一部分。

示意圖如5.9所示。

VxWorks內(nèi)核解讀-5

圖5.9 內(nèi)存塊分割示意圖

memAlignedBlockSplit()函數(shù)是空閑內(nèi)存塊分割的核心函數(shù),設(shè)從A塊申請(qǐng)nWords字節(jié)的內(nèi)存大小,要求alignment字節(jié)對(duì)齊,其代碼分析如下:

(1)將從A塊的尾部endOfBlock往上偏移nWords-BLOCK_HDR字節(jié)處pNewBlock作為新塊的頭部地址。

(2)pNewBlock往上偏移,以使之a(chǎn)lignment字節(jié)對(duì)齊。這一步導(dǎo)致申請(qǐng)塊的后面出現(xiàn)了剩余空間,正如上圖中所示。

(3)計(jì)算塊A的剩余空間blockSize=(char *) pNewHdr - (char *) pHdr,若blockSize小于最小塊大小minWords,且新塊的起始地址pNewHdr等于塊A的起始地址pHdr,那么新快就從塊A的起始地址開始,即將塊A分配出去,否則申請(qǐng)失敗。若blockSize大于最小塊minWords,則更新塊A的大小,即塊A仍然存在只是其內(nèi)存空間變小了。

(4)計(jì)算新塊尾部剩余多小空間endOfBlock - (UINT) pNewHdr - nWords,若剩余的空間小于最小塊大小minWords,則將該剩余空間也分配給申請(qǐng)的塊,否則為該剩余空間構(gòu)建一個(gè)新塊,并插入內(nèi)存分區(qū)的空閑鏈表。

(5)返回pNewHdr。

memAlignedBlockSplit()實(shí)現(xiàn)如下:

LOCAL BLOCK_HDR *memAlignedBlockSplit

(

    PART_ID partId,

    BLOCK_HDR *pHdr,

    unsigned nWords,            /* 需要分割出來(lái)的字?jǐn)?shù),包含塊頭 */

    unsigned minWords,          /* 所允許的最小字?jǐn)?shù) */

    unsigned alignment                          /* 邊界對(duì)齊數(shù) */

)

{

    FAST BLOCK_HDR *pNewHdr;

    FAST BLOCK_HDR *pNextHdr;

    FAST char *endOfBlock;

    FAST char *pNewBlock;

    int blockSize;

    /*計(jì)算出當(dāng)前塊的末尾位置 */

    endOfBlock = (char *) pHdr + (pHdr->nWords * 2);

    /* 計(jì)算出新塊的起始位置 */

//通過(guò)memPartAlignedAlloc()調(diào)用函數(shù)的分析,我們指定nWords中起始已經(jīng)包含了

//塊頭的位置,所以這里在分配內(nèi)存是只考慮實(shí)際使用的內(nèi)存。

    pNewBlock = (char *) ((unsigned) endOfBlock -

                   ((nWords - sizeof (BLOCK_HDR) / 2) * 2));

    /* 通過(guò)邊界向內(nèi)對(duì)齊調(diào)整內(nèi)存塊起始位置,這將使得分配的內(nèi)存偏大 */

    pNewBlock = (char *)((unsigned) pNewBlock & ~(alignment - 1));

//將確定的內(nèi)存塊起始位置假設(shè)塊頭大小,這里才考慮進(jìn)了塊頭的大小

    pNewHdr = BLOCK_TO_HDR (pNewBlock);

    /* 分割之后剩下的塊的大小 */

    blockSize = ((char *) pNewHdr - (char *) pHdr) / 2;

    if (blockSize < minWords)

         {

//如果分割之后剩下的內(nèi)存塊,小于分區(qū)規(guī)定的最小內(nèi)存,并且切換分割出去的內(nèi)存塊

//恰好就是原來(lái)的內(nèi)存塊,則將原來(lái)的內(nèi)存塊從空閑塊鏈表中刪除

//否則,分割之后剩余的內(nèi)存太小,不足以繼續(xù)掛載空閑塊鏈表上,則函數(shù)返回NULL;

// memPartAlignedAlloc()將會(huì)嘗試這從下一個(gè)空閑塊中繼續(xù)分割

     if (pNewHdr == pHdr)

         dllRemove (&partId->freeList, HDR_TO_NODE (pHdr));

     else

         return (NULL);

         }

    else

         {

         pNewHdr->pPrevHdr = pHdr;

         pHdr->nWords = blockSize;

         }

//檢查由于新塊地址對(duì)齊導(dǎo)致的多出來(lái)的內(nèi)存碎片,是否足夠大

//足夠足夠大,則單獨(dú)作為一個(gè)空閑塊插入空閑塊鏈表;

//否則并入新分割出來(lái)的塊中。

    if (((UINT) endOfBlock - (UINT) pNewHdr - (nWords * 2)) < (minWords * 2))

         {

         /* 將產(chǎn)生的碎片全部并入新分割出來(lái)的塊中 */

         pNewHdr->nWords = (endOfBlock - pNewBlock + sizeof (BLOCK_HDR)) / 2;

         pNewHdr->free     = TRUE;

         /*調(diào)整后面的空閑塊,使其指向新分割出來(lái)的塊*/

         NEXT_HDR (pNewHdr)->pPrevHdr = pNewHdr;

         }

    else

         {

         /* the extra bytes are big enough to be a fragment on the free list -

          * first, fix up the newly allocated block.

          */

//余下的碎片最夠大,首先讓其成為一個(gè)單獨(dú)的塊

         pNewHdr->nWords = nWords;

         pNewHdr->free     = TRUE;

         /*將這個(gè)單獨(dú)的塊加入空閑塊鏈表*/

         pNextHdr = NEXT_HDR (pNewHdr);

         pNextHdr->nWords = ((UINT) endOfBlock - (UINT) pNextHdr) / 2;

         pNextHdr->pPrevHdr = pNewHdr;

         pNextHdr->free = TRUE;

//加入空閑塊鏈表

         dllAdd (&partId->freeList, HDR_TO_NODE (pNextHdr));       

         /* fix next block to point to the new fragment on the free list */

//調(diào)整新塊后面的空閑鏈表

         NEXT_HDR (pNextHdr)->pPrevHdr = pNextHdr;

         }

    return (pNewHdr);

}

分析:在vxWorks的內(nèi)存池中,所有的內(nèi)存塊,無(wú)論是空閑塊還是非空閑塊,均是通過(guò)其每一個(gè)塊內(nèi)部的第一個(gè)塊頭和最后一個(gè)塊頭將內(nèi)存池中的所有塊連接成一個(gè)整體;

就單個(gè)內(nèi)存塊而言,緊接著前面第一個(gè)塊頭后一個(gè)塊頭才代表著該內(nèi)存塊中處于收尾兩個(gè)塊頭大小的那塊內(nèi)存;

并且這第二內(nèi)存塊頭和空閑塊頭相比僅少了DL_NODE成員域,用于鏈入空閑鏈表。

示意圖如下:

typedef struct blockHdr            /* BLOCK_HDR */

{

struct blockHdr *     pPrevHdr;         /* pointer to previous block hdr */

unsigned           nWords : 31;    /* size in words of this block */

unsigned           free   : 1;       /* TRUE = this block is free */

} BLOCK_HDR;

typedef struct                    /* FREE_BLOCK */

{

struct

{

struct blockHdr *   pPrevHdr;       /* pointer to previous block hdr */

unsigned      nWords : 31;/* size in words of this block */

unsigned      free   : 1;       /* TRUE = this block is free */

} hdr;

DL_NODE                   node;                 /* freelist links */

} FREE_BLOCK;

正因?yàn)槿绱?,在第二個(gè)內(nèi)存塊后面只需要額外包含sizeof(DN_NODE)大小的空間,兩個(gè)類型就可以互相裝換。

換句話說(shuō)內(nèi)存塊除去收尾塊頭,只要剩下的內(nèi)存可以存放一個(gè)空閑頭的大小,這個(gè)內(nèi)存塊就

可以參與管理。

這也是為什么memSysPartition必須強(qiáng)調(diào)最小內(nèi)存必須為一個(gè)空閑塊頭的大小。

5.2.3.3 內(nèi)存塊的釋放free()

內(nèi)存釋放時(shí),根據(jù)塊頭中的信息判斷相鄰的內(nèi)存塊是否空閑,如果相鄰內(nèi)存塊空閑,將進(jìn)行內(nèi)存塊合并,并修改空閑塊長(zhǎng)度;否則就把新釋放的內(nèi)存插入到空閑鏈表中。這里,空閑鏈表采用的是雙鏈表的數(shù)據(jù)結(jié)構(gòu),它將緊臨塊頭的8個(gè)字節(jié)(2個(gè)指針長(zhǎng)度)用來(lái)存放雙鏈表的指針,所有由malloc()分配的內(nèi)存必須顯式調(diào)用free()進(jìn)行釋放,以避免內(nèi)存溢出,如圖5.10。

VxWorks內(nèi)核解讀-5

圖5.10 內(nèi)存釋放

free()將malloc()分配的內(nèi)存塊釋放到內(nèi)存分區(qū)memSysPartition的空閑塊鏈表中,其代碼如下:

void free (void *ptr )

{

    (void) memPartFree (&memSysPartition, (char *) ptr);

}

free()是memPartFree()的封裝。

如果pPrevHdr指向的前一塊也為空閑塊,則只需擴(kuò)大前一塊的內(nèi)存空間大小即可;否則,把空閑塊鏈入分區(qū)的freeList鏈表??臻e塊鏈入的時(shí)候,還要判斷能否與后一塊內(nèi)存空間合并(當(dāng)前塊的頭部地址加上當(dāng)前塊的大小即為下一塊的頭部地址)。

內(nèi)存釋放的關(guān)鍵函數(shù)是memPartFree,設(shè)內(nèi)存分區(qū)為partId,釋放的內(nèi)存地址為pBlock,分析如下:

(1)通過(guò)BLOCK_TO_HDR宏,獲得內(nèi)存頭pHdr。

(2)獲得內(nèi)存塊大小pHdr->nWords。

(3)如果前一塊空閑PREV_HDR (pHdr)->free為TRUE,則擴(kuò)大前一塊即可,擴(kuò)大后的前一塊為當(dāng)前塊。否則將該塊作為獨(dú)立空閑塊鏈入分區(qū)空閑鏈表partId->freeList。

(4)檢查后一塊是否空閑,若是則將后一塊并入到當(dāng)前塊上。

(5)更新后一塊的pPrevHdr指針指向當(dāng)前塊。

代碼如下:

STATUS memPartFree( PART_ID partId, char *pBlock )

{

    FAST BLOCK_HDR *pHdr;

    FAST unsigned   nWords;

    FAST BLOCK_HDR *pNextHdr;

    if (OBJ_VERIFY (partId, memPartClassId) != OK)

         return (ERROR);

    if (pBlock == NULL)

                   return (OK);                                  /* ANSI C compatibility */

    pHdr   = BLOCK_TO_HDR (pBlock);

    /* 獲取memSysPartition的信號(hào)量 */

    semTake (&partId->sem, WAIT_FOREVER);

    /* optional check for validity of block */

    if ((partId->options & MEM_BLOCK_CHECK) &&

        !memPartBlockIsValid (partId, pHdr, FALSE))

         {

         semGive (&partId->sem);                           /* release mutual exclusion */

         if (memPartBlockErrorRtn != NULL)

             (* memPartBlockErrorRtn) (partId, pBlock, "memPartFree");

         if (partId->options & MEM_BLOCK_ERROR_SUSPEND_FLAG)

             {

             if ((taskIdCurrent->options & VX_UNBREAKABLE) == 0)

                   taskSuspend (0);

             }

         errnoSet (S_memLib_BLOCK_ERROR);

         return (ERROR);

         }

    nWords = pHdr->nWords;

//檢查該內(nèi)存塊前面的內(nèi)存塊是否空閑,如果空閑將該內(nèi)存塊合并到前面的空閑內(nèi)存塊中

    if (PREV_HDR (pHdr)->free)

         {

         pHdr->free = FALSE;                          /* this isn't a free block */

         pHdr = PREV_HDR (pHdr);                          /* coalesce with prev block */

         pHdr->nWords += nWords;

         }

    else

         {//否則單獨(dú)作為空閑塊,摻入memSysPartition分區(qū)的空閑塊鏈表

         pHdr->free = TRUE;                   /* add new free block */

         dllInsert (&partId->freeList, (DL_NODE *) NULL, HDR_TO_NODE (pHdr));

         }

//檢查與其相鄰的后面的內(nèi)存塊釋放空閑,如果空閑將其后面的內(nèi)存塊從空閑隊(duì)列中

//刪除,并并入當(dāng)前空閑塊。

    pNextHdr = NEXT_HDR (pHdr);

    if (pNextHdr->free)

         {

         pHdr->nWords += pNextHdr->nWords;   /* coalesce with next */

         dllRemove (&partId->freeList, HDR_TO_NODE (pNextHdr));

         }

    /* fix up prev info of whatever block is now next */

    NEXT_HDR (pHdr)->pPrevHdr = pHdr;

    /* adjust allocation stats */

    partId->curBlocksAllocated--;

    partId->curWordsAllocated -= nWords;

    semGive (&partId->sem);

    return (OK);

}

5.3 VxWorks與動(dòng)態(tài)內(nèi)存相關(guān)的API

表5.2 VxWorks5.5動(dòng)態(tài)內(nèi)存管理相關(guān)API

VxWorks內(nèi)核解讀-5

VxWorks 5.5允許用戶建立并管理自己的內(nèi)存分區(qū)(Memory Partition),并提供相應(yīng)的函數(shù),如下表5.3所示。

表5.3 創(chuàng)建內(nèi)存分區(qū)函數(shù)

VxWorks內(nèi)核解讀-5

每個(gè)分區(qū)又各自的分區(qū)ID。ID實(shí)際是指向分區(qū)內(nèi)存管理數(shù)據(jù)結(jié)構(gòu)mem_part的指針,定義在memLib.h中。

typedef struct mem_part *PART_ID;

5.4 VxWorks內(nèi)存分配機(jī)制總結(jié)

在可用內(nèi)存塊上建立空閑塊,并交給內(nèi)存分區(qū)進(jìn)行管理。內(nèi)存分區(qū)記錄了空閑塊鏈表和一些統(tǒng)計(jì)信息。申請(qǐng)時(shí)從分區(qū)的空閑鏈表中找到合適的空閑塊,并從該空閑塊上分割出一塊交給用戶使用,當(dāng)剩余空閑塊太小時(shí),整塊交給用戶使用。釋放時(shí),判斷能否進(jìn)行前向、后向合并,若可以則直接合并,否則作為一個(gè)獨(dú)立塊鏈入空閑鏈表中。

用戶申請(qǐng)到的內(nèi)存塊,在它的前面隱藏了該塊的基本信息(BLOCK_HDR,DL_NODE等信息),內(nèi)存釋放就是根據(jù)這個(gè)隱藏信息進(jìn)行的。

內(nèi)存塊之間建立了兩種聯(lián)系:pPrevHdr元素存放了物理上連續(xù)的前一塊內(nèi)存的地址,用于空閑內(nèi)存塊的合并;鏈表節(jié)點(diǎn)node用于將空閑內(nèi)存塊鏈入到分區(qū)的freeList鏈表中,用于空閑塊的管理??臻e內(nèi)存塊合并機(jī)制保證了任意兩個(gè)空閑內(nèi)存塊在物理上不是連續(xù)的,從而避免了內(nèi)存碎片。

內(nèi)存分配采用首次適應(yīng)(first fit)算法,即每次分配時(shí),都是分配空閑鏈表中第一個(gè)合適塊。設(shè)申請(qǐng)nWords字節(jié)的內(nèi)存大小,要求alignment字節(jié)對(duì)齊,那么合適的要求是內(nèi)存塊的可用空間大于nWords + alignment / 2或者內(nèi)存塊可用空間等于nWords且alignment字節(jié)對(duì)齊。

這里需要注意的是當(dāng)初始任務(wù)終結(jié)時(shí),為初始化任務(wù)預(yù)留的內(nèi)存10000字節(jié),是作為一個(gè)獨(dú)立的空閑塊,加入到分區(qū)freeList鏈表中,為啥vxWorks沒(méi)有這樣做?這是因?yàn)樵跒槌跏既蝿?wù)tTaskRoot分配??臻g時(shí),分區(qū)內(nèi)存管理機(jī)制還沒(méi)有建立,即malloc()服務(wù)例程還不能實(shí)現(xiàn),只能手動(dòng)進(jìn)行分配,當(dāng)初始任務(wù)被刪除時(shí),為其手動(dòng)分配的任務(wù)只能作為一個(gè)獨(dú)立的空閑塊,加入到分區(qū)freeList鏈表中,而不能和其它內(nèi)存開合并。

至此,本篇介紹的VxWorks內(nèi)存管理機(jī)制就告一段落了,嚴(yán)格意義上本篇介紹的內(nèi)存管理模塊,比如malloc()/free()已經(jīng)超過(guò)了Wind內(nèi)存管理的范疇,但是為了描述的完整性,稍微增加了一點(diǎn)點(diǎn)O(∩_∩)O~。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多