MacOS环境-手写操作系统-17-内存管理算法实现

简介: MacOS环境-手写操作系统-17-内存管理算法实现

存管理算法实现

1.简介

在上一节,我们得知可用内存的大小后,我们就可以开发一个简单的管理算法去管理和分配可用用内存。

2.代码

首先创建一个头文件mem_util.h,用来定义内存管理模块相关的数值,变量和接口:

#define  MEMMAN_FREES  4096

struct FREEINFO {
    unsigned int addr, size;
};

struct MEMMAN {
    int frees, maxfrees, lostsize, losts;
    struct FREEINFO free[MEMMAN_FREES];
};

void memman_init(struct MEMMAN *man);

unsigned int memman_total(struct MEMMAN *man);

unsigned int memman_alloc(struct MEMMAN *man, unsigned int size);

int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size);

FREEINFO 结构用来表示可用内存的起始地址和大小


MEMMAN 表示内存管理器 其中的frees 表示当前可用内存对应的FREEINO结构体有多少个


maxfrees 表示我们的内存管理器最多可以容纳多少个可用内存片 一个可用内存片就是一个FREEINFO结构体


当有内存释放时 有些释放后的内存块无法重现加入内存管理器 这些内存块就得丢掉


那么lostsize 就用来记录 内存管理器总共放弃了多少内存碎片 losts记录的是碎片的数量


memman_init 用来初始化内存管理区结构体


memman_total用来计算一个内存管理区存储着多少可用的内存


memman_alloc 用来从指定的内存管理器对象中获取可用内存


memman_free 则用于释放不再需要的内存片


C语言的相关实现(mem_util.c)

#include "mem_util.h"

void memman_init(struct MEMMAN *man) {
    man->frees = 0;
    man->maxfrees = 0;
    man->lostsize = 0;
    man->losts = 0;
}

unsigned int memman_total(struct MEMMAN *man) {
    unsigned int i, t = 0;
    for (i = 0; i < man->frees; i++) {
        t += man->free[i].size;
    }

    return t;
}

unsigned int memman_alloc(struct MEMMAN *man, unsigned int size) {
    unsigned int i, a;
    for (i = 0; i < man->frees; i++) {
        if (man->free[i].size >= size) {
            a = man->free[i].addr;
            man->free[i].size -= size;
            if (man->free[i].size == 0) {
                man->frees--;
            }

            return a;
        }
    }

    return 0;
}

int memman_free(struct MEMMAN *man, unsigned int addr, unsigned int size) {
    int i, j;
    for (i = 0; i < man->frees; i++) {
        if (man->free[i].addr > addr) {
            break;
        }
    }

    if (i > 0) {
        if (man->free[i-1].addr + man->free[i-1].size == addr) {
           man->free[i-1].size += size;
           if (i < man->frees) {
               if (addr + size == man->free[i].addr) {
                   man->free[i-1].size += man->free[i].size;
                   man->frees--;
               }
           }

           return 0;
        }
    }

    if (i < man->frees) {
        if (addr + size == man->free[i].addr) {
           man->free[i].addr = addr;
           man->free[i].size += size;
           return 0;
        }
    }

    if (man->frees < MEMMAN_FREES) {
        for (j = man->frees; j > i; j--) {
            man->free[j] = man->free[j-1];
        }

        man->frees++;
        if (man->maxfrees < man->frees) {
            man->maxfrees = man->frees;
        }

        man->free[i].addr = addr;
        man->free[i].size = size;
        return 0;
    }

    man->losts++;
    man->lostsize += size;
    return -1;
}

头三个函数逻辑比较简单 复杂的是内存释放时的处理逻辑


当有内存释放的时候 我们需要把释放内存的起始地址和大小作为参数传入


假定我们要释放的内存片起始地址是 0x200 大小是0x100


(1)如果内存管理对象中存在着一片可用内存 起始地址是0x100 长度为0x100


那么当前释放的内存片就可以跟原有可用内存合并 合并后的内存块就是起始地址为0x100 长度为0x200的一片内存块


(2)如果内存管理对象不但含有起始地址为0x100 长度为0x100的内存片


而且还含有起始地址为0x300 长度为0x100的内存片


这样的话 三块内存片就可以合并成一块内存 也就是起始地址为0x100 长度为0x300的一个大块内存片


这就是代码if( i > 0) {….} 这个模块的逻辑


如果不存在上面的情况


(3)比如当前内存管理模块存在的内存块是其实地址为0x100 长度为0x50


另一块内存块起始地址是0x350 长度为0x100:


FREEINFO{ addr : 0x100; size : 0x50};

FREEINFO{addr: 0x350; size: 0x100};


这样的话,我们就构建一个对应于要释放内存的FREEINFO对象 然后把这个对象插入到上面两个对象之间:

FREEINFO{ addr : 0x100; size : 0x50};


FREEINFO{addr: 0x200; size: 0x100};


FREEINFO{addr: 0x350; size: 0x100};


这就是对应于if (i < man->frees){…} 这个分支的主要逻辑。


(4)如果当前所有可用内存的起始地址都大于要释放的内存块,则将释放的内存块插到最前面,例如当前可用内存块为:


FREEINOF {addr: 0x350; size: 0x100;}

FREEINFO {addr: 0x460; size: 0x100;}


那么释放起始地址为0x200的内存块后,情况如下:


FREEINFO{addr: 0x200; size: 0x100;}

FREEINOF {addr: 0x350; size: 0x100;}

FREEINFO {addr: 0x460; size: 0x100;}


(5)或者如果当前释放的内存块起始地址大于所有可用内存块,例如要释放的内存块起始地址是0x450, 其他可用的内存块起始地址分别是0x100, 0x300, 那么释放的内存块则添加到末尾:

