新的内存管理模块 (#303)

  实现了具有优秀架构设计的新的内存管理模块,对内核空间和用户空间的内存映射、分配、释放、管理等操作进行了封装,使得内核开发者可以更加方便地进行内存管理。

  内存管理模块主要由以下类型的组件组成:

- **硬件抽象层(MemoryManagementArch)** - 提供对具体处理器架构的抽象,使得内存管理模块可以在不同的处理器架构上运行
- **页面映射器(PageMapper)**- 提供对虚拟地址和物理地址的映射,以及页表的创建、填写、销毁、权限管理等操作。分为两种类型:内核页表映射器(KernelMapper)和用户页表映射器(位于具体的用户地址空间结构中)
- **页面刷新器(PageFlusher)** - 提供对页表的刷新操作(整表刷新、单页刷新、跨核心刷新)
- **页帧分配器(FrameAllocator)** - 提供对页帧的分配、释放、管理等操作。具体来说,包括BumpAllocator、BuddyAllocator
- **小对象分配器** - 提供对小内存对象的分配、释放、管理等操作。指的是内核里面的SlabAllocator (SlabAllocator的实现目前还没有完成)
- **MMIO空间管理器** - 提供对MMIO地址空间的分配、管理操作。(目前这个模块待进一步重构)
- **用户地址空间管理机制** - 提供对用户地址空间的管理。
    - VMA机制 - 提供对用户地址空间的管理,包括VMA的创建、销毁、权限管理等操作
    - 用户映射管理 - 与VMA机制共同作用,管理用户地址空间的映射
- **系统调用层** - 提供对用户空间的内存管理系统调用,包括mmap、munmap、mprotect、mremap等
- **C接口兼容层** - 提供对原有的C代码的接口,是的C代码能够正常运行。


除上面的新增内容以外,其它的更改内容:
- 新增二进制加载器,以及elf的解析器
- 解决由于local_irq_save、local_irq_restore函数的汇编不规范导致影响栈行为的bug。
- 解决local_irq_save未关中断的错误。
- 修复sys_gettimeofday对timezone参数的处理的bug

---------

Co-authored-by: kong <kongweichao@dragonos.org>
This commit is contained in:
LoGin
2023-07-22 16:27:02 +08:00
committed by GitHub
parent bb5f098a86
commit 40fe15e095
124 changed files with 8277 additions and 5150 deletions

View File

@ -1,15 +0,0 @@
# 内存分配指南
DragonOS提供了一些用于内存分配的api。您可以使用*kmalloc*来分配小的内存块,也可以使用*alloc_pages*分配连续的2MB大小的内存页面。
## 选择合适的内存分配器
在内核中,最直接、最简单的分配内存的方式就是,使用`kmalloc()`函数进行分配。并且,出于安全起见,除非内存在分配后一定会被覆盖,且您能确保内存中的脏数据一定不会对程序造成影响,在其余情况下,我们建议使用`kzalloc()`进行内存分配,它将会在`kmalloc()`的基础上,把申请到的内存进行清零。
您可以通过`kmalloc()`函数分配得到32bytes到1MBytes之间的内存对象。并且这些内存对象具有以下的性质
- 内存起始地址及大小按照2次幂对齐。比如申请的是80bytes的内存空间那么获得的内存对象大小为128bytes且内存地址按照128bytes对齐
对于需要大量连续内存的分配,可以使用`alloc_pages()`向页面分配器申请连续的内存页。
当内存空间不再被使用时,那么必须释放他们。若您使用的是`kmalloc()`分配的内存,那么您需要使用`kfree()`释放它。若是使用`alloc_pages()`分配的内存,则需要使用`free_pages()`来释放它们。

View File

@ -13,16 +13,3 @@
data_structures
casting
softirq
内存管理
===================
这里快速讲解了如何在DragonOS中分配、使用内存。以便您能快速的了解这个模块。
详细的内存管理模块的文档请参见::ref:`memory_management_module`
.. toctree::
:maxdepth: 1
allocate-memory
mm-api

View File

@ -1,267 +0,0 @@
(_core_mm_api)=
# 内存管理API
## SLAB内存池
SLAB内存池提供小内存对象的分配功能。
### `void *kmalloc(unsigned long size, gfp_t gfp)`
&emsp;&emsp;获取小块的内存。
#### 描述
&emsp;&emsp;kmalloc用于获取那些小于2M内存页大小的内存对象。可分配的内存对象大小为32bytes~1MBytes. 且分配的内存块大小、起始地址按照2的n次幂进行对齐。比如申请的是80bytes的内存空间那么获得的内存对象大小为128bytes且内存地址按照128bytes对齐
##### 参数
**size**
&emsp;&emsp;内存对象的大小
**gfp**
&emsp;&emsp;标志位
### `void *kzalloc(unsigned long size, gfp_t gfp)`
#### 描述
&emsp;&emsp;获取小块的内存并将其清零。其余功能与kmalloc相同。
##### 参数
**size**
&emsp;&emsp;内存对象的大小
**gfp**
&emsp;&emsp;标志位
### `unsigned long kfree(void *address)`
&emsp;&emsp;释放从slab分配的内存。
#### 描述
&emsp;&emsp;该函数用于释放通过kmalloc申请的内存。如果`address`为NULL则函数被调用后无事发生。
&emsp;&emsp;请不要通过这个函数释放那些不是从`kmalloc()``kzalloc()`申请的内存,否则将会导致系统崩溃。
##### 参数
**address**
&emsp;&emsp;指向内存对象的起始地址的指针
## 物理页管理
DragonOS支持对物理页的直接操作
### `struct Page *alloc_pages(unsigned int zone_select, int num, ul flags)`
#### 描述
&emsp;&emsp;从物理页管理单元中申请一段连续的物理页
#### 参数
**zone_select**
&emsp;&emsp;要申请的物理页所位于的内存区域
可选值:
- `ZONE_DMA` DMA映射专用区域
- `ZONE_NORMAL` 正常的物理内存区域,已在页表高地址处映射
- `ZONE_UNMAPPED_IN_PGT` 尚未在页表中映射的区域
**num**
&emsp;&emsp;要申请的连续物理页的数目该值应当小于64
**flags**
&emsp;&emsp;分配的页面要被设置成的属性
可选值:
- `PAGE_PGT_MAPPED` 页面在页表中已被映射
- `PAGE_KERNEL_INIT` 内核初始化所占用的页
- `PAGE_DEVICE` 设备MMIO映射的内存
- `PAGE_KERNEL` 内核层页
- `PAGE_SHARED` 共享页
#### 返回值
##### 成功
&emsp;&emsp;成功申请则返回指向起始页面的Page结构体的指针
##### 失败
&emsp;&emsp;当ZONE错误或内存不足时返回`NULL`
### `void free_pages(struct Page *page, int number)`
#### 描述
&emsp;&emsp;从物理页管理单元中释放一段连续的物理页。
#### 参数
**page**
&emsp;&emsp;要释放的第一个物理页的Page结构体
**number**
&emsp;&emsp;要释放的连续内存页的数量。该值应小于64
## 页表管理
### `int mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool use4k)`
#### 描述
&emsp;&emsp;将一段物理地址映射到当前页表的指定虚拟地址处
#### 参数
**virt_addr_start**
&emsp;&emsp;虚拟地址的起始地址
**phys_addr_start**
&emsp;&emsp;物理地址的起始地址
**length**
&emsp;&emsp;要映射的地址空间的长度
**flags**
&emsp;&emsp;页表项的属性
**use4k**
&emsp;&emsp;使用4级页表将地址区域映射为若干4K页
### `int mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user, bool flush, bool use4k)`
#### 描述
&emsp;&emsp;将一段物理地址映射到指定页表的指定虚拟地址处
#### 参数
**proc_page_table_addr**
&emsp;&emsp;指定的顶层页表的起始地址
**is_phys**
&emsp;&emsp;该顶层页表地址是否为物理地址
**virt_addr_start**
&emsp;&emsp;虚拟地址的起始地址
**phys_addr_start**
&emsp;&emsp;物理地址的起始地址
**length**
&emsp;&emsp;要映射的地址空间的长度
**flags**
&emsp;&emsp;页表项的属性
**user**
&emsp;&emsp;页面是否为用户态可访问
**flush**
&emsp;&emsp;完成映射后是否刷新TLB
**use4k**
&emsp;&emsp;使用4级页表将地址区域映射为若干4K页
#### 返回值
- 映射成功0
- 映射失败:-EFAULT
### `void mm_unmap_proc_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul length)`
#### 描述
&emsp;&emsp;取消给定页表中的指定地址空间的页表项映射。
#### 参数
**proc_page_table_addr**
&emsp;&emsp;指定的顶层页表的基地址
**is_phys**
&emsp;&emsp;该顶层页表地址是否为物理地址
**virt_addr_start**
&emsp;&emsp;虚拟地址的起始地址
**length**
&emsp;&emsp;要取消映射的地址空间的长度
### `mm_unmap_addr(virt_addr, length)`
#### 描述
&emsp;&emsp;该宏定义用于取消当前进程的页表中的指定地址空间的页表项映射。
#### 参数
**virt_addr**
&emsp;&emsp;虚拟地址的起始地址
**length**
&emsp;&emsp;要取消映射的地址空间的长度
## 内存信息获取
### `struct mm_stat_t mm_stat()`
#### 描述
&emsp;&emsp;获取计算机目前的内存空间使用情况
#### 参数
#### 返回值
&emsp;&emsp;返回值是一个`mm_mstat_t`结构体,该结构体定义于`mm/mm.h`中。其中包含了以下信息(单位均为字节):
| 参数名 | 解释 |
| ---------- | ----------------------- |
| total | 计算机的总内存数量大小 |
| used | 已使用的内存大小 |
| free | 空闲物理页所占的内存大小 |
| shared | 共享的内存大小 |
| cache_used | 位于slab缓冲区中的已使用的内存大小 |
| cache_free | 位于slab缓冲区中的空闲的内存大小 |
| available | 系统总空闲内存大小包括kmalloc缓冲区 |