操作系统实践之路——六、内存(1.如何划分与组织内存?)


前言
本章开始进行内存方面内容的讲解,本章主要讲解是如何划分与组织内存的。

1.分段还是分页来划分内存?

从内存管理角度分析

第一点,从表示方式和状态确定角度考虑。 段的长度大小不一,用什么数据结构表示一个段,如何确定一个段已经分配还是空闲呢?而页的大小固定,我们只需用位图就能表示页的分配与释放。

比方说,位图中第 1 位为 1,表示第一个页已经分配;位图中第 2 位为 0,表示第二个页是空闲,每个页的开始地址和大小都是固定的。

第二点,从内存碎片的利用看。 由于段的长度大小不一,更容易产生内存碎片。段与段之间存在着不大不小的空闲空间,内存总的空闲空间很多,但是放不下一个新段。

而页的大小固定,分配最小单位是页,页也会产生碎片,比如我需要请求分配 4 个页,但在内存中从第 1~3 个页是空闲的,第 4 个页是分配出去了,第 5 个页是空闲的。这种情况下,我们通过修改页表的方式,就能让连续的虚拟页面映射到非连续的物理页面。

第三点,从内存和硬盘的数据交换效率考虑, 当内存不足时,操作系统希望把内存中的一部分数据写回硬盘,来释放内存。这就涉及到内存和硬盘交换数据,交换单位是段还是页?

如果是段的话,其大小不一,A 段有 50MB,B 段有 1KB,A、B 段写回硬盘的时间也不同,有的段需要时间长,有的段需要时间短,硬盘的空间分配也会有上面第二点同样的问题,这样会导致系统性能抖动。如果每次交换一个页,则没有这些问题。

​ 还有最后一点,段最大的问题是使得虚拟内存地址空间,难于实施。

​ 综上,我们自然选择分页模式来管理内存,其实现在所有的商用操作系统都使用了分页模式管理内存。我们用 4KB 作为页大小,这也正好对应 x86 CPU 长模式下 MMU 4KB 的分页方式。

2.如何表示一个页?

​ 首先是把物理内存空间分成 4KB 大小页,这页表示从地址 x 开始到 x+0xFFF 这一段的物理内存空间,x 必须是 0x1000 对齐的。这一段 x+0xFFF 的内存空间,称为内存页

​ 真正的物理内存空间布局信息来源于 e820map_t 结构数组,之前的初始化中,我们已经将其转换成 phymmarge_t 结构数组了,由 kmachbsp->mb_e820expadr 指向。

3.如何具体表示一个页?

​ 我们需要页的状态、页的地址、页的分配记数、页的类型、页的链表,你自然就会想到,这些信息可以用一个 C 语言结构体封装起来。

//内存空间地址描述符标志
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;     //缓存位
    u64_t paf_kmap:1;      //映射位
    u64_t paf_lock:1;      //锁定位
    u64_t paf_dirty:1;     //脏位
    u64_t paf_busy:1;      //忙位
    u64_t paf_rv2:4;       //保留位
    u64_t paf_padrs:52;    //页物理地址位
}__attribute__((packed)) phyadrflgs_t;
//内存空间地址描述符
typedef struct s_MSADSC
{
    list_h_t md_list;           //链表
    spinlock_t md_lock;         //保护自身的自旋锁
    msadflgs_t md_indxflgs;     //内存空间地址描述符标志
    phyadrflgs_t md_phyadrs;    //物理地址和标志
    void* md_odlink;            //相邻且相同大小msadsc的指针
}__attribute__((packed)) msadsc_t;

​ 在 cosmos/include/halinc/ 下建立一个 msadsc_t.h 文件,在其中实现这个结构体。msadsc_t 结构看似很大,实则很小,也必须要小,因为它表示一个页面,物理内存页有多少就需要有多少个 msadsc_t 结构。正是因为页面地址总是按 4KB 对齐,所以 phyadrflgs_t 结构的低 12 位(64-52----0x1000-0xFFF)才可以另作它用。

4.如何组织内存页?

​ 按照我们之前对 msadsc_t 结构的定义,组织内存页就是组织 msadsc_t 结构,而 msadsc_t 结构中就有一个链表,你大概已经猜到了,我们组织 msadsc_t 结构正是通过另一个数据结构中的链表,将 msadsc_t 结构串连在其中的。

​ 我们需要更加科学合理地组织 msadsc_t 结构,下面我们来定义一个挂载 msadsc_t 结构的数据结构,它其中需要锁、状态、msadsc_t 结构数量,挂载 msadsc_t 结构的链表、和一些统计数据。因此,我们构造一个 bafhlst_t 数据结构。

​ 有了 bafhlst_t 数据结构,我们只是有了挂载 msadsc_t 结构的地方,这并没有做到科学合理。但是,如果我们把多个 bafhlst_t 数据结构组织起来,形成一个 bafhlst_t 结构数组,并且把这个 bafhlst_t 结构数组放在一个更高的数据结构中,这个数据结构就是内存分割合并数据结构——memdivmer_t,那情况就不一样了。

#define MDIVMER_ARR_LMAX 52
typedef struct s_MEMDIVMER
{
    spinlock_t dm_lock;      //保护自身结构的自旋锁
    u32_t dm_stus;           //状态
    uint_t dm_divnr;         //内存分配次数
    uint_t dm_mernr;         //内存合并次数
    bafhlst_t dm_mdmlielst[MDIVMER_ARR_LMAX];//bafhlst_t结构数组
    bafhlst_t dm_onemsalst;  //单个的bafhlst_t结构
}memdivmer_t;

​ dm_mdmlielst 数组挂载连续 msadsc_t 结构的数量等于用 1 左移其数组下标,如数组下标为 3,那结果就是 8(1<<3)个连续的 msadsc_t 结构。

​ 需要注意的是,我们并不在意其中第一个 msadsc_t 结构对应的内存物理地址从哪里开始,但是第一个 msadsc_t 结构与最后一个 msadsc_t 结构,它们之间的内存物理地址是连续的。

画个图可以解释一下:

在这里插入图片描述

​ 从上图上我们可以看出,每个内存区 memarea_t 结构中包含一个内存分割合并 memdivmer_t 结构,而在 memdivmer_t 结构中又包含 dm_mdmlielst 数组。在 dm_mdmlielst 数组中挂载了多个 msadsc_t 结构。

思考题-我们为什么要以 2 的(0~52)次方为页面数来组织页面呢?

用2的N次方寻址主要有几方面:

1、内存对齐,提升CPU寻址速度 。用 2 的幂次可以把算术运算都转化为位操作,位操作是要比算术运算快的。

2、内存分配时,根据需求大小快速定位至少从哪一部分开始

3、内存分配时,并发加锁,分组可以提升效率

4、内存分配回收时,很多计算也更简单

5、降低内存碎片

参考资料

以上内容是我学习彭东老师的《操作系统实战45讲》后所进行的一个笔记记录,如有错误,还请各位大佬多多指教。

我主要参考了以下资料,十分感谢:

操作系统实战45讲——彭东老师

评论区大佬——neohope

  • 24
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值