热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HylicOS内存抽象

HylicOS已经完成了部分硬件抽象层的工作,包括MMU的初始化并对虚拟内存到物理内存做了映射,创建了页表目录。对串口进行了初始化,实现了


HylicOS已经完成了部分硬件抽象层的工作,包括MMU的初始化并对虚拟内存到物理内存做了映射,创建了页表目录。对串口进行了初始化,实现了printk格式化打印函数,方便了日志输出和程序调试。建立了异常向量表。

现在要做的是内存管理部分。
对内存资源的管理最终要实现对内存完全地掌控:知道哪些地方有可用合法内存。能够快速找到一块想要的大小的内存,这块内存想大就大,想小就小。

为了能够掌握硬件可用的所有物理内存资源,需要对所有的物理内存资源进行描述:
考虑到移植性,每个硬件平台上的物理内存的分布都是不同的,对不同硬件平台的物理内存进行描述也必将不同,所以不能写死。
mini2440有一块64M的SDRAM,有一些地址空间映射的是设备寄存器,有些是空洞,依据这些属性对每一类内存抽象出一个结构体

typedef enum
{
ADRSPACE_NOT = 0,
ADRSPACE_IO = 1,
ADRSPACE_SDRAM = 2,
ADRSPACE_RAM = 3,
ADRSPACE_ROM = 4,
ADRSPACE_NORFLASH = 5,
ADRSPACE_NANDFLASH = 6,
}phymem_type_e;
typedef struct
{
phymem_type_e phymem_type;
u32_t dev_type;
adr_t adr_start;
adr_t adr_end;
}phymemspce_t;

其中:
phymem_type成员指示这块物理内存的类型,dev_type是指示设备的类型
adr_start成员描述这块物理内存的起始地址,adr_end成员描述这块物理内存的终止地址。

有了这个结构体作为模版,就可以将各块物理内存创建为一个个对象,为了更好地管理这些物理内存对象,可以将他们放到一个数组中:

HAL_DEFGLOB_VARIABLE(phymemspce_t,phymemspce)[PLFM_ADRSPCE_NR] = {
{ADRSPACE_NORFLASH,0,0,0x001fffff},
{ADRSPACE_IO,0,0x08000000,0x0800000f},
{ADRSPACE_IO,0,0x10000000,0x1000000f},
{ADRSPACE_IO,0,0x19000000,0x190fffff},
{ADRSPACE_IO,0,0x20000000,0x2000000f},
{ADRSPACE_IO,0,0x28000000,0x28000007},
{ADRSPACE_IO,0,0x29000000,0x29000007},
{ADRSPACE_SDRAM,0,0x30000000,0x33ffffff},
{ADRSPACE_IO,0,0x48000000,0x48000030},
{ADRSPACE_IO,0,0x49000000,0x49000058},
{ADRSPACE_IO,0,0x4a000000,0x4a00001c},
{ADRSPACE_IO,0,0x4b000000,0x4b0000e0},
{ADRSPACE_IO,0,0x4c000000,0x4c000018},
{ADRSPACE_IO,0,0x4d000000,0x4d000060},
{ADRSPACE_IO,0,0x4e000000,0x4e00003c},
{ADRSPACE_IO,0,0x4f000000,0x4f0000a0},
{ADRSPACE_IO,0,0x50000000,0x50008028},
{ADRSPACE_IO,0,0x51000000,0x51000040},
{ADRSPACE_IO,0,0x52000000,0x5200026f},
{ADRSPACE_IO,0,0x53000000,0x53000008},
{ADRSPACE_IO,0,0x54000000,0x54000010},
{ADRSPACE_IO,0,0x55000000,0x55000012},
{ADRSPACE_IO,0,0x56000000,0x560000cc},
{ADRSPACE_IO,0,0x57000040,0x5700008b},
{ADRSPACE_IO,0,0x58000000,0x58000014},
{ADRSPACE_IO,0,0x59000000,0x59000034},
{ADRSPACE_IO,0,0x5a000000,0x5a000043},
{ADRSPACE_IO,0,0x5b000000,0x5b00001c},
{ADRSPACE_NOT,DEV_TYPE,0,0}};

这个数组的定义和初始化应该放在一个全局位置(hal_global.c中),后面很多模块都要使用到它。
对于这个HAL_DEFGLOB_VARIABLE宏,是仿UNIX美学所在。用于定义只在一个本文件定义,其他文件则为外部声明的方式
其定义在hal_global.c对应的头文件中hal_global_t.h为:

#define HAL_DEFGLOB_VARIABLE(vartype,varname) EXTERN (__attribute__((".head.text"))) vartype varname

