MMU在VxWorks中的实现

MMU在VxWorks中的实现
1.内存管理单元MMU
VxWorks提供两级虚内存支持:与VxWorks捆绑在一起的基本级和需要可选产品VxVMI的完全级。

1.1指令和数据MMU
PowerPC MMU允许指令和数据MMU被分别允许或禁止。在SELECT_MMU下的属性窗口的参数表中的参数USER_I_MMU_ENABLE和 USER_D_MMU_ENABLE缺省是被允许的。为了允许/禁止一个或两个MMU,选择合适的参数并设置/删除TRUE。

1.2 60X内存映射
PowerPC 603和604的MMU两种模式的内存映射。第一种是BAT模式,它允许在大小从128KB到256MB的内存块映射进一个BAT寄存器。第二种是段模式,它可以将内存映射为4KB的页。Tornado支持这两种内存模式。

1.2.1 603/604的块地址变换模式
一个BAT寄存器的大小是两个32位的值。对于PowerPC 603和604,有八个BAT寄存器实现:四个用于指令MMU,四个用于数据MMU。

在sysLib.c中定义的sysBatDesc[]处理BAT寄存器配置。寄存器由MMU库中的初始化软件设置。缺省这些寄存器被清零。

用于填充数组sysBatDesc[]的所有配置常量在适合于PowerPC 603和604的目录installDir/target/h/arch/ppc/mmu603Lib.h中被定义。

1.2.2 603/604的段模式
这种模式指定每一内存页的配置。整个物理内存由sysLib.c中定义的数据结构数组sysPhysMemDesc[]描叙。这个数组由每一页或每一组页的配置常量组成。所有的配置常量在表7-1中定义。虚内存接口对PowerPC虚内存页是有用的。

使用在表7-1中列出的用于每一页或每一组页的常量VM_STATE_CACHEABLE来设置cache为copy-back模式。

除VM_STATE_CACHEABLE之外,还支持以下常量:

VM_STATE_CACHEABLE_WRITETHROUGH
VM_STATE_MEM_COHERENCY
VM_STATE_MEM_COHERENCY_NOT
VM_STATE_GUARDED
VM_STATE_GUARDED_NOT
第一个常量设置页描叙符cache模式域为可cache的write-through模式。Cache的一致性和保护模式由其他常量控制。

页表大小依赖被映射的总的内存。被映射的内存越大,页表越大。在MMU库初始化期间,被映射的内存的总大小被计算,这允许动态决定页表大小。下表显示待映射的内存的总数和页表大小之间的对应关系。

表 9-4: 待映射的内存的总数和页表大小之间的对应关系

8 MB or less 64 KB
16 MB 128 KB
32 MB 56 KB
64 MB 512 KB
128 MB 1 MB
256 MB 2 MB
512 MB 4 MB
1 GB 8 MB
2 GB 16 MB
4 GB 32 MB
--------------------------------------------------------------------------------


2.虚拟内存(包括VxVMI选项)
对于带有MMU的板子,VxWorks提供虚拟内存的支持。捆绑虚拟内存支持提供标记不可cache的缓冲区的能力。这对内存在处理器间共享或发生DMA传送的多处理器环境是有用的。关于捆绑虚拟内存支持的信息,参考第三节:虚拟内存接口和vmBaseLib和cacheLib的引用入口(库函数)。

非捆绑虚拟内存支持可用作可选的组件VxVMI 。VxVMI 提供了设置文本段text和中断向量表为只读的能力,并且包括一组用于开发人员管理他们自己的虚拟内存上下文的子程序。关于VxVMI 的信息,参考第三节:虚拟内存接口和vmLib的引用入口(库函数)。


3.虚拟内存接口
3.1介绍
VxWorks 提供两级虚内存支持:基本级被与VxWorks捆绑在一起并且提供基于每一页的cache。完全级是非捆绑的,并且需要可选组件VxVMI。VxVMI提供文本段text和VxWorks中断向量表的写保护,并且提供一种体系结构无关的接口给CPU的内存管理单元MMU。关于怎样安装VxVMI的信息,参考Tornado入门(Tornado Getting Started)。

