内核为频繁使用的数据结构分配物理内存采用的是_物理内存管理——支持任意大小内存分配

bddb6e8a905db99fb3372e40a30e2da8.png

试验目标

上一节描述了如何进行页级内存的分配与释放管理,可以比较有效地完成以页大小为最小单位(粒度)的内存分配和回收工作,这样可以很好地与分页机制配合在一起提供有效的分页管理。但在操作系统的实际运行过程中,还有很多对小于一页的任意大小内存的动态申请需求,则以页为最小单位就无法适应这类需求了。当然,我们可以直接把物理内存页管理器改为粒度为1字节的物理内存管理器,这主要存在两个问题:

  • 由于页目录表和页表的大小是4KB,且一个页表项管理的内存空间大小也是4KB,故需要扩展新的函数和结构匹配对页表的管理支持,导致代码复杂;
  • 即使采用上述4种内存分配算法,在支持任意大小的内存分配请求上,依然存在效率不高,有外碎片和内碎片等问题。

所以,一个更加合理的办法是在物理内存页管理器的基础之上建立一个支持任意大小的内存分配管理器,形成二级内存管理,满足高效支持任意大小的内存分配请求。这就对动态内存分配管理提出了新的挑战,即花尽量少的时间完成对内存的分配和回收,且保证能够产生的内外碎片尽量小。

proj6中参考Jeff Bonwick 为 Sun OS 操作系统首次引入的一种算法:slab算法。slab算法的基本思路有两个,一个是通过缓存实现“对象”重用,另一个是在一个连续页空间放同样类型的“对象”。

Jeff Bonwick在SUN OS内核中观察到内核在运行时会为有限的对象集(内核中各种常见的数据结构)分配大量内存,且对内核中这些数据结构进行初始化所需的时间超过了对其进行分配和释放所需的时间。因此他的结论是不应该将内存释放回一个全局的空闲内存池,而是将内存保持为针对特定目而初始化的状态。例如,如果内存被分配给了一个变量,那么只需在为此变量首次分配内存时执行一次变量初始化函数即可,当该变量被回收并进一步被后续分配时就不需要执行这个初始化函数,因为从上次释放和调用析构之后,它已经处于所需的状态中了。

在一个连续地址空间放同样类型的“对象”有助于快速查找和修改同样类型的“对象”,提高分配的时间效率,并减少碎片。

proj6概述

实现描述

proj6基于proj5.2实现,主要在buddy物理内存页管理器的基础上,增加了一级任意大小内存分配管理,通过slab算法实现对小内存的简洁分配,为后续的运行时动态内存管理提供通用的内存申请和释放接口、在proj6中,可以了解到:

  • slab算法的数据结构和具体实现;
  • 小内存分配管理器与物理内存页管理器的接口和交互过程。

项目组成

proj6
├── boot
│   └── ……
├── kern
│   ├── debug
│   │   └── ……
│   ├── driver
│   │   └── ……
│   ├── init
│   │   └── ……
│   ├── libs
│   │   └── ……
│   ├── mm
│   │   ├── ……
│   │   ├── memlayout.h
│   │   ├── slab.c
│   │   └── slab.h
│   ├── sync
│   │   └── ……
│   └── trap
│       └── ……
├── libs
│   └── ……
├── Makefile
└── ……

11 directories, 58 files

相对与proj5.2,proj6增加了slab.[ch]两个个文件,主要完成对slab内存管理算法的简单实现。

编译运行

(THU.CST) os is loading ...

Special kernel symbols:
  entry  0xc010002c (phys)
  etext  0xc0109530 (phys)
  edata  0xc0122aa0 (phys)
  end    0xc0123cb8 (phys)
Kernel executable memory footprint: 144KB
memory managment: buddy_pmm_manager
e820map:
  memory: 0009f400, [00000000, 0009f3ff], type = 1.
  memory: 00000c00, [0009f400, 0009ffff], type = 2.
  memory: 00010000, [000f0000, 000fffff], type = 2.
  memory: 07efd000, [00100000, 07ffcfff], type = 1.
  memory: 00003000, [07ffd000, 07ffffff], type = 2.
  memory: 00040000, [fffc0000, ffffffff], type = 2.