这里EXTERN宏被定义为extern
除此之外还要再定义一个规则在hal_global.h里:

#ifndef HALGLOBAL_HEAD
#undef EXTERN
#define EXTERN
#endif

当一个模块include了hal_global_t.h和hal_global.h时,使用HAL_DEFGLOB_VARIABLE定义变量,此时有EXTERN的定义,就是外部声明。
当一个模块include了hal_global_t.h和hal_global.h前额外的#define HALGLOBAL_HEAD,此时没有EXTERN宏的定义,就是定义。

这种做法可以在一处定义变量,其他使用它的地方只做外部声明。



现在物理地址被分类且描述出来了,操作系统需要服务程序,在程序需要内存资源时给出一块内存的起始地址,所以在操作系统的设计阶段就应该将辽阔物理内存切割为一块块单元区域,在这里我们的系统每一个区域都是4MB,用一个结构体描述,保存该单元区域的起始地址和结束地址,还有这个区域内部更加细分的分区情况,更包括了细分后的分区的占用情况:

typedef struct mmapdsc
{
list_h_t map_list; //list to connection every memory map describtor
adr_t map_phyadr; //memory physical start address
adr_t map_phyadrend; //memory physical end address
u32_t map_flg; //used to determine next domain(memory map attribution status)
u32_t map_attrstatus; //map_flg=6 map_attrstatus[0] 4MB=>4MB 0=>available | 1=>busy
//map_flg=5 map_attrstatus[1:0] 4MB=>2MB+2MB
//map_flg=4 map_attrstatus[3:0] 4MB=>1MB+1MB+1MB+1MB
spinlock_t map_lock;
}mmapdsc_t;

从每个成员的注释中已经可以一窥究竟,以表格形式列出更加清晰:


memberfunction
map_list区域单元彼此需要链接起来,该成员作为链表节点
map_phyadr区域单元的起始物理地址
map_phyadrend区域单元的结束物理地址
map_flg决定了该块区域单元以怎样精度细分
map_attrstatus细分后区域的位图描述
map_lock保护该结构的自旋锁

现在这个结构体还只是一纸空谈,需要实际为每个区域单元分配实际物理地址,一一绑定到每个实际的4MB地址上去。需要写一个初始化函数。在此之前我们需要一个更加宏观的数据结构,将前面两种结构的信息都存储进去:
我们将这个结构整体叫做机器数据结构,由下面注释可见,核心就是
mmapdscadr, mmapdscnum以及phyadrdsc,phyadrdscnum四个成员。前俩是对每一个4MB物理内存单元的抽象和描述,后俩是对整个物理内存的描述。

typedef struct mach
{
spinlock_t mlock;
list_h_t mlist;
adr_t krlposramstart; //kernel position start at ram
adr_t krlposramend; //kernel position end at ram
mmapdsc_t* mmapdscadr; //memory map describe start addr at ram
uint_t mmapdscnum; //memory map describe number
uint_t phyadrdscnum; //physical address space describer number
phymemspce_t* phyadrdsc; //physical address space describer start address
// ilnedsc_t* ilnedsc;
uint_t ilnedscnum;
// intfltdsc_t* intfltdsc; //interrupt source describer address
//uint_t intfltnum; //interrupt source describer number
}mach_t;

首先需要定义一个mach_t类型的全局变量,同样是使用HAL_DEFGLOB_VARIABLE宏定义在hal_global.c中:

HAL_DEFGLOB_VARIABLE(mach_t,osmach);

对机器数据结构单独开一个C文件进行初始化与操作,machine.c被创建出来以承担这个责任:
对机器数据结构的初始化,首先是krlposramstart,这个成员用KRNL_INRAM_START赋值,指示内核在RAM中的起始位置
值为0x30000000,顺带,我们开一下HyliOS各个部分在内存的排布吧
在这里插入图片描述
橙色部分是中断向量表所在位置,也是内核起始位置,0X3000_0000
绿色部分是MMU的页表目录所在位置,0x3000_4000
红色部分是内核代码段起始位置,0x3000_8000。
__end_kernel这个宏在链接文件里定义,由链接器符号链接完之后自动计算,下面代码也引用了这个宏(记得带这个“&”)
在__end_kernel之后就是mmapdsc结构组成的数组所在了,mmapdsc的结束位置一下子不好确定。