3.2基本虚内存支持
对于带有MMU的系统。 VxWorks允许你通过设置相关缓冲区不可cache来更有效的执行DMA及处理器间通信。当其他处理器或DMA设备正访问同一内存位置时,确保数据没有进行局部的缓冲是必要的。如果没有能力将内存的各部分设置为不可cache的,那么cache必须从全局上被关闭(导致性能下降)或缓冲区必须被手工地刷新/使无效。

通过在项目工具(project facility)VxWorks视图中选择INCLUDE_MMU_BASIC包含基本虚内存支持,参考3.3节:虚内存配制;也可以用 cacheDmaMalloc( )分配不可cache的缓冲区,参考cacheLib 的引用入口(库函数)。

3.3虚内存配置
以下讨论的配置适用于捆绑和非捆绑的虚内存支持。

在项目工具(project facility)中定义表7-1中的常量来反映你的系统配置。

表7-1: MMU配置常量

--------------------------------------------------------------------------------
常量描叙
--------------------------------------------------------------------------------
INCLUDE_MMU_BASIC 不带VxVMI选项的基本MMU支持。
INCLUDE_MMU_FULL 带VxVMI选项的完全MMU支持。
INCLUDE_PROTECT_TEXT 文本段保护(要求完全的MMU支持)。
INCLUDE_PROTECT_VEC_TABLE 中断向量表保护(要求完全的MMU支持)。
--------------------------------------------------------------------------------

对于你的处理器,合适的缺省页大小(4 KB或8KB)由你的BSP的VM_PAGE_SIZE定义。如果你因为某种原因必须改变这个值,重定义config.h中的VM_PAGE_SIZE。参考VxWorks程序员指南第八章配置和编译。

为设置内存为不可cache,必须有一种虚-到-物理的映射。在vmLib.h中的数据结构PHYS_MEM_DESC定义用于映射物理内存的参数。每块板的内存映射用sysPhysMemDesc(被声明为一个PHYS_MEM_DESC的数组)在sysLib.c中定义。除了定义内存页的初始状态之外, sysPhysMemDesc还定义用于映射虚-到-物理内存的虚地址。

修改sysPhysMemDesc以反映你的系统配置。比如,你可能需要增加没有包含在结构数组sysPhysMemDesc中的处理器间通信缓冲区的地址。

不包含在结构数组中的I/O设备和内存必须被映射和设置为不可cache。通常, devices and memory not already included in the structure must also be mapped and made noncacheable. In general, 板外内存区域被指定为不可cache。参考VxWorks网络程序员指南:数据连路层网络组件。

警告:在 sysPhysMemDesc中定义的内存区,必须是页对齐的,并且必须跨越完整的页。换句话说,结构PHYS_MEM_DESC 的最初三个域都必须恰好是VM_PAGE_SIZE 的倍数。如果指定的sysPhysMemDesc的元素不是页对齐的,则在VxWorks初始化期间将导致崩溃。

以下例子配置由使用共享内存的网络的多个CPU 组成。一块单独的内存板被用于这个共享内存池。因为这块内存不总是被映射,对于所有在网络上的板子,它必须被增加到sysPhysMemDesc中。内存起始于0x4000000,并且必须被设置为不可cache。

/* shared memory */
{
(void *) 0x4000000, /* virtual address */
(void *) 0x4000000, /* physical address */
0x20000, /* length */
/* initial state mask */
VM_STATE_MASK_VALID | VM_STATE_MASK_WRITABLE |VM_STATE_MASK_CACHEABLE,
/* initial state */
VM_STATE_VALID | VM_STATE_WRITABLE | VM_STATE_CACHEABLE_NOT
}

3.4通常的用法
这一节描叙VxVMI的通常用法和用于写保护文本段和中断向量表的配置。

VxVMI用MMU来保护内存区以免被重写,这由内存的写保护页完成。并不是所有的目标硬件支持写保护。对于大多体系结构,页大小是8K。尝试写一块被写保护的内存位置会导致一个总线出错。

当VxWorks被装入是时,所有text段是写保护的。其他用ld()装入的目标模块的text段被自动标记为只读的。当目标模块被装入时,被写保护的内存按页大小递增被分配。没有其他的步骤对于写保护应用程序代码是必须的。

