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

一步步写操作系统(四)

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


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值