void machine_init(void)
{
osmach_init(&osmach);
return;
}
void osmach_init(mach_t* initp)
{
hal_spinlock_init(&initp->mlock);
list_init(&initp->mlist);
initp->krlposramstart = KRNL_INRAM_START;
initp->krlposramend = (adr_t) (&__end_kernel);
// initp->mmapdsc bitmap address
initp->mmapdscadr = (mmapdsc_t* )(ALIGN((uint_t)(&__end_kernel),4096));
initp->mmapdscnum = 0;
initp->phyadrdsc = phymemspce;
return;
}

现在再来写初始化mmapdsc_t结构数组的代码


void init_mmapdsc(mach_t* mach_spedsc)
{
phymemspce_t* pmsp = mach_spedsc->phyadrdsc; //build in hal_global.c
uint_t space_num = mach_spedsc->phyadrdscnum;
uint_t mapdsccnt = 0;
//init all mmapdsc
for(uint_t i&#61;0; i<space_num; i&#43;&#43;)
{
//just sdram can be used to shared with app
if(pmsp[i].phymem_type &#61;&#61; ADRSPACE_SDRAM)
{
mapdsccnt &#61; init_core_mmapdsc(pmsp[i].adr_start,
pmsp[i].adr_end,
mach_spedsc->mmapdscadr,
mapdsccnt ) ;
}
}
mach_spedsc->mmapdscnum &#61; mapdsccnt;
//added mmapdsc thus need to modify kernel end address in ram
mach_spedsc->krlposramend &#61; (adr_t)( (uint_t)mach_spedsc->mmapdscadr &#43; \
mapdsccnt*sizeof(mach_spedsc));
}
uint_t init_core_mmapdsc(adr_t base_adr,adr_t end_adr,mmapdsc_t* mmapdsc,uint_t mapdsc_curindex)
{
uint_t curindex &#61; mapdsc_curindex;
adr_t tmpadr &#61; end_adr;
for( ;base_adr < end_adr
;base_adr &#43;&#61; EACH_MAP_SIZE, curindex&#43;&#43;)
{
if((base_adr&#43;EACH_MAP_SIZE) < end_adr)
{
tmpadr &#61; ( base_adr&#43;EACH_MAP_SIZE) - 1;
}
else
{
tmpadr &#61; end_adr;
}
mmapdsc_init(mmapds[curindex],
base_adr,
tmpadr,
0,
MAP_FLAGS_VAL(0,MAPF_ACSZ_4MB,MAPF_SZ_4MB));
}
return curindex;
}
void mmapdsc_init(mmapdsc_t* mmp,adr_t phyadr,adr_t phyadre,u32_t attrstatus,u32_t flgs)
{
list_init(&mmp->map_list);
hal_spinlock_init(&mmp->mlock);
mmp->map_phyadr &#61; phyadr;
mmp->map_phyadrend &#61; phyadre;
mmp->map_attrstatus &#61; attrstatus;
mmp->map_flg &#61; flgs;
return;
}






推荐阅读
  • Android JSON基础,音视频开发进阶指南目录
    Array里面的对象数据是有序的,json字符串最外层是方括号的,方括号:[]解析jsonArray代码try{json字符串最外层是 ... [详细]
  • 本文介绍了brain的意思、读音、翻译、用法、发音、词组、同反义词等内容,以及脑新东方在线英语词典的相关信息。还包括了brain的词汇搭配、形容词和名词的用法,以及与brain相关的短语和词组。此外,还介绍了与brain相关的医学术语和智囊团等相关内容。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • Html5-Canvas实现简易的抽奖转盘效果
    本文介绍了如何使用Html5和Canvas标签来实现简易的抽奖转盘效果,同时使用了jQueryRotate.js旋转插件。文章中给出了主要的html和css代码,并展示了实现的基本效果。 ... [详细]
  • mac php错误日志配置方法及错误级别修改
    本文介绍了在mac环境下配置php错误日志的方法,包括修改php.ini文件和httpd.conf文件的操作步骤。同时还介绍了如何修改错误级别,以及相应的错误级别参考链接。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • 【重识云原生】第四章云网络4.8.3.2节——Open vSwitch工作原理详解
    2OpenvSwitch架构2.1OVS整体架构ovs-vswitchd:守护程序,实现交换功能,和Linux内核兼容模块一起,实现基于流的交换flow-basedswitchin ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 本文介绍了[从头学数学]中第101节关于比例的相关问题的研究和修炼过程。主要内容包括[机器小伟]和[工程师阿伟]一起研究比例的相关问题,并给出了一个求比例的函数scale的实现。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 本文介绍了一个React Native新手在尝试将数据发布到服务器时遇到的问题,以及他的React Native代码和服务器端代码。他使用fetch方法将数据发送到服务器,但无法在服务器端读取/获取发布的数据。 ... [详细]
author-avatar
shiorinrin_933_893
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有