在系统初始化期间,VxWorks写保护中断向量表。修改中断向量表的唯一办法是使用程序intConnect( ),在整个调用期间intConnect( )写允许中断向量表。

为包含写保护,在项目工具VxWorks视图中选择如下:

INCLUDE_MMU_FULL
INCLUDE_PROTECT_TEXT
INCLUDE_PROTECT_VEC_TABLE

3.5使用可编程的MMU
本章描叙了为了使用vmLib中的低级程序处理(manipulate)可编程的MMU所提供的工具(facility)。你可以设置数据为任务或代码段的私有数据,设置内存为不可cahe,设置内存的各部分为写保护的。用于实现虚内存的基本结构是虚内存上下文(VMC)。

对于VxVMI程序的汇总,参考vmLib的引用入口(库函数)。

3.5.1虚内存上下文(VMC)
虚内存上下文 (VM_CONTEXT, 在vmLib中定义)由一张转换表和其他用于映射虚地址到实地址的信息组成。多个虚地址上下文可以被创建和随意的被换入/换出。

Ø 全局虚内存

一些系统对象,诸如text段和信号量,必须是对所有在系统中的任务是可访问的,不管虚内存上下文是否是当前的。这些对象依靠全局虚内存被设置为可访问的。全局虚内存通过映射所有系统内的物理内存到虚内存空间中的相同地址被创建(这个映射被定义在sysPhysMemDesc中)。在缺省系统配置中,这最先在物理内存和全局虚内存之间给出一对一的关系。比如,虚地址0x5000映射到实地址0x5000。在某些体系结构中,可能使用 sysPhysMemDesc 来建立虚拟内存,以便虚到实地址的映射是一对一的。

全局虚内存可以根据所有虚内存上下文访问。对一个虚内存上下文中的全局映象所做的修改会出现在所有虚内存上下文中。在虚内存上下文被创建之前,用vmGlobalMap()增加所有全局内存。在虚内存上下文被创建之后增加的全局内存可能对已存在的上下文是不可用的。

Ø 初始化

全局虚内存被从usrRoot 调用的usrMmuInit()中的vmGlobalMapInit()初始化。程序usrMmuInit()在 installDir/target/src/config/usrMmuInit.c中,并且用sysPhysMemDesc创建全局虚内存,接着它创建一个缺省虚内存上下文,并设置该缺省上下文为当前的。它也可以随时随地使能MMU。

Ø 页状态

每一虚内存页(通常是8K)有一种与之关联的状态。一页可以是有效/的无效的,可写的/不可写的,或可cahe/不可cahe。对于相关的常量请看表7-2。

表7-2: 状态标志

--------------------------------------------------------------------------------
常量描叙
--------------------------------------------------------------------------------
VM_STATE_VALID Valid translation
VM_STATE_VALID_NOT Invalid translation
VM_STATE_WRITABLE Writable memory
VM_STATE_WRITABLE_NOT Read-only memory
VM_STATE_CACHEABLE Cacheable memory
VM_STATE_CACHEABLE_NOT Noncacheable memory
--------------------------------------------------------------------------------

l 有效性

有效状态指示虚到实转换是正确的。当转换表被初始化时,全局虚内存被标记为有效的。所有其他虚内存被初始化为无效的。

l 可写性

通过设置状态为不可写,页可以被设置为只读。这被VxWorks用于写保护所有text段。

l 可cache

内存页的cache可以通过设置状态标志为不可cache被禁止。这对处理器间共享的内存(包括DMA设备)是有用的。
可用程序vmStateSet()改变一页的状态。除了指定状态标志外,一个状态屏蔽必须描叙哪一标志需要被改变。在vmLib.h中指定的其他体系结构相关的状态请看表7-3。

表7-3: 状态屏蔽

--------------------------------------------------------------------------------
常量描叙
--------------------------------------------------------------------------------
VM_STATE_MASK_VALID Modify valid flag
VM_STATE_MASK_WRITABLE Modify write flag
VM_STATE_MASK_CACHEABLE Modify cache flag
--------------------------------------------------------------------------------

3.5.2私有虚内存
私有虚内存可以通过创建一个新的虚内存上下文。这对于通过使数据对于其他任务不可访问或通过限制对特定程序的访问来保护数据是有用的。虚内存上下文不会被自动地为任务创建,但可以被创建并按应用程序特定的方式被换入和换出。

