在vmm文件夹中的vmmArea.c文件是实现页表控制块反查功能。通过给定的地址(虚拟或者物理)可以查找到对应页面控制块LW_VMM_PAGE。 系统是通过哈希红黑树来实现这个功能的。首先在zone包含了一个哈希表。通过哈希索引值找到对应的红黑树根。也zone页面管理类似,页面关系是通过哈希表插到对应的链表头。有几个比较重要的结构体。LW_VMM_AREA 是一个非常重要的结构,它包含了一个哈希表。这是一个红黑树树哈希表。
下图中index代表的就是哈希表的索引值,哈希表就是LW_VMM_AREA中的
+------+----+-------+---------+-------+------+--------+----------+-------+
| | | | | | | | | |
| index| | | | | | | | |
+-+---------+-------+---------+-------+------+--------+----------+-------+
|
|
|
|
| +-----------------+
| | PLW_VMM_PAGE |
| | |
| +---------------+ +-----------------+
| | | | |
| |PLW_VMM_PAGE | +--------->+LW_TREE_RB_NODE |
| +---------------+ | +-----------------+
| | +-----+ | |
+----->+LW_TREE_RB_NODE+---+ | |
+---------------+ | | |
| | | | |
| | | +-----------------+
+---------------+ |
|
|
| +-----------------+
| | PLW_VMM_PAGE |
| | |
| +-----------------+
| | |
| | LW_TREE_RB_NODE |
+----------->+ |
+-----------------+
| |
| |
| |
+-----------------+
__areaVirtualSpaceInit
此函数是初始化虚拟地址的反查表。主要是创建哈希表。
/*********************************************************************************************************
** 函数名称: __areaVirtualSpaceInit
** 功能描述: 初始化虚拟地址空间管理块
** 输 入 : pvirdes 虚拟空间描述
** 输 出 : ERROR CODE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
ULONG __areaVirtualSpaceInit (LW_MMU_VIRTUAL_DESC pvirdes[])
{
REGISTER PLW_MMU_CONTEXT pmmuctx = __vmmGetCurCtx();
INT i;
addr_t ulAddr = __ARCH_ULONG_MAX;
addr_t ulEnd = 0;
for (i = 0; i < LW_CFG_VMM_VIR_NUM; i++) {
if (pvirdes[i].VIRD_stSize == 0) {
break;
}
if (ulAddr > pvirdes[i].VIRD_ulVirAddr) {
ulAddr = pvirdes[i].VIRD_ulVirAddr;
}
if (ulEnd < (pvirdes[i].VIRD_ulVirAddr + pvirdes[i].VIRD_stSize)) {
ulEnd = (pvirdes[i].VIRD_ulVirAddr + pvirdes[i].VIRD_stSize);
}
}
if (ulAddr == __ARCH_ULONG_MAX) {
_ErrorHandle(ENOSPC);
return (ENOSPC);
}
return (__areaSpaceInit(&pmmuctx->MMUCTX_vmareaVirSpace, ulAddr, (size_t)(ulEnd - ulAddr)));
}
虚拟地址描述符指定了虚拟地址的起始地址和大小。查找其中地址最小值和最大值。相减就是size。从mmu上下文中获得LW_VMM_AREA,虚拟地址是一个LW_VMM_AREA。物理地址是多个。
然后调用__areaSpaceInit函数。
此函数根据地址长度选择合适大小的哈希表。
选择合适哈希表大小后,给关键数据赋值,然后分配哈希表空间。
__areaPhysicalSpaceInit
此函数是物理地址反查表初始化。
ULONG __areaPhysicalSpaceInit (LW_MMU_PHYSICAL_DESC pphydesc[])
{
REGISTER ULONG ulError = ERROR_NONE;
static ULONG ulZone = 0; /* 可多次追尾添加内存 */
INT i;
for (i = 0; ; i++) {
if (pphydesc[i].PHYD_stSize == 0) {
break;
}
switch (pphydesc[i].PHYD_uiType) {
case LW_PHYSICAL_MEM_DMA:
case LW_PHYSICAL_MEM_APP:
if (ulZone < LW_CFG_VMM_ZONE_NUM) {
ulError = __areaSpaceInit(&_G_vmareaZoneSpace[ulZone],
pphydesc[i].PHYD_ulPhyAddr,
pphydesc[i].PHYD_stSize);
if (ulError) {
_ErrorHandle(ulError);
return (ulError);
}
ulZone++;
}
break;
default:
break;
}
}
return (ERROR_NONE);
}
和虚拟地址初始化一样都会调用__areaSpaceInit函数。不过物理地址对物理地址描述符中的DMA和APP属性才创建反差表。并且指定了多个LW_VMM_AREA。
__areaInsertPage
此函数是往红黑树中插入一个页面管理块。
/*********************************************************************************************************
** 函数名称: __areaInsertPage
** 功能描述: 向地址空间中注册一个页面控制块
** 输 入 : pvmarea 地址空间管理块
** ulAddr 起始地址
** pvmpage 页面控制块
** 输 出 : NONE
** 全局变量:
** 调用模块:
*********************************************************************************************************/
static VOID __areaInsertPage (PLW_VMM_AREA pvmarea, addr_t ulAddr, PLW_VMM_PAGE pvmpage)
{
ULONG ulHashIndex;
REGISTER PLW_TREE_RB_ROOT ptrbr;
REGISTER PLW_TREE_RB_NODE *pptrbnRoot;
REGISTER PLW_TREE_RB_NODE ptrbnParent = LW_NULL;
PLW_VMM_PAGE pvmpageTemp;
ulHashIndex = __VMM_AREA_HASH_INDEX(pvmarea, ulAddr);
ptrbr = &pvmarea->AREA_ptrbrHash[ulHashIndex]; /* 确定 hash 表位置 */
pptrbnRoot = &ptrbr->TRBR_ptrbnNode;
while (*pptrbnRoot) {
ptrbnParent = *pptrbnRoot;
pvmpageTemp = _TREE_ENTRY(ptrbnParent, LW_VMM_PAGE, PAGE_trbnNode);
if (ulAddr < pvmpageTemp->PAGE_ulPageAddr) {
pptrbnRoot = _tree_rb_get_left_addr(*pptrbnRoot);
} else if (ulAddr > pvmpageTemp->PAGE_ulPageAddr) {
pptrbnRoot = _tree_rb_get_right_addr(*pptrbnRoot);
} else {
return; /* 不可能运行到这里 */
}
}
_tree_rb_link_node(&pvmpage->PAGE_trbnNode,
ptrbnParent,
pptrbnRoot); /* 插入到树中 */
_Tree_Rb_Insert_Color(&pvmpage->PAGE_trbnNode,
&pvmarea->AREA_ptrbrHash[ulHashIndex]); /* 维持平衡 */
}
在插入前首先要计算哈希表的索引值index。SylixOS 使用的计算公式如下:
算出索引值index 后就可以获得对应的红黑树树根。对于根节点第一次插入时根结点TRBR_ptrbnNode指针为空。所以不进入while循环,直接调用_tree_rb_link_node,_Tree_Rb_Insert_Color两个函数,将vmpage中红黑树节点地址赋值给TRBR_ptrbnNode。当不是首次插入时按照正常差分查找,然后加入到树中,最后平衡树。
__areaSearchPage
此函数是在哈希红黑树中查找对应的addr,先找到红黑树node对应的结构体母体vmpage,然后地址和vmpage中的地址比对大小,不断查找,相等时就返回这个vmpage。
__areaUnlinkPage
此函数是从红黑树中删除一个vmpage节点,首先还是先获得哈希表中索引值,获得红黑树的根节点,然后调用红黑树库函数删除当前节点。