跟我一起写操作系统

    返回首页    发表留言
本文作者:李德强
          第三节 按字节申请内存
 
 

        有了按内存页申请内存之后就可以动态申请内存了,但这远远是不够的,因为如果每个程序都按照4KB的大小来申请内存,则会大大的浪费内存空间,比如要申请一个10个字节的内存,操作系统则会为它分配一个大小为4096B的内存页,另外一个程序要申请一个4字节的内存,操作系统仍然会为它分配一个4096B的内存页。这样的内存分配是不合理的,是对内存资源大大的浪费。为了能让内存资源更加合理被程序所利用,还需要对其进行进一步的处理:页内位图。

        页内位图是指在一个内存页当中,每一个字节的使用状态都被位图记录下来,也就是用这个map位图来记录页内内存的使用状态。这里采用的是一个bit位代表4个字节的内存。那么对于一个内存页的4096B的内存使用状态则需要页内位图的大小是4096 / 4 / 8 = 128。一个动态内存页中前128字节为页内位图所占用的空间,而这部分空间在位图中占128 / 4 / 8 = 4个字节。如下:



        如果申请内存大小大于一个页大小,则按整页分配内存:

if (size > (MM_PAGE_SIZE - 128))
{
        //计算有多少个内存页
         u32 count = (size / MM_PAGE_SIZE);
        //如果有余数,说明要多分配一个页面
        if (size % MM_PAGE_SIZE != 0)
        {
                count++;
        }
        //按整页分配内存
        return alloc_page(count);
}

        在动态分配内存时要按4字节对齐分配内存。这样会有效的避免内存浪费的问题,也就是说如果位图中每个bit位表示的内存字节太小,那么页内位图本身所占用的内存空间就会增大。如果让页内位图占用空间小,但是每个bit位所表示的内存字节就会有所浪费,比如某个程序申请1个字节时,操作系统也只能为其分配4个字节。这一个权衡值,这里使用的是4字节对齐。

        如果申请内存大小大于一个页面的大小4096B则直接按申请内存页方式来申请:

u32 alloc_size = size / 4;
//如果有余数,说明要多分配一个4字节
if (size % 4 > 0)
{
        alloc_size++;
}

        下面代码中为内存分配相应的内存空间,其实现原理为:先找到一个可分配的内存页,可以是未使用的,也可以是动态分配的,然后在这个内存页中逐个向下查找内存位图,直到找到符合申请内存空间大小的内存区域,并将这个区域设置为已使用,并将其开始地址返回:

//i为map下标,j为页内map下标,k为页内字节位偏移,c为查找count,
int i, j, k, c = 0, break_status = 0, run_time = 0;
//is为起始map下标,js为页内起始map下标,ks为面内起始字节偏移
int is = -1, js = -1, ks = -1;
//从未被分配内存页的地方开始查找
for (i = MMAP_USED_SIZE; i < MAP_SIZE && !break_status; i++)
{
        //如果是未使用或动态内存
        if (mmap[i] == MM_FREE || mmap[i] == MM_DYNAMIC)
        {
                //取得页内map位图
                u8 *mpmap = (u8*) (i * MM_PAGE_SIZE);
                //跳过前4个字节,并小于128个字节中查找页内位图map
                //一个页面为4096字节,前128字节为页内位图0x80 * 0x8 * 0x4 = 0x1000 = 4096
                //这128个字节占用页内位图为0x80 / 0x8 / 0x4 = 0x4字节所以要跳过前4个字节
                for (j = 4; j < 128 && !break_status; j++)
                {
                        //字节偏移从0到7字节来查找可用内存
                        for (k = 0; k < 8 && !break_status; k++)
                        {
                                //如果可以使用
                                if (((mpmap[j] >> k) & 0x1) == 0)
                                {
                                        //已找到数量自增
                                        c++;
                                }
                                //如果找到可分配内存数量达到预先要申请的数量
                                if (c >= alloc_size)
                                {
                                        //跳出
                                        break_status = 1;
                                }
                        }
                }
        }
}
//清空分配数
c = 0;
//正式分配内存
if (break_status == 1 && is != -1 && js != -1 && ks != -1)
{
        //从内存位图开始
        for (i = is; i < MAP_SIZE; i++)
        {
                //如果是可分配内存
                if (mmap[i] == MM_FREE || mmap[i] == MM_DYNAMIC)
                {
                        //取得页内位图
                        u8 *mpmap = (u8*) (i * MM_PAGE_SIZE);
                        //从页内位图的第4个字节开始
                        for (j = 4; j < 128; j++)
                        {
                                //如果是第1次,找到起始地址
                                if (run_time == 0)
                                {
                                        j = js;
                                }
                                //页内偏移
                                for (k = 0; k < 8; k++)
                                {
                                        //如果是第1次,找到起始偏移
                                        if (run_time == 0)
                                        {
                                                k = ks;
                                        }
                                        //更新页内位图
                                        mpmap[j] |= (1 << k);
                                        //找到数量自增
                                        c++;
                                        //次数加1
                                        run_time++;
                                        //找到预期的申请数量
                                        if (c >= alloc_size)
                                        {
                                                //将此内存页设定为动态分配
                                                mmap[i] = MM_DYNAMIC;
                                                //返回申请内存地址
                                                return (void*) (is * MM_PAGE_SIZE + (js * 8 * 4) + (ks * 4));
                                        }
                                }
                        }
                }
        }
}

        释放内存,如果要释放的内存大小大于一个内存页的大小,则直接按释放内存页的方式来释放内存:

//如果大小大于一个内存页大小,则按整内存页释放
if (size > (MM_PAGE_SIZE - 128))
 {
        //计算有多少个内存页
        int count = (size / MM_PAGE_SIZE);
        //如果有余数说明要多释放一个页
        if (size % MM_PAGE_SIZE != 0)
        {
               count++;
        }
        //释放内存页
        free_page(addr, count);
        return;
}

        4字节对齐,与分配是一样:

//按4字节对齐释放
int alloc_size = size / 4;
//如果有余数则说明要多释放一个4字节空间
if (size % 4 > 0)
{
        alloc_size++;
}

       按4字节来释放内存,并将页内位图的相应的标识位修改为未使用状态:

//释放内存起始号
int is, js, ks, run_time = 0, count = 0;
//计算位图起始号
is = (u32) addr / MM_PAGE_SIZE;
//计算页内位图起始号
js = ((u32) addr % MM_PAGE_SIZE) / (8 * 4);
//计算页内位图位偏移起始号
ks = ((u32) addr % MM_PAGE_SIZE) % (8 * 4) / 4;
//从内存页开始
for (int i = is; i < MAP_SIZE; i++)
{
        //取得页内偏移
        u8 *mpmap = (u8*) (i * MM_PAGE_SIZE);
        //从页内位图中第4个开始
        for (int j = 4; j < 128; j++)
        {
                //如果是第1次释放
                if (run_time == 0)
                {
                        j = js;
                }
                //从页内偏移位开始
                for (int k = 0; k < 8; k++)
                {
                        //如果是第1次释放
                        if (run_time == 0)
                        {
                                k = ks;
                        }
                        //设定页内位图为动态可用内存
                        mpmap[j] &= (~(1 << k));
                        //数量自增
                        count++;
                        //次数自增
                        run_time++;
                        //如果已释放了预期大小的内存
                        if (count >= alloc_size)
                        {
                                //完成,返回
                                return;
                        }
                }
        }
}

        将安装内存申请模块加入start_kernel函数中:

//安装内存申请模块
install_alloc();

        加入内存申请测试代码:

char *p = alloc_page(1);
printf("%x\n", p);
char *p1 = alloc_mm(4);
printf("%x\n", p1);
char *p2 = alloc_mm(4);
printf("%x\n", p2);
free_mm(p2, 4);
free_mm(p1, 4);
free_page(p, 1);
p = alloc_page(1);
printf("%x\n", p);
p1 = alloc_mm(4);
printf("%x\n", p1);
p2 = alloc_mm(4);
printf("%x\n", p2);
free_mm(p2, 4);
free_mm(p1, 4);
free_page(p, 1);

        将alloc.c加入Makefile并编译运行,运行结果如下:



 

        源代码的下载地址为:

https           https://github.com/magicworldos/lidqos.git 
git             git@github.com:magicworldos/lidqos.git 
subverion       https://github.com/magicworldos/lidqos 
branch          v0.10

    返回首页    返回顶部
#1楼  wkv1  于 2017年11月10日14:54:44 发表
 
屌的飞起。。。赞。。。
#2楼  雷锋v1  于 2017年11月22日21:15:28 发表
 
//计算有多少个内存页

u32 count = (size / MM_PAGE_SIZE);

这段应该是: u32 count = (size /( MM_PAGE_SIZE-128);吧
#3楼  雷锋v1  于 2017年11月22日21:15:29 发表
 
//计算有多少个内存页

u32 count = (size / MM_PAGE_SIZE);

这段应该是: u32 count = (size /( MM_PAGE_SIZE-128);吧
#4楼  李德强  于 2017年11月23日19:47:05 发表
 
您说的非常对!是应该这样。

那时的我还不会像您说的这种计算方法。
  看不清?点击刷新

 

  Copyright © 2015-2023 问渠网 辽ICP备15013245号