在系统初始化时,一个缺省上下文被创建。所有任务使用这个缺省上下文。为了创建私有虚内存,一个任务必须用vmContextCreate ()创建一个新的虚内存上下文,并设置它为当前的。所有虚内存上下文共享在系统初始化时创建的全局映象。参考图7-1。在当前虚内存上下文中(包括全局虚内存)仅仅有效的虚内存被访问。在其他虚内存上下文中定义的虚内存不可访问。为设置其他内存上下文为当前的,使用vmCurrentSet()。

图7-1: 虚内存的全局映像



为创建一个新的虚到实的映像,使用vmMap();物理和虚地址都必须被预先决定。物理内存(它必须按页对齐)可以使用valloc ()获得。决定虚地址最容易的办法是使用vmGlobalInfoGet()来查找不是一个全局映像的虚页。关于这种方案,如果多个映像被要求,一个任务必须明确它自己的私有虚内存页以确保它不映射同一非全局地址两次。

当物理页被映射进虚空间的新区域时,该物理页可从两个不同的虚地址(一个被称为别名的条件)访问:最近映射的虚地址和以前的虚地址等价于全局虚内存中的一个物理地址。对于某些体系结构,这可能导致问题,因为这个 cache对于同一内存位置可能保持两个不同的值。为避免这种情况,可以用vmStateSet ()使在全局虚内存中的虚拟页无效。这也确保仅仅当包含新映像的虚内存上下文是当前的时,这些数据才可以访问。

图7-2描述两个私有虚内存上下文。新上下文(pvmc2)映射虚地址0x6000000到物理地址0x10000。为了避免从这个虚上下文的外界(pvmc1)访问这个地址,相应的物理地址(0x10000)必须被设置为无效的。如果使用地址0x10000来访问这块内存,一个总线出错发生,因为这个地址现在是无效的。

图7-2: 映射私有虚内存



事例7-1: 私有虚内存上下文
在以下代码事例中,私有虚内存上下文被用于从一个任务的私有内存区分配内存。程序contextSetup()创建一块私有虚内存上下文,它在一个上下文切换期间被设置为当前的。该虚内存上下文被保存在这个任务的TCB的域spare1中。切换异常分支(hook)被用于保存旧的上下文和安装该任务的私有上下文。注意:切换异常分支(hook)的使用增加了上下文切换的时间。一块用户定义的内存区被使用私有虚内存上下文创建。该内存区ID被保存于该任务TCB的域spare2中。任何没有私有虚内存上下文的任务必须调用contextSetup()。
如下是一个测试该代码的简单任务(A sample task to test the code is included)。


--------------------------------------------------------------------------------

/* contextExample.h - header file for vm contexts used by switch hooks */

#define NUM_PAGES (3)

--------------------------------------------------------------------------------

/* context.c - use context switch hooks to make task private context current */

#include /"vxWorks.h/"
#include /"vmLib.h/"
#include /"semLib.h/"
#include /"taskLib.h/"
#include /"taskHookLib.h/"
#include /"memLib.h/"
#include /"contextExample.h/"

void privContextSwitch (WIND_TCB *pOldTask, WIND_TCB *pNewTask);

/************************************************************************
*
* initContextSetup - install context switch hook
*
*/

STATUS initContextSetup ( )
{
/* Install switch hook */

if (taskSwitchHookAdd ((FUNCPTR) privContextSwitch) == ERROR)
return (ERROR);

return (OK);
}

/************************************************************************
*
* contextSetup - initialize context and create separate memory partition
*
* Call only once for each task that wants a private context.
*
* This could be made into a create-hook routine if every task on the
* system needs a private context. To use as a create hook, the code for
* installing the new virtual memory context should be replaced by simply
* saving the new context in spare1 of the task/'s TCB.
*/

