一步步写操作系统(四) 内存管理

来源:互联网 发布:删除excel中重复数据 编辑:程序博客网 时间:2024/06/15 14:59

一步步写操作系统(四)

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   0xFFtypedef 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   0typedef 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