• 《真象还原》读书笔记——第八章 内存管理系统(字符串操作、位图定义与实现)


    8.1 makefile

    8.2 实现 assert 断言

    8.2.1 实现开、关中断的函数

    kernel/interrupt.c
    补充了获取中断状态和设置中断的函数

    
    #include "io.h"
    #include "interrupt.h"
    #include "stdint.h"
    #include "global.h"
    #include "print.h"
    
    #define IDT_DESC_CNT	0x21	//支持的中断数目
    #define PIC_M_CTRL  0x20    // 主片的控制端口是 0x20 
    #define PIC_M_DATA  0x21    // 主片的数据端口是 0x21
    #define PIC_S_CTRL  0xa0    // 从片的控制端口是 0xa0
    #define PIC_S_DATA  0xa1    // 从片的数据端口是 0xa1
    
    #define EFLAGS_IF   0x00000200  //eflags 寄存器if位为1
    #define GET_EFLAGS(EFLAG_VAR) asm volatile("pushfl; popl %0":"=g"(EFLAG_VAR));
    
    // 中断门描述符结构体
    struct gate_desc{
        uint16_t    func_offset_low_word;
        uint16_t    selector;
        uint8_t     dcount;
        uint8_t     attribute;
        uint16_t    func_offset_high_word;
    };
    
    //静态函数声明 非必须
    static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function);
    static struct gate_desc idt[IDT_DESC_CNT]; // idt 是中断描述符表
    // 声明引用定义在 kernel.S 中的中断处理函数入口数组
    extern intr_handler intr_entry_table[IDT_DESC_CNT]; 
    char *intr_name[IDT_DESC_CNT];  //保存异常的名字
    /*定义中断处理程序数组,在kernel.asm中定义的intrXXentry
        只是中断处理程序的入口,最终调用的是 ide_table 中的处理程序*/
    intr_handler idt_table[IDT_DESC_CNT];
    
    /*初始化可编程中断处理器 8259A*/
    static void pic_init(void) {
        /*初始化主片 */ 
        outb (PIC_M_CTRL, 0x11); // ICW1: 边沿触发,级联 8259, 需要 ICW4 
        outb (PIC_M_DATA, 0x20); // ICW2: 起始中断向量号为 0x20 
        // 也就是 IR[0-7] 为 0x20 ~ 0x27 
        outb (PIC_M_DATA, 0x04); // ICW3: IR2 接从片
        outb (PIC_M_DATA, 0x01); // ICW4: 8086 模式, 正常 EOI 
    
        /*初始化从片 */ 
        outb (PIC_S_CTRL, 0x11); // ICW1: 边沿触发,级联 8259, 需要 ICW4 
        outb (PIC_S_DATA, 0x28); // ICW2: 起始中断向量号为 0x28 
        // 也就是 IR[8-15]为 0x28 ~ 0x2F 
        outb (PIC_S_DATA, 0x02); // ICW3: 设置从片连接到主片的 IR2 引脚
        outb (PIC_S_DATA, 0x01); // ICW4: 8086 模式, 正常 EOI 
    
        /*打开主片上 IR0,也就是目前只接受时钟产生的中断 */ 
        outb (PIC_M_DATA, 0xfe); //1111 1110 fe
        outb (PIC_S_DATA, 0xff); 
    
        put_str(" pic_init done\n"); 
    
    }
    
    
    /*创建中断门描述符*/
    static void make_idt_desc(struct gate_desc* p_gdesc, uint8_t attr, intr_handler function) { 
        p_gdesc->func_offset_low_word = (int32_t)function & 0x0000FFFF;
        p_gdesc->selector = SELECTOR_K_CODE;
        p_gdesc->dcount = 0;
        p_gdesc->attribute = attr;
        p_gdesc->func_offset_high_word = ((uint32_t)function & 0xFFFF0000) >> 16;
    }
    /*初始化中断描述符表*/
    static void idt_desc_init(void){
        int i;
        for(i = 0; i < IDT_DESC_CNT; ++i){
            make_idt_desc(&idt[i], IDT_DESC_ATTR_DPL0, intr_entry_table[i]);
        }
        put_str("   idt_desc_init done\n");
    }
    
     /*通用的中断处理函数*/
     static void general_intr_handler(uint8_t vec_nr){
        if(vec_nr == 0x27 || vec_nr == 0x2f){
            //IRQ7和IRQ5会产生伪中断,无需处理
            //0x2f是从片 8259A 上的最后一个 IRQ 引脚,保留项
            return;
        }
        put_str("int vector : 0x");
        put_int(vec_nr);
        put_char(' ');
        put_str(intr_name[vec_nr]);
        put_char('\n');
     }
    
    /*完成一般中断处理函数注册及异常名称注册*/
    static void exception_init(void){
        int i;
        for(i = 0; i < IDT_DESC_CNT; ++i){
            /*idt_table中的函数是在进入中断后根据中断向量好调用的
            见kernel/kernel.asm 的 call[idt_table + %1*4]*/
            idt_table[i] = general_intr_handler;
            //默认为general_intr_handler
            //以后会有 register_handler注册具体的处理函数
            intr_name[i] = "unknown";   //先统一为 "unknown"
        }
        intr_name[0] = "#DE Divide Error";
        intr_name[1] = "#DB Debug Exception";
        intr_name[2] = "NMI Interrupt";
        intr_name[3] = "#BP Breakpoint Exception";
        intr_name[4] = "#OF Overflow Exception";
        intr_name[5] = "#BR BOUND Range Exceeded Exception";
        intr_name[6] = "#UD Invalid Opcode Exception";
        intr_name[7] = "#NM Device Not Available Exception";
        intr_name[8] = "#DF Double Fault Exception";
        intr_name[9] = "Coprocessor Segment Overrun"; 
        intr_name[10] = "#TS Invalid TSS Exception";
        intr_name[11] = "#NP Segment Not Present";
        intr_name[12] = "#SS Stack Fault Exception";
        intr_name[13] = "#GP General Protection Exception"; 
        intr_name[14] = "#PF Page-Fault Exception";
        // intr_name[15] 第 15 项是 intel 保留项,未使用
        intr_name[16] = "#MF x87 FPU Floating-Point Error";
        intr_name[17] = "#AC Alignment Check Exception"; 
        intr_name[18] = "#MC Machine-Check Exception";
        intr_name[19] = "#XF SIMD Floating-Point Exception";
        intr_name[0x20] = "#CLOCK";
    
    }
    
    /*获取当前中断状态*/
    enum intr_status intr_get_status(void){
        uint32_t eflags = 0;
        GET_EFLAGS(eflags);
        return (EFLAGS_IF & eflags) ? INTR_ON : INTR_OFF;
    }
    /*设置中断状态*/
    enum intr_status intr_set_status(enum intr_status status){
        return (status & INTR_ON) ? intr_enable():intr_disable();
    }
    
    /*开中断,返回之前的状态*/
    enum intr_status intr_enable(){
        enum intr_status old_status;
        if(INTR_ON == intr_get_status()){
            old_status = INTR_ON;
        }else{
            old_status = INTR_OFF;
            asm volatile("sti");//关中断 cli 将IF置1
        }
        return old_status;
    }
    /*关闭中断*/
    enum intr_status intr_disable(void){
        enum intr_status old_status;
        if(INTR_ON == intr_get_status()){
            old_status = INTR_ON;
            asm volatile("cli" : : : "memory");
        }else{
            old_status = INTR_OFF;
        }
        return old_status;
    }
    
     /*完成有关中断的所有初始化工作*/
     void idt_init(){
        put_str("idt_init start\n");
        idt_desc_init();    // 初始化中断描述符表
        exception_init();   //异常名和中断处理函数初始化
        pic_init();         //初始化 8259A
    
        /*加载 idt*/
        uint64_t idt_operand = ((sizeof(idt) - 1) | ((uint64_t)((uint32_t)idt << 16))); 
        asm volatile("lidt %0"::"m"(idt_operand));
        put_str("idt_init done\n");
     }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173

    kernel / interrupt.h

    #ifndef __KERNEL_INTERRUPT_H
    #define __KERNEL_INTERRUPT_H
    
    #include "stdint.h"
    typedef void* intr_handler;
    
    enum intr_status{
        INTR_OFF,
        INTR_ON
    };
    
    void idt_init();
    
    enum intr_status intr_get_status(void);
    enum intr_status intr_set_status(enum intr_status);
    enum intr_status intr_enable(void);
    enum intr_status intr_disable(void);
    
     #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    kernel / debug.h

    #ifndef __KERNEL_DEBUG_H
    #define __KERNEL_DEBUG_H
    
    void panic_spin(char* filename, int line, const char* func, const char* condition); 
    /************* __VA_ARGV__ *********
     * __VA_ARGS__ 是预处理器所支持的专用标识符
     * 代表所有与省略号相对应的参数
     * "..."表示定义的宏其参数可变。
     */
    #define PANIC(...) panic_spin(__FILE__, __LINE__, __func__, __VA_ARGS__)
    /*********************************************/
        #ifdef NDEBUG
            #define ASSERT(CONDITION) ((void)0)
        #else
            #define ASSERT(CONDITION) \
            if(CONDITION){}else{ \
                /*符号#让编译器将宏的参数转化为字符串字面量 */ \
                PANIC(#CONDITION);  \
            }
        #endif /*__NDEBUG*/
    #endif /*__KERNEL_DEBUG_H*/
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    kernel / debug.c

    #include "debug.h"
    #include "print.h"
    #include "interrupt.h"
    
    /*打印文件名、行号、函数名、条件并使程序悬停*/
    void panic_spin(char *filename, int line, const char *func, const char *cons )
    {
        intr_disable();//有时候会单独调用 panic_spin , 所以在此处关中断
        put_str("\n\n\n!!!!! error !!!!!\n");
        put_str("filename:");put_str(filename);put_str("\n");
        put_str("line:0x");put_int(line);put_str("\n");
        put_str("function:");put_str((char*)func);put_str("\n");
        put_str("condition:");put_str((char*)cons);put_str("\n");
        while(1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    kernel / main.c

    #include "print.h"
    #include "init.h"
    #include "debug.h"
    int main(void){
        put_str("I am kernel\n");
        init_all();
        ASSERT(1==2);
        //asm volatile("sti");
    
        while(1);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果:
    在这里插入图片描述

    8.3 实现字符串操作函数

    lib / string.h

    #ifndef __LIB_STRING_H
    #define __LIB_STRING_H
    
    #include "stdint.h"
    
    /*将dst_起始的size个字节置为value*/
     void memset(void *dst_, uint8_t value, uint32_t size);
    
     /*将src_起始的size个字节复制到 dst_ */
     void memcpy(void *dst_, const void *src, uint32_t size);
     /*连续比较以地址a_和地址b_开头的size个字节.
        相等则返回 0, a>b return 1, a
    int memcmp(const void *a_, const void *b_, uint32_t size);
    
    /*src_ 复制到 dst_*/
    char *strcpy(char *dst_, const char *src_);
    
    /*返回字符串长度*/
    uint32_t strlen(const char *str){;
    /*比较两个字符串,若a_中的字符大于b_中的字符调用1,相等返回0,否则返回-1*/
    int8_t strcmp(const char *a, const char *b);
    
    /*左到右找str中首次出现的 ch 的地址*/
    char *strchr(const char *str, const uint8_t ch);
    
    /*从后往前找字符串str中首次出现的ch的地址*/
    char *strrchr(const char *str, const uint8_t ch);
    
    /*将字符串 src 拼接到dst_后,返回拼接的字符串地址*/
    char *strcat(char *dst_, const char *str_);
    
    /*在字符串str中查找字符ch出现的次数*/
    uint32_t strchrs(const char *str, uint8_t ch);
    
    #endif /*__LIB_STRING_H*/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    lib / string.c

    #include "string.h"
    #include "global.h"
    #include "debug.h"
    
    /*将dst_起始的size个字节置为value*/
     void memset(void *dst_, uint8_t value, uint32_t size)
     {
        ASSERT(dst_ != NULL);
        uint8_t *dst = (uint8_t*)dst_;
        while(size-- > 0){
            *dst++ = value;
        }
        return;
     }
    
     /*将src_起始的size个字节复制到 dst_ */
     void memcpy(void *dst_, const void *src, uint32_t size){
        ASSERT(dst_ != NULL && src_ != NULL);
        uint8_t *dst = dst_;
        const uint8_t *src = src_;
        while(size-- > 0){
            *dst++ = *src++;
        }
     }
     /*连续比较以地址a_和地址b_开头的size个字节.
        相等则返回 0, a>b return 1, a
    int memcmp(const void *a_, const void *b_, uint32_t size)
    {
        const char *a = a_;
        const char *b = b_;
        ASSERT(a != NULL || b != NULL);
        while(size-- > 0){
            if(*a != *b){
                return (*a > *b) ? 1: -1;
            }
            ++a;
            ++b;
        }
        return 0;
    }
    
    /*src_ 复制到 dst_*/
    char *strcpy(char *dst_, const char *src_)
    {
        ASSERT(dst_ != NULL && src_ != NULL);
        char *r = dst_;
        while((*dst_ ++ = *src_ ++));
        return r;
    }
    
    /*返回字符串长度*/
    uint32_t strlen(const char *str){
        ASSERT(str != NULL);
        const char *p = str;
        while(*p++);
        return (p - str - 1);
    }
    /*比较两个字符串,若a_中的字符大于b_中的字符调用1,相等返回0,否则返回-1*/
    int8_t strcmp(const char *a, const char *b)
    {
        ASSERT(a != NULL && b != NULL);
        while(*a != 0 && *a == *b){
            ++a;
            ++b;
        }
        //if(*a < *b) return -1;
        //if(*a > *b) return 1; else return 0;
        return (*a < *b) ? -1 : (*a > *b);
    }
    
    /*左到右找str中首次出现的 ch 的地址*/
    char *strchr(const char *str, const uint8_t ch)
    {
        ASSERT(str != NULL);
        while(*str != 0){
            if(*str == ch){
                return (char *)str;//需要强制转化为返回值类型。
            }
            ++str;
        }
        return NULL;
    }
    
    /*从后往前找字符串str中首次出现的ch的地址*/
    char *strrchr(const char *str, const uint8_t ch)
    {
        ASSERT(str != NULL);
        const char *last_char = NULL;
        while(*str != 0){
            if(*str == ch){
                last_char = str;
            }
            ++str;
    
        }
        return (char *)last_char;
    }
    
    /*将字符串 src 拼接到dst_后,返回拼接的字符串地址*/
    char *strcat(char *dst_, const char *str_)
    {
        ASSERT(dst_ != NULL && src_ != NULL);
        char *str = dst_;
        while(*str++);
        --str;
        while((*str++ = *src_++));
        return dst_;
    }
    
    /*在字符串str中查找字符ch出现的次数*/
    uint32_t strchrs(const char *str, uint8_t ch){
        ASSERT(str != NULL);
        uint32_t ch_cnt = 0;
        const char *p = str;
        while(*p != 0){
            if(*p == ch){
                ++ch_cnt;
            }
            ++p;
        }
        return ch_cnt;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122

    8.4 位图 bitmap 及其函数的实现

    8.4.1 位图简介

    一个字节有8位,所以位图的一个字节对应8个资源单位。假设是管理内存,每一位都将表示实际物理内存中的 4KB。也就是 1页。如果某位为0,就是可以分配,如果某位为1,就是不可分配。
    在这里插入图片描述

    8.4.2 位图的定义和实现

    lib / kernel / bitmap.h

    #ifndef __LIB_KERNEL_BITMAP_H
    #define __LIB_KERNEL_BITMAP_H
    #include "global.h"
    #define BITMAP_MASK 1
    struct bitmap{
        uint32_t btmp_bytes_len;
        /*遍历位图时候是以字节为单位,微操是位。
        所以此处的位图指针必须是单字节*/
        uint8_t *bits;
    };
    void bitmap_init(struct bitmap *btmp);
    bool bitmap_scan_test(struct bitmap *btmp, uint32_t bit_idx);
    int bitmap_scan(struct bitmap *btmp, uint32_t cnt);
    void bitmap_set(struct bitmap *btmp, uint32_t bit_idx, int8_t value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    lib/ kernel/ bitmap.c

    #include "bitmap.h"
    #include "stdint.h"
    #include "string.h"
    #include "print.h"
    #include "interrupt.h"
    #include "debug.h"
    
    /*初始化 btmp 位图*/
    void bitmap_init(struct bitmap *btmp)
    {
        memset(btmp->bits, 0, btmp->btmp_bytes_len);
    }
    
    bool bitmap_scan_test(struct bitmap *btmp, uint32_t bit_idx)
    {
        uint32_t byte_idx = bit_idx / 8;//向下取整用于数组索引。
        uint32_t bit_odd = bit_idx % 8; //取余用索引数组内的位
        return (btmp->bits[byte_idx] & (BITMAP_MASK << bit_odd));
    }
    
    int bitmap_scan(struct bitmap *btmp, uint32_t cnt)
    {
        uint32_t idx_byte = 0;
        /*先字节比较*/
        while((0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)){
            //该字节无空位,去下一个字节
            ++idx_byte;
        }
        ASSERT(idx_byte < btmp->btmp_bytes_len);
        if(idx_byte == btmp->btmp_bytes_len){ //找不到可用空间
            return -1;
        }
    
        //某字节有空位,则依次查找
        int idx_bit = 0;
        while((uint8_t)(BITMAP_MASK << idx_bit & btmp->bits [idx_byte]){
            ++idx_bit;
        }
    
        int bit_idx_start = idx_byte * 8 + idx_bit;
        if(cnt == 1){
            return bit_idx_start;
        }
        uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start);
        //记录还有多少位可以判断
        uint32_t next_bit = bit_idx-start + 1;
        uint32_t count = 1;
    
        bit_idx_start = -1;
        while (bit_left -- > 0){
            if(!(bitmap_scan_test(btmp, next_bit))){
                count++;
            }else{
                count = 0;
            }
            if(count == cnt){
                bit_idx_start = next_bit - cnt + 1;
                break;
            }
            next_bit++;
        }
        return bit_idx_start;
    }
    /*将位图bit_idx设置为value*/
    void bitmap_set(struct bitmap *btmp, uint32_t bit_idx, int8_t value)
    {
        ASSERT((value == 0) || (value == 1));
        uint32_t byte_idx = bit_idx / 8;
        uint32_t bit_odd = bit_idx % 8;
    
        if(value){
            btmp->bits[byte_idx] != (BITMAP_MASK << bit_odd);
        }else{
            btmp->bits[byte_idx] &= (BITMAP_MASK << bit_odd);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    8.5 内存管理系统

    8.5.1 内存池规划

    分页机制下有了虚拟、物理这两种地址,们本节所讨论的就是有关这两类地址的内存池规划问题。
    创建虚拟内存地址池和物理内存地址池
    内存的规划
    分为内核内存池和用户内存池。
    内存池中的内存单位大小是 4KB.
    所以任务都有各自的 4GB,需要为所有任务维护它们自己的虚拟地址池,一个任务一个。
    在这里插入图片描述
    kernel / memory.h

    #ifndef __KERNEL_MEMORY_H
    #define __KERNEL_MEMORY_H
    
    #include "stdint.h"
    #include "bitmap.h"
    
    /*虚拟地址池,用于虚拟地址管理*/
    struct virtual_addr{
        struct bitmap vaddr_bitmap; // 虚拟地址用到的位图结构
        uint32_t vaddr_start;       // 虚拟地址起始地址
    };
    extern struct pool kernel_pool, user_pool;
    void mem_init(void);
    
    #endif
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    kernel / memory.c

    #include "memory.h"
    #include "stdint.h"
    #include "print.h"
    
    #define PG_SIZE 4096
    
    /*************** 位图地址 ******************
     * 因为0xc009f00 是内核主线程栈顶, 0xc009e000 是内核主线程的 pcb
     * 一个页框大小的位图可表示 128MB 内存,位图位置安排在地址 0xc009a000,
     * 这样本系统最大支持 4个页框的位图,即 512 MB
    */
    #define MEM_BITMAP_BASE 0xc009a000
      /* 0xc0000000 是内核从虚拟地址 3G 起,
      0x100000 指跨过低端 1MB 内存,使虚拟地址在逻辑上连续。*/
    #define K_HEAP_START 0xc0100000
    /* 内存池结构,生成两个实例,用于管理内核和用户内存池*/
    struct pool{
        struct bitmap pool_bitmap;  //本内存池用到的位图结构,用于管理物理内存
        uint32_t phy_addr_start;    // 本内存池所管理物理内存的起始地址
        uint32_t pool_size;         // 本内存池字节容量
    };
    struct pool kernel_pool, user_pool; // 生成内核内存池和用户内存池
    struct virtual_addr kernel_vaddr; // 此结构用来给内核分配虚拟地址
    
    /*初始化内存池*/
    static void mem_pool_init(uint32_t all_mem)
    {
        put_str("   mem_pool_init start\n");
        uint32_t page_table_size = PG_SIZE * 256;
        /*页表大小 = 页目录表(1页) + 第0和第768页目录框指向同一个页表(第1个页表)+
        第 769~1022个页目录项共指向 254 个页表(254页),共 256 个页框*/
    
        uint32_t used_mem = page_table_size + 0x100000;
        //0x100000 为低端 1MB 内存。
        uint32_t free_mem = all_mem - used_mem;
        uint16_t all_free_pages = free_mem / PG_SIZE;
        /*1页为4KB,不管总内存是不是 4k 的倍数,
         *对于以页为单位的内存分配策略,不足一页的内存不用考虑了*/
        uint16_t kernel_free_pages = all_free_pages / 2; 
        uint16_t user_free_pages = all_free_pages - kernel_free_pages;
        /* 为简化位图操作,余数不处理,坏处是这样做会丢内存。
         好处是不用做内存的越界检查,因为位图表示的内存少于实际物理内存*/
        uint32_t kbm_length = kernel_free_pages / 8; 
        // Kernel BitMap 的长度,位图中的一位表示一页,以字节为单位
        uint32_t ubm_length = user_free_pages / 8;
        // User BitMap 的长度
        //Kernel pool start,内核内存池的起始地址。
        uint32_t kp_start = used_mem;
        //User Pool start,  用户内存池的起始地址
        uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE;
    
        kernel_pool.phy_addr_start = kp_start;
        user_pool.phy_addr_start = up_start;
    
        kernel_pool.pool_size = kernel_free_pages * PG_SIZE;
        user_pool.pool_size = user_free_pages * PG_SIZE;
    
        kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
        user_pool.pool_bitmap.btmp_bytes_len = ubm_length;
    
         /********* 内核内存池和用户内存池位图 ***********
          * 位图是全局的数据,长度不固定。
          * 全局或静态的数组需要在编译时知道其长度,
          * 而我们需要根据总内存大小算出需要多少字节,
          * 所以改为指定一块内存来生成位图。
          ***********************************************/
    
         //内核使用的最高 0xc009f000,这是主线程的栈地址
         //(内核的大小预计为70KB左右)
         //32MB内存占有的位图为 2KB
         //内核内存池的位图先定在 MEM_BITMAP_BASE (0xc009a000) 处
         kernel_pool.pool_bitmap.bits = (void*)MEM_BITMAP_BASE;
         /* 用户内存池的位图紧跟在内核内存池位图之后 */
         user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length);
         /********************输出内存池信息**********************/ 
        put_str(" kernel_pool_bitmap_start:");
        put_int((int)kernel_pool.pool_bitmap.bits); 
        put_str(" kernel_pool_phy_addr_start:"); 
        put_int(kernel_pool.phy_addr_start);
        put_str("\n"); 
        put_str("user_pool_bitmap_start:");
        put_int((int)user_pool.pool_bitmap.bits);
        put_str(" user_pool_phy_addr_start:");
        put_int(user_pool.phy_addr_start);
        put_str("\n");
    
        /* 将位图置于 0*/
        bitmap_init(&kernel_pool.pool_bitmap);
        bitmap_init(&user_pool.pool_bitmap); 
        put_str("bitmap_init2\n");
        /* 下面初始化内核虚拟地址的位图,按实际物理内存大小生成数组。*/ 
        kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length; 
        // 用于维护内核堆的虚拟地址,所以要和内核内存池大小一致
        /* 位图的数组指向一块未使用的内存,目前定位在内核内存池和用户内存池之外*/
        kernel_vaddr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length);
        kernel_vaddr.vaddr_start = K_HEAP_START;
        bitmap_init(&kernel_vaddr.vaddr_bitmap);
        put_str("   mem_pool_init done\n");
    }
    
    /*内存管理部分初始化入口*/
    void mem_init()
    {
        put_str("mem_init start\n");
        uint32_t mem_bytes_total = (*(uint32_t *)(0xb00));
        mem_pool_init(mem_bytes_total);// 初始化内存池
        put_str("mem_init done\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108

    还有一点:
    这里的 total_mem_bytes 一定要放在这个位置上,恰好是 地址 0xb000。
    因为 mem_init() 中要用到这个地址。上面存放的是内存大小。

    loader.asm

    %include "boot.inc"
    
    section loader vstart=LOADER_BASE_ADDR
    
    GDT_BASE:
        dd  0x00000000
        dd  0x00000000
    CODE_DESC:
        dd  0x0000FFFF
        dd  DESC_CODE_HIGH4
    DATA_STACK_DESC:
        dd  0x0000FFFF
        dd  DESC_DATA_HIGH4
    VIDEO_DESC:
        dd  0x80000007
        dd  DESC_VIDEO_HIGH4
    GDT_SIZE            equ $-GDT_BASE
    GDT_LIMIT           equ GDT_SIZE-1
    times   60 dq   0;预留60个描述符的空位
    ;选择子
    SELECTOR_CODE   equ (0x0001 << 3) + TI_GDT + RPL0
    SELECTOR_DATA   equ (0x0002 << 3) + TI_GDT + RPL0
    SELECTOR_VIDEO  equ (0x0003 << 3) + TI_GDT + RPL0
    
    ;============= 读取内存数 
    total_mem_bytes     dd  0  ;4 保存内存容量
    ;gdt 指针
    gdt_ptr dw  GDT_LIMIT
            dd  GDT_BASE
    
    
    ards_buf    times   244 db 0
    ards_nr     dw      0
    /* ...略 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    kernel / init.c

    #include "init.h"
    #include "print.h"
    #include "interrupt.h"
    #include "../device/timer.h"
    #include "memory.h"
    
    /*负责初始化所有模块*/
    void init_all()
    {
        put_str("init_all\n");
        idt_init(); //初始化中断
        //timer_init();
        mem_init();
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    kernel / main.c

    #include "print.h"
    #include "init.h"
    #include "debug.h"
    int main(void){
        put_str("I am kernel\n");
        init_all();
        //ASSERT(1==2);
        //asm volatile("sti");
    
        while(1);
        return 0;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    运行结果:
    在这里插入图片描述

  • 相关阅读:
    BIT-4-数组
    python入门
    AI发展目前最大挑战是什么?
    它让你1小时精通RabbitMQ消息队列、且能扛高并发
    CSRF(Pikachu靶场练习)
    Intellij IDEA--导入导出配置
    【c++】多态
    ES组合查询语法
    【iOS】—— 深入了解GCD
    Node基础
  • 原文地址:https://blog.csdn.net/orange1710/article/details/136302295