STATUS contextSetup (void)
{
VM_CONTEXT_ID pNewContext;
int pageSize;
int pageBlkSize;
char * pPhysAddr;
char * pVirtAddr;
UINT8 * globalPgBlkArray;
int newMemSize;
int index;
WIND_TCB * pTcb;

/* create context */

pNewContext = vmContextCreate();

/* get page and page block size */

pageSize = vmPageSizeGet ();
pageBlkSize = vmPageBlockSizeGet ();
newMemSize = pageSize * NUM_PAGES;

/* allocate physical memory that is page aligned */

if ((pPhysAddr = (char *) valloc (newMemSize)) == NULL)
return (ERROR);

/* Select virtual address to map. For this example, since only one page
* block is used per task, simply use the first address that is not a
* global mapping. vmGlobalInfoGet( ) returns a boolean array where each
* element corresponds to a block of virtual memory.
*/

globalPgBlkArray = vmGlobalInfoGet();
for (index = 0; globalPgBlkArray[index] == TRUE; index++)
;
pVirtAddr = (char *) (index * pageBlkSize);

/* map physical memory to new context */

if (vmMap (pNewContext, pVirtAddr, pPhysAddr, newMemSize) == ERROR)
{
free (pPhysAddr);
return (ERROR);
}

/*
* Set state in global virtual memory to be invalid - any access to
* this memory must be done through new context.
*/

if (vmStateSet(pNewContext, pPhysAddr, newMemSize, VM_STATE_MASK_VALID,
VM_STATE_VALID_NOT) == ERROR)
return (ERROR);

/* get tasks TCB */

pTcb = taskTcb (taskIdSelf());

/* change virtual memory contexts */

/*
* Stash the current vm context in the spare TCB field -- the switch
* hook will install this when this task gets swapped out.
*/

pTcb->spare1 = (int) vmCurrentGet();

/* install new tasks context */

vmCurrentSet (pNewContext);

/* create new memory partition and store id in task/'s TCB */

if ((pTcb->spare2 = (int) memPartCreate (pVirtAddr,newMemSize)) == NULL)
return (ERROR);

return (OK);
}

/*******************************************************************
*
* privContextSwitch - routine to be executed on a context switch
*
* If old task had private context, save it. If new task has private
* context, install it.
*/

void privContextSwitch
(
WIND_TCB *pOldTcb,
WIND_TCB *pNewTcb
)

{
VM_CONTEXT_ID pContext = NULL;

/* If previous task had private context, save it--reset previous context. */

if (pOldTcb->spare1)
{
pContext = (VM_CONTEXT_ID) pOldTcb->spare1;
pOldTcb->spare1 = (int) vmCurrentGet ();

/* restore old context */

vmCurrentSet (pContext);
}

/*
* If next task has private context, map new context and save previous
* context in task/'s TCB.
*/

if (pNewTcb->spare1)
{
pContext = (VM_CONTEXT_ID) pNewTcb->spare1;
pNewTcb->spare1 = (int) vmCurrentGet();

/* install new tasks context */

vmCurrentSet (pContext);
}
}

--------------------------------------------------------------------------------

/* taskExample.h - header file for testing VM contexts used by switch hook */

/* This code is used by the sample task. */

#define MAX (10000000)

typedef struct myStuff {
int stuff;
int myStuff;
} MY_DATA;

--------------------------------------------------------------------------------

/* testTask.c - task code to test switch hooks */

#include /"vxWorks.h/"
#include /"memLib.h/"
#include /"taskLib.h/"
#include /"stdio.h/"
#include /"vmLib.h/"
#include /"taskExample.h/"

IMPORT char *string = /"test//n/";

MY_DATA *pMem;

/************************************************************************
*
* testTask - allocate private memory and use it
*
* Loop forever, modifying memory and printing out a global string. Use this
* in conjunction with testing from the shell. Since pMem points to private
* memory, the shell should generate a bus error when it tries to read it.
* For example:
* -> sp testTask
* -> d pMem
*/

STATUS testTask (void)
{
int val;
WIND_TCB *myTcb;

/* install private context */

if (contextSetup () == ERROR)
return (ERROR);

/* get TCB */

myTcb = taskTcb (taskIdSelf ());

/* allocate private memory */

if ((pMem = (MY_DATA *) memPartAlloc((PART_ID) myTcb->spare2,
sizeof (MY_DATA))) == NULL)
return (ERROR);

/*
* Forever, modify data in private memory and display string in
* global memory.
*/

FOREVER
{
for (val = 0; val <= MAX; val++)
{
/* modify structure */

pMem->stuff = val;
pMem->myStuff = val / 2;

/* make sure can access global virtual memory */

printf (string);

taskDelay (sysClkRateGet() * 10);
}
}
return (OK);
}

