[读书笔记]30 天自制操作系统 day9 内存管理

本文介绍了内存容量检查的方法,包括如何通过写入和读取内存值来验证内存连接的正确性,以及如何关闭缓存以确保检查的有效性。此外,还探讨了内存管理的基本原理和技术选择,包括位图管理和列表管理两种方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 内存容量检查

  1. 由于内存和CPU的距离比CPU内部元件之间的距离要远很多, 因此,在寄存器内部使用 mov 要比 从寄存器MOV 到内存快很多
  2. 另外, 由于寄存器的速度快, 但是存储的容量非常有限, 所以不得不频繁的使用内存
  3. 为了解决这个问题, IBM 引入了 高速缓存存储器 (cache memory), 但是成本非常高
  4. 本质上呢, 我们可以讲这个高速缓存器件理解成是一个类似缓冲区的概念
    这里写图片描述

2. 内存容量检查思路

  1. 内存检查的时候, 我们可以往内存中随便写入一个值, 然后马上读取, 来检测读取的值和写入的值是否是相等的。 如果内存连接正常, 那么写入的值应该可以存放在内存中。 但是如果没有连接上的话, 读出来的值一定是乱七八糟的。
    为保证检查的有效性, 我们必须关闭缓存。
  2. 首先, 我们通过将cpu 的EFLAGS 的第 18 bit 设置为1, ie, 使用缓存,对于 i486 以上的cpu 是可以设置成功的, 而对于 i486 以下的cpu, 是不能设置成功的, 通过这个可以判断 cpu 类型
  3. 下面代码检测速度有些慢, 可以考虑修改增量 为 4KB
  4. 效果:
    这里写图片描述
  5. 相关代码如下:
#define EFLAGS_AC_BIT       0x00040000
#define CR0_CACHE_DISABLE   0x60000000

unsigned int memtest(unsigned int start, unsigned int end)
{
    char flg486 = 0;
    unsigned int eflg, cr0, i;

    /* 386か、486以降なのかの確認 */
    eflg = io_load_eflags();
    eflg |= EFLAGS_AC_BIT; /* AC-bit = 1 */
    io_store_eflags(eflg);
    eflg = io_load_eflags();
    if ((eflg & EFLAGS_AC_BIT) != 0) { /* 386ではAC=1にしても自動で0に戻ってしまう */
        flg486 = 1;
    }
    eflg &= ~EFLAGS_AC_BIT; /* AC-bit = 0 */
    io_store_eflags(eflg);

    if (flg486 != 0) {
        cr0 = load_cr0();
        cr0 |= CR0_CACHE_DISABLE; /* キャッシュ禁止 */
        store_cr0(cr0);
    }

    i = memtest_sub(start, end);

    if (flg486 != 0) {
        cr0 = load_cr0();
        cr0 &= ~CR0_CACHE_DISABLE; /* キャッシュ許可 */
        store_cr0(cr0);
    }

    return i;
}

unsigned int memtest_sub(unsigned int start, unsigned int end)
{
    unsigned int i, *p, old, pat0 = 0xaa55aa55, pat1 = 0x55aa55aa;
    for (i = start; i <= end; i += 0x1000) {
        p = (unsigned int *) (i + 0xffc);
        old = *p;           /* いじる前の値を覚えておく */
        *p = pat0;          /* ためしに書いてみる */
        *p ^= 0xffffffff;   /* そしてそれを反転してみる */
        if (*p != pat1) {   /* 反転結果になったか? */
not_memory:
            *p = old;
            break;
        }
        *p ^= 0xffffffff;   /* もう一度反転してみる */
        if (*p != pat0) {   /* 元に戻ったか? */
            goto not_memory; 
        }
        *p = old;           /* いじった値を元に戻す */
    }
    return i;
}

3. 反思

  1. 其实上面的结果是不对的, 我们实际的内存容量应该只有32MB
  2. 通过 make -r bootpack.nas 可以获取bootpack.c 的汇编文件, 通过查看这个文件, 我们发现:

        _memtest_sub:
            PUSH    EBP
            MOV EBP,ESP
            MOV EDX,DWORD [12+EBP]
            MOV EAX,DWORD [8+EBP]
            CMP EAX,EDX
            JA  L30
        L36:
        L34:
            ADD EAX,4096
            CMP EAX,EDX
            JBE L36
        L30:
            POP EBP
            RET
  3. 显然智能的gcc 编译器, 已经为我们做了很多不必要的优化操作
    这里写图片描述

  4. 为了避免编译器自动优化, 这里考虑使用汇编来编写内存检测代码

_memtest_sub:   ; unsigned int memtest_sub(unsigned int start, unsigned int end)
        PUSH    EDI                     ; (EBX, ESI, EDI も使いたいので)
        PUSH    ESI
        PUSH    EBX
        MOV     ESI,0xaa55aa55          ; pat0 = 0xaa55aa55;
        MOV     EDI,0x55aa55aa          ; pat1 = 0x55aa55aa;
        MOV     EAX,[ESP+12+4]          ; i = start;
mts_loop:
        MOV     EBX,EAX
        ADD     EBX,0xffc               ; p = i + 0xffc;
        MOV     EDX,[EBX]               ; old = *p;
        MOV     [EBX],ESI               ; *p = pat0;
        XOR     DWORD [EBX],0xffffffff  ; *p ^= 0xffffffff;
        CMP     EDI,[EBX]               ; if (*p != pat1) goto fin;
        JNE     mts_fin
        XOR     DWORD [EBX],0xffffffff  ; *p ^= 0xffffffff;
        CMP     ESI,[EBX]               ; if (*p != pat0) goto fin;
        JNE     mts_fin
        MOV     [EBX],EDX               ; *p = old;
        ADD     EAX,0x1000              ; i += 0x1000;
        CMP     EAX,[ESP+12+8]          ; if (i <= end) goto mts_loop;
        JBE     mts_loop
        POP     EBX
        POP     ESI
        POP     EDI
        RET
mts_fin:
        MOV     [EBX],EDX               ; *p = old;
        POP     EBX
        POP     ESI
        POP     EDI
        RET
  1. 效果:
    这里写图片描述

4. 挑战内存管理

  1. 内存管理的基础, 一个是内存分配, 另一个是内存释放
  2. 一种方式是使用 位图管理表, 但是缺点是浪费空间
  3. 另一种方式是使用列表管理方式
    1. 这种方式优点: 占用内存少, 并且大块的内存分配和释放比较迅速
    2. 缺点: 管理程序变得复杂了, 另外当可用空间变得零零散散的时候, 列表空间可能被用完。。。
  4. 这里采用第二种方式进行处理, 相应代码:
    这里写图片描述
    这里写图片描述
    这里写图片描述
    这里写图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值