分段 || 分页
分段与分页的问题我们在MMU那一部分说过,
添加链接描述
当时说主要考虑三个问题,
- 段长度可变,但物理地址长度固定
- 跟内存的交互时如果分段我们要把整个段都放入内存
- 假设段很长,物理段整块划分可能无法找到完整那么长的物理段,由此有很多碎片。
我们简单整理一下,还是这几个问题,表达的更清楚一些:
- 从表示方式与分配是否确定角度: 首先段长度不固定,那么我们无法用一个确定的数据结构去表示这个段;其次我们无法确定这个段是否已经被分配。而对应的,页的大小是固定的,我们用位图表示页的分配与释放。这个位图可用1表示第几个页已经被分配。0则表示空闲。不过该方式较为低效。
- 从内存碎片的利用角度:上面我们说到段的长度不固定,除了不好用数据结构表示这个段以外,段的大小不固定,更容易产生内存碎片。比如123段分配100 200 300个字节,然后第2个段释放了,第4段需要201个字节,刚才的2段却无法满足,随着运行的情况复杂起来,这种碎片会极大影响我们内存的利用效率。 可能说到这个地方你觉得,那对页的需求不也可能出现上面的情况么。确实是这样,但是页是由页表负责映射的,我们可以修改页表使连续的虚拟地址映射到非连续的物理页表上。
- 从内存和硬盘的数据交互角度:我们说过内存不足的时候我们需要把内存里不常用的内容写回硬盘,以此来释放内存。那么内存和硬盘交互的数据是段好还是页好? 如果是段,段长度不固定,导致写回硬盘的时间不固定,硬盘的空间分配空间也会有碎片,这样会造成系统性能抖动,反之如果用页就都是固定的,没有这个问题。
综上,我们使用分页模型。
页如何表示
把物理内存分成4KB大小的页,每一段从地址 x 开始到 x+0xFFF 这一段的物理内存空间。
但是我们要注意,内存的真实物理地址并不是连续的,比如有些部分可能是空的,有些地方放的是显存,有些地方放的是外设寄存器。
我们之前获取了物理内存空间布局信息,即之前内容中,e820map_t 结构数组,而且在内存初始化的时候,我们将其转换成了phymmarge_t 结构数组。
那么如何表示一个页?当然是用结构体。(用位图太过低效)
一个msadsc_t结构体是主体:
包括链表(方便将其挂载到其他数据结构中),自旋锁,内存空间地址的标志(描述结构体本身信息的结构体),物理地址的标志(描述具体物理位的结构体);
//内存空间地址描述符标志
typedef struct s_MSADFLGS
{
u32_t mf_olkty:2; //挂入链表的类型
u32_t mf_lstty:1; //是否挂入链表
u32_t mf_mocty:2; //分配类型,被谁占用了,内核还是应用或者空闲
u32_t mf_marty:3; //属于哪个区
u32_t mf_uindx:24; //分配计数
}__attribute__((packed)) msadflgs_t;
//物理地址和标志
typedef struct s_PHYADRFLGS
{
u64_t paf_alloc:1; //分配位
u64_t paf_shared:1; //共享位
u64_t paf_swap:1; //交换位
u64_t paf_cache:1;