FREEINFO{addr: 0x100; size: 0x100;}

FREEINOF {addr: 0x350; size: 0x50;}

FREEINFO {addr: 0x450; size: 0x100;}


这就是 if (man->frees < MEMMAN_FREES) {…} 的实现逻辑。


(6)如果以上情况都不满足的话,那么当前回收的内存块则直接丢弃


一般而言 操作系统的内存算法不可能如此简单 内存分配算法是系统内核最为复杂的模块之一


我们当前先从简单入手 后面在慢慢引入分页机制 实现更复杂的内存管理算法


3.编译和运行(这次复杂一些 注意留意)

由于当前内存管理模块与原来C语言实现的模块是分开的


因此它们需要单独编译 然后再把两个编译好的 .o 文件合并成一个模块


编译过程如下:


(我是在MAC下 当前版本是 MacOS BigSur)


先使用命令编译mem_util.c


再编译write_vga_desktop.c


i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o mem_util.o mem_util.c

i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga_desktop.o write_vga_desktop.c


编译后 用ld将其合并链接起来


i386-elf-ld -m elf_i386 -r write_vga_desktop.o mem_util.o -o ckernel.o


链接后 对其进行反编译


./objconv -fnasm ckernel.o ckernel.asm


接着删除其中多余的部分


(两千多行我就不放出来了)


接着再配合kernel一起汇编


nasm -o kernel.bat kernel.asm


(如果出现错误 说跳转不能太长 记得添加 near 我当前环境没有报错 所以我就不处理了)


运行java 生成 system.img

目录
相关文章
|
5月前
|
传感器 机器学习/深度学习 监控
【路径规划】一种越野环境下车辆驾驶风险规避运动规划算法(Matlab代码实现)
【路径规划】一种越野环境下车辆驾驶风险规避运动规划算法(Matlab代码实现)
132 1
|
7月前
|
机器学习/深度学习 存储 算法
强化学习算法基准测试:6种算法在多智能体环境中的表现实测
本文系统研究了多智能体强化学习的算法性能与评估框架,选用井字棋和连珠四子作为基准环境,对比分析Q-learning、蒙特卡洛、Sarsa等表格方法在对抗场景中的表现。实验表明,表格方法在小规模状态空间(如井字棋)中可有效学习策略,但在大规模状态空间(如连珠四子)中因泛化能力不足而失效,揭示了向函数逼近技术演进的必要性。研究构建了标准化评估流程,明确了不同算法的适用边界,为理解强化学习的可扩展性问题提供了实证支持与理论参考。
380 0
强化学习算法基准测试:6种算法在多智能体环境中的表现实测
|
算法 调度 Python
深入理解操作系统中的进程调度算法
在操作系统中,进程调度是核心任务之一,它决定了哪个进程将获得CPU的使用权。本文通过浅显易懂的语言和生动的比喻,带领读者了解进程调度算法的重要性及其工作原理,同时提供代码示例帮助理解。
|
9月前
|
缓存 Linux 数据安全/隐私保护
Linux环境下如何通过手动调用drop_caches命令释放内存
总的来说,记录住“drop_caches” 命令并理解其含义,可以让你在日常使用Linux的过程中更加娴熟和自如。
1475 23
|
11月前
|
机器学习/深度学习 存储 PyTorch
PyTorch内存优化的10种策略总结:在有限资源环境下高效训练模型
在大规模深度学习模型训练中,GPU内存容量常成为瓶颈,特别是在训练大型语言模型和视觉Transformer时。本文系统介绍了多种内存优化策略,包括混合精度训练、低精度训练(如BF16)、梯度检查点、梯度累积、张量分片与分布式训练、
516 14
PyTorch内存优化的10种策略总结:在有限资源环境下高效训练模型
|
12月前
|
存储 人工智能 编译器
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
799 11
【03】鸿蒙实战应用开发-华为鸿蒙纯血操作系统Harmony OS NEXT-测试hello word效果-虚拟华为手机真机环境调试-为DevEco Studio编译器安装中文插件-测试写一个滑动块效果-介绍诸如ohos.ui等依赖库-全过程实战项目分享-从零开发到上线-优雅草卓伊凡
|
9月前
|
Kubernetes Cloud Native 区块链
Arista cEOS 4.30.10M - 针对云原生环境设计的容器化网络操作系统
Arista cEOS 4.30.10M - 针对云原生环境设计的容器化网络操作系统
277 0
|
C语言 开发者 内存技术
探索操作系统核心:从进程管理到内存分配
本文将深入探讨操作系统的两大核心功能——进程管理和内存分配。通过直观的代码示例,我们将了解如何在操作系统中实现这些基本功能,以及它们如何影响系统性能和稳定性。文章旨在为读者提供一个清晰的操作系统内部工作机制视角,同时强调理解和掌握这些概念对于任何软件开发人员的重要性。
|
Linux 调度 C语言
深入理解操作系统:从进程管理到内存优化
本文旨在为读者提供一次深入浅出的操作系统之旅,从进程管理的基本概念出发,逐步探索到内存管理的高级技巧。我们将通过实际代码示例,揭示操作系统如何高效地调度和优化资源,确保系统稳定运行。无论你是初学者还是有一定基础的开发者,这篇文章都将为你打开一扇了解操作系统深层工作原理的大门。
182 4
|
缓存 Ubuntu Linux
Linux环境下测试服务器的DDR5内存性能
通过使用 `memtester`和 `sysbench`等工具,可以有效地测试Linux环境下服务器的DDR5内存性能。这些工具不仅可以评估内存的读写速度,还可以检测内存中的潜在问题,帮助确保系统的稳定性和性能。通过合理配置和使用这些工具,系统管理员可以深入了解服务器内存的性能状况,为系统优化提供数据支持。
1201 4

推荐镜像

更多