check_alloc_page() succeeded!
check_pgdir() succeeded!
check_boot_pgdir() succeeded!
-------------------- BEGIN --------------------
PDE(0e0) c0000000-f8000000 38000000 urw
  |-- PTE(38000) c0000000-f8000000 38000000 -rw
PDE(001) fac00000-fb000000 00400000 -rw
  |-- PTE(000e0) faf00000-fafe0000 000e0000 urw
  |-- PTE(00001) fafeb000-fafec000 00001000 -rw
--------------------- END ---------------------
check_slab() succeeded!
++ setup timer interrupts
100 ticks
100 ticks

【实现】slab算法的简化设计实现

数据结构描述

slab 算法采用了两层数据组织结构。在最高层是 slab_cache,这是一个不同大小slab 缓存的链接列表数组。slab_cache的每个数组元素都是一个管理和存储给定大小的空闲对象(obj)的slab 结构链表,这样每个slab设定一个要管理的给定大小的对象池,占用物理空间连续的1个或多个物理页。slab_cache的每个数组元素管理两种slab列表:

  • slabs_full:完全分配的 slab
  • slabs_notfull:部分分配的 slab

注意 slabs_notfull列表中的 slab 是可以进行回收(reaping),使得slab 所使用的内存可被返回给操作系统供其他子系统使用。

slab 列表中的每个 slab 都是一个连续的内存块(一个或多个连续页),它们被划分成一个个obj。这些obj是中进行分配和释放的基本元素。由于对象是从 slab 中进行分配和释放的,因此单个 slab 可以在 slab 链表之间进行移动。例如,当一个 slab 中的所有对象都被使用完时,就从 slabs_notfull 链表中移动到 slabs_full 链表中。当一个 slab 完全被分配并且有对象被释放后,就从 slabs_full 列表中移动到 slabs_notfull列表中。下面是ucore中的slab架构图:

eee17e5d698d492d2be1900db550563a.png

slab架构图

分配与释放内存实现描述

现在来看一下能够创建新 slab 缓存、向缓存中增加内存、销毁缓存的接口以及 slab 中对对象进行分配和释放操作的slab相关操作过程和函数。

第一个步骤是通过执行slab_init函数初始化slab_cache 缓存结构。然后其他 slab 缓存函数将使用该引用进行内存分配与释放等操作。ucore中最常用的内存管理函数是 kmalloc 和 kfree 函数。这两个函数的原型如下:

  • void *kmalloc( size_t size );
  • void kfree(void *objp );

在分配任意大小的空闲块时,kmalloc通过调用kmem_cache_alloc函数来遍历对应size的slab,来查找可以满足大小限制的缓存。如果kmem_cache_alloc函数发现slab中有空闲的obj,则分配这个对象;如果没有空闲的obj了,则调用kmem_cache_grow函数分配包含1到多页组成的空闲slab,然后继续分配。要使用 kfree 释放对象时,通过进一步调用kmem_cache_free把分配对象返回到slab中,并标记为空闲,建立空闲obj之间的链接关系。

(可进一步详细一些)


本文转载自:https://chyyuu.gitbooks.io/simple_os_book/content/zh/chapter-3/implement_rand_mem.html,原作者为陈渝(清华大学计算机科学与技术系副教授)老师,转载已获原作者认证。

微信公众号:迪捷数原

联系电话:010-56131268;15600442810、13260299730 (微信同号)

联系邮箱:contact@digiproto.com

工作地址:北京市海淀区中关村软件园

浙江省绍兴市越城区中关村•水木湾区科学园3#802

公司网址:www.digiproto.com

公司简介:浙江迪捷软件科技有限公司2013年成立于北京,专注于安全关键领域数字化转型,提供军工领域的MBSE产品和解决方案,遵循中立开放的商业理念,为我国防务等安全关键领域提供MBSE和数字装备等解决方案。我司软件产品全部为自主研发,具有核心知识产权,涉及了高端装备的设计、研发和测试等环节。公司注册资金为1000万,总部位于浙江省绍兴市,在北京、上海等地设有分公司。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值