/************************************************************************
*
* testVmContextGet - return a task/'s virtual memory context stored in TCB
*
* Used with vmContextShow()1
to display a task/'s virtual memory context.
* For example, from the shell, type:
* -> tid = sp (testTask)
* -> vmContextShow (testVmContextGet (tid))
*/

VM_CONTEXT_ID testVmContextGet
(
UINT tid
)
{
return ((VM_CONTEXT_ID) ((taskTcb (tid))->spare1));
}

3.5.3非cache的内存
不支持总线动态监视的体系结构必须禁止用于处理器之间通信(或通过DMA设备)的内存cache。如果多处理器读取和写入一内存位置,你必须保证当CPU访问数据时,它正在使用最新的数据。如果cache被用于一个或多个CPU的系统中,在CPU的一个数据cache中可能有一个该数据的本地备份。在图7- 3的事例中,一个有多个CPU的系统共享数据,并且在系统上的一个CPU(CPU 0)高速缓存(cache)该共享数据。在CPU 0上的一个任务读数据[1],并且接着修改值[2];然而,当在另一个CPU(CPU 1)上的一个任务访问它[3]时,该新值可能还在cache中并且没有更新到内存。因此由CPU 1上的任务使用的值是旧值,并且没有反映由CPU 0上的任务所做的修改;那个值还在CPU 0的数据cache[2]中。

图7-3: 与数据cache相关的(with Data Caching)可能的问题的事例



为了禁止基于页的高速缓存(caching),使用vmStateSet();比如:

vmStateSet (pContext, pSData, len, VM_STATE_MASK_CACHEABLE, VM_STATE_CACHEABLE_NOT)

为分配不可cache的内存,参看cacheDmaMalloc()的引用入口。

3.5.4不可写的内存
内存可以被标记为不可写的。内存区可以使用vmStateSet ()被写保护来避免无意中做的访问。

这样做的一种用处是将数据模块的修改限制到一个具体的程序。如果一个数据模块是全局的但只读,任务可以读该模块但不能修改它。任何必须修改这个模块的任务必须调用相关的程序。在该程序内,该数据在整个程序调用期间被修改为可写的,这块内存被设置为VM_STATE_WRITABLE_NOT 。

事例7-2: 不可写内存
在这段代码事例中,为了修改由pData所指向数据结构,一个任务必须调用dataModify()。这个程序使内存可写,修改数据,并且设置内存返回到不可写。如果一个任务试图读该内存,它是成功的;然而,如果它试图在dataModify()之外修改数据,那么一个总线出错就会发生。


--------------------------------------------------------------------------------

/* privateCode.h - header file to make data writable from routine only */

#define MAX 1024

typedef struct myData
{
char stuff[MAX];
int moreStuff;
} MY_DATA;

--------------------------------------------------------------------------------

/* privateCode.c - uses VM contexts to make data private to a code segment */

#include /"vxWorks.h/"
#include /"vmLib.h/"
#include /"semLib.h/"
#include /"privateCode.h/"

MY_DATA * pData;
SEM_ID dataSemId;
int pageSize;

/***********************************************************************
*
* initData - allocate memory and make it nonwritable
*
* This routine initializes data and should be called only once.
*
*/

STATUS initData (void)
{
pageSize = vmPageSizeGet();

/* create semaphore to protect data */

dataSemId = semBCreate (SEM_Q_PRIORITY, SEM_EMPTY);

/* allocate memory = to a page */

pData = (MY_DATA *) valloc (pageSize);

/* initialize data and make it read-only */

bzero (pData, pageSize);
if (vmStateSet (NULL, pData, pageSize, VM_STATE_MASK_WRITABLE,
VM_STATE_WRITABLE_NOT) == ERROR)
{
semGive (dataSemId);
return (ERROR);
}

/* release semaphore */

semGive (dataSemId);
return (OK);
}

/********************************************************************
*
* dataModify - modify data
*
* To modify data, tasks must call this routine, passing a pointer to
* the new data.
* To test from the shell use:
* -> initData
* -> sp dataModify
* -> d pData
* -> bfill (pdata, 1024, /'X/')
*/

