一步步写操作系统(四)
4.内存管理
在弄清楚了boot以及asm和c语言之间的调用关系以后,写底层就已经没有任何问题了。一些机制,比如中断、调用门、异常等等,都是可以通过这一个简单的语言调用关系来书写出一个完整的功能。在我看来,现在不忙讨论这些机制的问题,因为这些太超前。不妨先来讨论一下和编程更接近的问题,也就是内存分配。这个重要但是会比那些机制更简单。
之前在帖子中提到使用C语言的struct来实现C++的class,我们的内存管理就是要用这种方法来进行编写。
首先,应该清楚,内存管理需要什么。不管是物理内存还是虚拟内存、单页内存还是分页内存,管理内存无外乎就两个变量:起始地址和偏移量。在内存管理中其实地址就是基地址,偏移量就是内存可用大小。我们在成熟的操作系统中就可以模拟这两个变量,那就是char型数组。
以下是可以在成熟操作系统比如linux或者windows上运行的内存管理程序。
首先来看一个基本类,这是在使用C语言的struct来实现C++的class中提到的经过改版的基本类:
MultiLink.h
// MultiLink.h
//
#pragma once
#include <stdlib.h>
#define __SUPER(B, T, E) \
union {\
B super; \
struct {\
Template##B (T, E)\
}; \
}
//
typedef struct MultiLinkElement {
#define MultiLinkElementTemplate(T)\
int linkcount;\
T ** prev;\
T ** next;\
void(*final)(T *that);\
T * (*free)(T * that);\
void(*clear)(T * that);
#define TemplateMultiLinkElement(T, E) MultiLinkElementTemplate(struct T)
TemplateMultiLinkElement(MultiLinkElement, NULL)
}MultiLinkElement;
void MultiLinkElement_clear(MultiLinkElement * that) {
int i;
for (i = 0; i < that->linkcount; i++) {
that->prev[i] = NULL;
that->next[i] = NULL;
}
}
MultiLinkElement * MultiLinkElement_free(MultiLinkElement * that) {
int i;
for (i = 0; i < that->linkcount; i++) {
if (that->prev[i] != NULL || that->next[i] != NULL) {
return that;
}
}
return NULL;
}
void _MultiLinkElement(MultiLinkElement * that, int linkcount) {
that->linkcount = linkcount;
that->clear = MultiLinkElement_clear;
that->free = MultiLinkElement_free;
that->final = NULL;
that->clear(that);
}
//
typedef struct MultiLinkBase {
#define MultiLinkBaseTemplate(T, E) \
int linkcount;\
int linkindex;\
E * link;\
void(*insertLink)(T * that, E * link, E * before, E * after); \
E * (*removeLink)(T * that, E * link); \
E * (*get)(T * that, int index); \
E * (*prev)(T *that, E * link); \
E * (*next)(T *that, E * link);
#define TemplateMultiLinkBase(T, E) MultiLinkBaseTemplate(struct T, struct E)
TemplateMultiLinkBase(MultiLinkBase, MultiLinkElement)
}MultiLinkBase;
MultiLinkElement * MultiLinkBase_removeLink(MultiLinkBase * that, MultiLinkElement * link) {
MultiLinkElement * before, * after;
if (link == NULL)
{
return NULL;
}
if (that->linkindex < 0)
{
return NULL;
}
if (link->prev[that->linkindex] == NULL || link->next[that->linkindex] == NULL)
{
return NULL;
}
before = link->prev[that->linkindex];
after = link->next[that->linkindex];
before->next[that->linkindex] = after;
after->prev[that->linkindex] = before;
link->prev[that->linkindex] = NULL;
link->next[that->linkindex] = NULL;
if (that->link == link)
{
that->link = after;
}
if (that->link == link)
{
that->link = NULL;
}
that->linkcount = that->linkcount - 1;
return link;
}
MultiLinkElement * MultiLinkBase_get(MultiLinkBase * that, int index) {
MultiLinkElement * temp;
if (that->link == NULL)
{
return NULL;
}
temp = that->link;
do
{
temp = temp->next[that->linkindex];
} while (temp && temp != that->link && --index);
return temp;
}
void MultiLinkBase_insertLink(MultiLinkBase * that, MultiLinkElement * link, MultiLinkElement * before, MultiLinkElement * after) {
MultiLinkElement * _link;
if (link == NULL)
{
return;
}
if (that->link == NULL)
{
that->link = link;
that->link->prev[that->linkindex] = link;
that->link->next[that->linkindex] = link;
that->linkcount = that->linkcount + 1;
return;
}
else
{
_link = NULL;
if (before == that->link)
{
_link = link;
}
if (before == NULL && after == NULL)
{
before = that->link;
after = that->link->prev[that->linkindex];
}
else if (before == NULL)
{
before = after->next[that->linkindex];
}
else if (after == NULL)
{
after = before->prev[that->linkindex];
}
else /* before != NULL && after != NULL*/
{
if (before->prev[that->linkindex] != after || after->next[that->linkindex] != before)
{
return;
}
}
if (before == NULL || after == NULL ||
before->prev[that->linkindex] == NULL ||
after->next[that->linkindex] == NULL)
{
return;
}
link->prev[that->linkindex] = after;
link->next[that->linkindex] = before;
after->next[that->linkindex] = link;
before->prev[that->linkindex] = link;
if (_link)
{
that->link = _link;
}
that->linkcount = that->linkcount + 1;
}
}
MultiLinkElement * MultiLinkBase_prev(MultiLinkBase *that, MultiLinkElement * link) {
if (link == NULL)
{
return NULL;
}
return link->prev[that->linkindex];
}
MultiLinkElement * MultiLinkBase_next(MultiLinkBase *that, MultiLinkElement * link) {
if (link == NULL)
{
return NULL;
}
return link->next[that->linkindex];
}
void _MultiLinkBase(MultiLinkBase * that, int linkindex) {
that->linkcount = 0;
that->linkindex = linkindex;
that->link = NULL;
that->insertLink = MultiLinkBase_insertLink;
that->prev = MultiLinkBase_prev;
that->next = MultiLinkBase_next;
that->removeLink = MultiLinkBase_removeLink;
that->get = MultiLinkBase_get;
}
typedef unsigned char UMAP;
#define MAP_SHIFT 8
#define POOL_MAX 10
#define MAP_MAX POOL_MAX / MAP_SHIFT + 1
#define MAP_MASK 0xFF
typedef struct ElementPool {
#define ElementPoolTemplate(T, E)\
E * pool;\
UMAP * map;\
int size;\
int msize;\
int count;\
E * (*at)(T * that, int index);\
E * (*get)(T * that);\
void(*back)(T * that, E * o);
#define TemplateElementPool(T, E) ElementPoolTemplate(struct T, struct E)
TemplateElementPool(ElementPool, MultiLinkElement)
}ElementPool;
MultiLinkElement * ElementPool_at(ElementPool * that, int index) {
// Inherit struct must override this function
// because the size of the type of pool is different
// Note: in this kind of inherit, be careful when
// using arry of specified type, but there's no
// need to worry about using pointer of the type
// e.g. MultiLinkElement has pointer array :
// prev and next, and there's no need to
// override any get/set function in inherit struct
return &that->pool[index];
}
MultiLinkElement * ElementPool_get(ElementPool * that) {
int i, j, index;
for (i = 0, index = 0; i < that->msize && index < that->size; i++, index += MAP_SHIFT) {
if (that->map[i] & MAP_MASK) {
for (j = 0; j < MAP_SHIFT && index < that->size; j++, index++) {
if (that->map[i] & (0x01 << j)) {
that->map[i] &= ~(0x01 << j);
return that->at(that, index);
}
}
}
}
return NULL;
}
void ElementPool_back(ElementPool * that, MultiLinkElement * o){
int i, j, index;
if (o == NULL) {
return;
}
for (index = 0; index < that->size; index++) {
if (that->at(that, index) == o) {
i = index / MAP_MASK;
j = index - i * MAP_MASK;
that->map[i] |= (0x01 << j);
return;
}
}
}
void _ElementPool(ElementPool * that, MultiLinkElement * pool, UMAP * map, int size) {
int i;
if (size > POOL_MAX) {
size = POOL_MAX;
}
that->pool = pool;
that->map = map;
that->size = size;
that->at = ElementPool_at;
that->get = ElementPool_get;
that->back = ElementPool_back;
that->msize = size / MAP_SHIFT + 1;
if (that->msize > MAP_MAX) {
that->msize = MAP_MAX;
}
for (i = 0; i < that->msize; i++) {
that->map[i] = MAP_MASK;
}
}
//
MultiLinkElement是基本的元素类,提供基本的数据存储基类,后期可以继承变为内存管理单元、任务管理单元等。然后MultiLinkBase是一个MultiLinkElement的容器类,用于管理元素类链表。ElementPool类是一个管理元素类的内存池,这个池就是是分配在kernel中的一个元素类数组,当需要创建一个元素类时由Pool来提供,因为在内存管理中是没有new和delete函数的。
接下来是内存单元类,继承自MultiLinkElement类:
MemStat.h
// MemStat.h
//
#pragma once
#include "MultiLink.h"
typedef int MEM_SIZE;
typedef char * MEM_ADDR;
typedef int MEM_STAT;
#define MEM_OCCUPPIED 1
#define MEM_AVAILABLE 0
typedef struct MemStat MemStat;
struct MemStat {
__SUPER(MultiLinkElement, MemStat, NULL);
MemStat * _prev[2];
MemStat * _next[2];
MEM_STAT stat;
MEM_SIZE size;
MEM_SIZE block;
MEM_ADDR addr;
void(*set)(MemStat * that, int size);
void (*split)(MemStat * that, MemStat * mem);
void (*merge)(MemStat * that, MemStat * mem);
};
void MemStat_set(MemStat * that, int size) {
// 4 k block
int div = size / 0x1000;
int del = size - div * 0x1000;
that->block = del ? (div + 1) * 0x1000 : div * 0x1000;
that->size = size;
}
void MemStat_final(MemStat * that){
//printf("MemStat final.");
}
void MemStat_split(MemStat * that, MemStat * mem) {
that->block -= mem->block;
that->size -= mem->block;
mem->addr = that->addr + that->block;
}
void MemStat_merge(MemStat * that, MemStat * mem) {
that->block += mem->block;
that->size += mem->block;
}
MemStat * _MemStat(MemStat * that, MEM_ADDR addr, MEM_SIZE size) {
that->prev = that->_prev;
that->next = that->_next;
_MultiLinkElement(&that->super, 2);
that->split = MemStat_split;
that->merge = MemStat_merge;
that->set = MemStat_set;
that->final = MemStat_final;
that->set(that, size);
return that;
}
然后是内存池类,继承自ElementPool类:
MemPool.h
// MemPool.h
//
#pragma once
#include "MultiLink.h"
#include "MemStat.h"
typedef struct MemPool MemPool;
struct MemPool {
__SUPER(ElementPool, MemPool, MemStat);
};
MemStat * MemPool_at(MemPool * that, int index) {
return &that->pool[index];
}
void _MemPool(MemPool * that, MemStat * pool, UMAP * map, int size) {
_ElementPool(&that->super, (MultiLinkElement *)pool, map, size);
that->at = MemPool_at;
}
最后是内存管理类,继承自MultiLinkBase类。这个类集合了上面两个MemStat和MemPool类,提供基本的池管理和内存管理:
MemMan.h
// MemMan.h
//
#pragma once
#include "MultiLink.h"
#include "MemStat.h"
#include "MemPool.h"
typedef struct MemMan MemMan;
struct MemMan {
__SUPER(MultiLinkBase, MemMan, MemStat);
MemStat pool[POOL_MAX];
UMAP map[MAP_MAX];
MemPool memPool;
void(*add)(MemMan * that, MemStat * link);
MemStat * (*getAddr)(MemMan * that, MEM_ADDR addr, int stat);
MEM_ADDR(*realloc)(MemMan * that, MEM_ADDR addr, MEM_SIZE size);
MEM_ADDR(*alloc)(MemMan * that, MEM_SIZE size);
void (*free)(MemMan * that, MEM_ADDR addr);
void (*merge)(MemMan * that, MemStat * start, MemStat * end);
void (*split)(MemMan * that, MemStat * tango, MemStat * item);
MemStat * (*remove)(MemMan * that, MemStat * link);
};
void MemMan_add(MemMan * that, MemStat * link) {
that->insertLink(that, link, NULL, NULL);
}
MemStat * MemMan_getAddr(MemMan * that, MEM_ADDR addr, int stat) {
MemStat * mem;
if (that->link == NULL) {
return NULL;
}
mem = that->link;
do {
if (mem->stat == stat && mem->addr == addr) {
return mem;
}
mem = that->next(that, mem);
} while (mem && mem != that->link);
return NULL;
}
MEM_ADDR MemMan_realloc(MemMan * that, MEM_ADDR addr, MEM_SIZE size) {
MemStat * mem;
MemStat * item;
MemStat * prev, *next;
int mark;
if (that->link == NULL) {
return (MEM_ADDR)NULL;
}
mem = that->getAddr(that, addr, 1);
if (mem == NULL) {
return (MEM_ADDR)NULL;
}
//MemStat * item = new MemStat(0, size);
item = that->memPool.get(&that->memPool);
if (item == NULL) {
return (MEM_ADDR)NULL;
}
item->set(item, size);
if (mem->block == item->block) {
mem->set(mem, size);
//delete item;
that->remove(that, item);
return addr;
}
if (mem->block > item->block) {
that->split(that, mem, item);
mem->stat = MEM_AVAILABLE;
item->stat = MEM_OCCUPPIED;
prev = that->prev(that, mem);
if (mem != that->link && prev->stat == MEM_AVAILABLE) {
that->merge(that, prev, mem);
}
return item->addr;
}
prev = that->prev(that, mem);
next = that->next(that, mem);
mark = 0;
if (item != that->link && prev->stat == MEM_AVAILABLE) {
if (mem->block + prev->block >= item->block) {
mark = -1;
if (next != that->link && next->stat == MEM_AVAILABLE) {
if (mem->block + next->block >= item->block) {
// next and prev which is the minimum
if (next->block < prev->block) {
mark = 1;
}
}
}
}
}
else if (next != that->link && next->stat == MEM_AVAILABLE) {
if (mem->block + next->block >= item->block) {
mark = 1;
}
}
if (mark > 0) {
item->set(item, next->block - item->block + mem->block);
if (item->block > 0) {
that->split(that, next, item);
}
else {
//delete item;
that->remove(that, item);
}
that->merge(that, mem, next);
return addr;
}
else if (mark < 0) {
item->set(item, item->block - mem->block);
that->split(that, prev, item);
that->merge(that, item, mem);
item->stat = MEM_OCCUPPIED;
item->set(item, size);
return item->addr;
}
else {
that->free(that, addr);
return that->alloc(that, size);
}
}
MEM_ADDR MemMan_alloc(MemMan * that, MEM_SIZE size) {
MemStat * item;
MemStat * mem ;
MemStat * min;
if (that->link == NULL) {
return (MEM_ADDR)NULL;
}
//MemStat * item = new MemStat(0, size);
item = that->memPool.get(&that->memPool);
if (item == NULL) {
return (MEM_ADDR)NULL;
}
item->set(item, size);
mem = that->link;
min = NULL;
do {
if (mem->stat == MEM_AVAILABLE && mem->block >= item->block) {
if (min == NULL || min->block > mem->block) {
min = mem;
}
}
mem = that->next(that, mem);
} while (mem && mem != that->link);
if (min) {
that->split(that, min, item);
item->stat = MEM_OCCUPPIED;
return item->addr;
}
return (MEM_ADDR)NULL;
}
void MemMan_free(MemMan * that, MEM_ADDR addr) {
MemStat * mem = that->getAddr(that, addr, MEM_OCCUPPIED);
MemStat * prev;
MemStat * next;
if (mem == NULL) {
return;
}
mem->stat = MEM_AVAILABLE;
prev = that->prev(that, mem);
next = that->next(that, mem);
if (mem != that->link && prev->stat == MEM_AVAILABLE) {
that->merge(that, prev, mem);
}
else if (next != that->link && next->stat == MEM_AVAILABLE) {
that->merge(that, mem, next);
}
if (mem != that->link && prev->stat == MEM_AVAILABLE &&
next != that->link && next->stat == MEM_AVAILABLE) {
that->merge(that, prev, next);
}
}
void MemMan_merge(MemMan * that, MemStat * start, MemStat * end) {
if (that->link == NULL) {
return;
}
if (start == NULL || end == NULL) {
return;
}
if (that->next(that, start) != end) {
return;
}
start->merge(start, end);
//delete that->remove(that, end);
that->remove(that, end);
}
MemStat * MemMan_remove(MemMan * that, MemStat * link) {
that->removeLink(that, link);
if (link->free(link) == NULL) {
that->memPool.back(&that->memPool, link);
}
return link;
}
void MemMan_split(MemMan * that, MemStat * tango, MemStat * item) {
if (that->link == NULL) {
return;
}
if (tango == NULL || item == NULL) {
return;
}
that->insertLink(that, item, NULL, tango);
if (tango->block == item->block) {
item->addr = tango->addr;
//delete that->removeLink(that, tango);
that->removeLink(that, tango);
return;
}
tango->split(tango, item);
}
MemMan * _MemMan(MemMan * that, int index) {
int i;
_MultiLinkBase(&that->super, index);
that->add = MemMan_add;
that->alloc = MemMan_alloc;
that->free = MemMan_free;
that->realloc = MemMan_realloc;
that->getAddr = MemMan_getAddr;
that->merge = MemMan_merge;
that->split = MemMan_split;
that->remove = MemMan_remove;
for (i = 0; i < POOL_MAX; i++) {
_MemStat(&that->pool[i], 0, 0);
}
_MemPool(&that->memPool, that->pool, that->map, POOL_MAX);
return that;
}
从MemMan类的代码可以看到,该管理类不仅继承自MultiLinkBase容器类,并且管理这MemStat和MemPool两个类,其中提供了MemStat数组供MemPool类使用,并且保存有MemPool需要的一个Map表,这个Map表是标记MemStat数组使用和未使用情况的映射表。另外注意的一点就是,在MemPool中,即使使用MemStat数组指针传递给其内含的pool指针,该指针也会被转化为基类MultiLinkElement指针,因此,使用这个指针索引MemStat时,不能直接基类提供的at函数更不能直接使用[]进行索引,而要在MemPool类中重写at函数进行索引,否则基类的at函数或者[]索引,计算的是MultiLInkElement的大小,而不是MemStat的大小,导致偏移出错。
在这里我们可以看到,这样的操作非常繁琐,但是,在完成底层书写以后,内存管理留出来的接口使用起来就很简单了,外层只需要初始化MemMan类,即可对内存进行管理:
Memory.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "MemMan.h"
char * memory;
MemMan memMan;
int main() {
_MemMan(&memMan, 1);
memory = (char *)malloc(0x10000000);//使用现存操作系统提供的malloc函数为内存管理提供一个模拟的内存(返回值为起始地址,大小为分配的大小)
memset(memory, 0, 0x10000000);
MemStat * mem = memMan.memPool.get(&memMan.memPool);
mem->addr = (MEM_ADDR)memory;
mem->set(mem, 0x10000000);
memMan.add(&memMan, mem);
MEM_ADDR addr = memMan.alloc(&memMan, 1000);
if (addr) {
memcpy((char *)addr, "alloc", 10);
}
int i;
for (i = 0; i < memMan.linkcount; i ++) {
mem = memMan.get(&memMan, i);
printf( "d: %10X, s: %10u, b: %10u, s: %d, v: %s\n", mem->addr, mem->size, mem->block, mem->stat, (char *)mem->addr);
}
printf("========================\n");
addr = memMan.realloc(&memMan, addr, 10000);
if (addr) {
memcpy((char *)addr, "RE-alloc", 10);
}
for (i = 0; i < memMan.linkcount; i ++) {
mem = memMan.get(&memMan, i);
printf( "d: %10X, s: %10u, b: %10u, s: %d, v: %s\n", mem->addr, mem->size, mem->block, mem->stat, (char *)mem->addr);
}
printf("========================\n");
addr = memMan.realloc(&memMan, addr, 10);
if (addr) {
memcpy((char *)addr, "RE-alloc", 10);
}
for (i = 0; i < memMan.linkcount; i ++) {
mem = memMan.get(&memMan, i);
printf( "d: %10X, s: %10u, b: %10u, s: %d, v: %s\n", mem->addr, mem->size, mem->block, mem->stat, (char *)mem->addr);
}
printf("========================\n");
memMan.free(&memMan, addr);
for (i = 0; i < memMan.linkcount; i ++) {
mem = memMan.get(&memMan, i);
printf( "d: %10X, s: %10u, b: %10u, s: %d, v: %s\n", mem->addr, mem->size, mem->block, mem->stat, (char *)mem->addr);
}
printf("========================\n");
for (i = 0; i < POOL_MAX; i++) {
addr = memMan.alloc(&memMan, 100);
if (!addr) {
printf("alloc error: @%d\n", i);
}
}
for (i = 0; i < memMan.linkcount; i ++) {
mem = memMan.get(&memMan, i);
printf( "d: %10X, s: %10u, b: %10u, s: %d, v: %s\n", mem->addr, mem->size, mem->block, mem->stat, (char *)mem->addr);
}
printf("========================\n");
free(memory);
}
同样,在我们自己写的操作系统中,也使用上面的方法对内存进行管理。
只是在内存起始地址、可用大小上,有一些出入。自己的操作系统中,需要避免使用kernel所在内存,并且要检测内存大小。
本代码,以及完整的可运行的操作系统代码已经更新到GitHub和Gitee,在Test/Memory_Linux下面有上面的代码,以供测试。
GITHUB: https://github.com/stophin/NanoOS
GITEE: https://gitee.com/stophin/NanoOS