STATUS dataModify
(
MY_DATA * pNewData
)
{

/* take semaphore for exclusive access to data */

semTake (dataSemId, WAIT_FOREVER);

/* make memory writable */

if (vmStateSet (NULL, pData, pageSize, VM_STATE_MASK_WRITABLE,
VM_STATE_WRITABLE) == ERROR)
{
semGive (dataSemId);
return (ERROR);
}

/* update data*/

bcopy (pNewData, pData, sizeof(MY_DATA));

/* make memory not writable */

if (vmStateSet (NULL, pData, pageSize, VM_STATE_MASK_WRITABLE,
VM_STATE_WRITABLE_NOT) == ERROR)
{
semGive (dataSemId);
return (ERROR);
}

semGive (dataSemId);

return (OK);
}

3.5.5疑难解答
如果INCLUDE_MMU_FULL_SHOW被包含在项目工具VxWorks视图中,你可以使用vmContextShow()来在标准输出设备上显示一个虚内存上下文。在以下的事例中,当前虚内存上下文被显示。在0x0和0x59fff之间的虚地址是写保护的;从0xff800000到 0xffbfffff是不可cache的;并且从0x2000000到0x2005fff是私有的。所有有效的项被列出并被用一个V+标记,无效的项没有被列出。

-> vmContextShow 0
value = 0 = 0x0
这个输出被送到标准输出设备,如下所示:

VIRTUAL ADDR BLOCK LENGTH PHYSICAL ADDR STATE
0x0 0x5a000 0x0 W- C+ V+ (global)
0x5a000 0x1f3c000 0x5a000 W+ C+ V+ (global)
0x1f9c000 0x2000 0x1f9c000 W+ C+ V+ (global)
0x1f9e000 0x2000 0x1f9e000 W- C+ V+ (global)
0x1fa0000 0x2000 0x1fa0000 W+ C+ V+ (global)
0x1fa2000 0x2000 0x1fa2000 W- C+ V+ (global)
0x1fa4000 0x6000 0x1fa4000 W+ C+ V+ (global)
0x1faa000 0x2000 0x1faa000 W- C+ V+ (global)
0x1fac000 0xa000 0x1fac000 W+ C+ V+ (global)
0x1fb6000 0x2000 0x1fb6000 W- C+ V+ (global)
0x1fb8000 0x36000 0x1fb8000 W+ C+ V+ (global)
0x1fee000 0x2000 0x1fee000 W- C+ V+ (global)
0x1ff0000 0x2000 0x1ff0000 W+ C+ V+ (global)
0x1ff2000 0x2000 0x1ff2000 W- C+ V+ (global)
0x1ff4000 0x2000 0x1ff4000 W+ C+ V+ (global)
0x1ff6000 0x2000 0x1ff6000 W- C+ V+ (global)
0x1ff8000 0x2000 0x1ff8000 W+ C+ V+ (global)
0x1ffa000 0x2000 0x1ffa000 W- C+ V+ (global)
0x1ffc000 0x4000 0x1ffc000 W+ C+ V+ (global)
0x2000000 0x6000 0x1f96000 W+ C+ V+
0xff800000 0x400000 0xff800000 W- C- V+ (global)
0xffe00000 0x20000 0xffe00000 W+ C+ V+ (global)
0xfff00000 0xf0000 0xfff00000 W+ C- V+ (global)

3.5.6预防方法
被标记为全局的内存不能使用vmMap ()被重新映射。为增加到全局虚内存中,使用vmGlobalMap ()。关于增加全局虚内存的更多信息,参看章节3.52:私有虚内存。

在不同体系结构中,MMU的性能各不相同。实际上,某些体系结构可能导致系统变得不确定。更多的信息,参看适合你的硬件的体系结构特定的文档。


1: 这个程序(vmContextShow())没有被编译进Tornado shell。为了从Tornado shell中使用它,你必须在VxWorks的配置文件中定义INCLUDE_MMU_FULL_SHOW。参看Tornado用户指南:项目。当调用这个程序时,它的输出被送到标准输出设备。

你可能感兴趣的:(MMU在VxWorks中的实现)