• MIT 6.S081 Operating System Lecture4 (随意的笔记)


    系列文章目录



    xv6 中的内存页是如何分配的

    在本课中,内存也相关源码路径为:
    kernel/kallo.c

    // Physical memory allocator, for user processes,
    // kernel stacks, page-table pages,
    // and pipe buffers. Allocates whole 4096-byte pages.
    
    #include "types.h"
    #include "param.h"
    #include "memlayout.h"
    #include "spinlock.h"
    #include "riscv.h"
    #include "defs.h"
    
    void freerange(void *pa_start, void *pa_end);
    
    extern char end[]; // first address after kernel.
                       // defined by kernel.ld.
    
    struct run {
      struct run *next;
    };
    
    struct {
      struct spinlock lock;
      struct run *freelist;
    } kmem;
    
    void
    kinit()
    {
      initlock(&kmem.lock, "kmem");
      freerange(end, (void*)PHYSTOP);
    }
    
    void
    freerange(void *pa_start, void *pa_end)
    {
      char *p;
      p = (char*)PGROUNDUP((uint64)pa_start);
      for(; p + PGSIZE <= (char*)pa_end; p += PGSIZE)
        kfree(p);
    }
    
    // Free the page of physical memory pointed at by pa,
    // which normally should have been returned by a
    // call to kalloc().  (The exception is when
    // initializing the allocator; see kinit above.)
    void
    kfree(void *pa)
    {
      struct run *r;
    
      if(((uint64)pa % PGSIZE) != 0 || (char*)pa < end || (uint64)pa >= PHYSTOP)
        panic("kfree");
    
      // Fill with junk to catch dangling refs.
      memset(pa, 1, PGSIZE);
    
      r = (struct run*)pa;
    
      acquire(&kmem.lock);
      r->next = kmem.freelist;
      kmem.freelist = r;
      release(&kmem.lock);
    }
    
    uint64
    kfreemem(void)
    {
      struct run *r;
      uint64 count = 0;
      acquire(&kmem.lock);
      r = kmem.freelist;
      while(r) {
        r = r->next;
        count++;
      }
      release(&kmem.lock);
      return count * PGSIZE;
    }
    
    // Allocate one 4096-byte page of physical memory.
    // Returns a pointer that the kernel can use.
    // Returns 0 if the memory cannot be allocated.
    void *
    kalloc(void)
    {
      struct run *r;
    
      acquire(&kmem.lock);
      r = kmem.freelist;
      if(r)
        kmem.freelist = r->next;
      release(&kmem.lock);
    
      if(r)
        memset((char*)r, 5, PGSIZE); // fill with junk
      return (void*)r;
    }
    
    
    • 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

    如何在一个物理内存上,创建不通的地址空间?
    如上所述:
    最常见的就是分配页,页表(pagetables) – 需要硬件支持或者通过内存管理单元实现(MMU – Memory Management Unit)

    在这里插入图片描述
    上图展示了如下步骤:

    1. CPU 执行指令,指令中的地址是虚拟地址 0x1000
    2. 通过 MMU 转换为物理地址
      • MMU 中维护一张表,用来进行虚拟地址和物理地址的映射关系
      • 通常来说,内存地址对应关系 这张表单也保存在内存中,所以CPU中需要一些寄存器(SATP)来存放表单在物理内存中的地址,用来告诉 MMU 去哪里找这份表单
      • 每个应用程序都有自己的表单,这个表单定义了它的地址空间,当操作系统将CPU从一个应用程序切换到另一个应用程序时,同时也需要切换SATP寄存器中的内容,从而指向新的进程保存在物理内存中的地址对应表单。
    3. 通过真实物理地址进行数据的存取读

    页表存的是物理地址
    通过虚拟地址找到对应的表单和偏移。)

    RISC-V 是多级页表

    多级页表,某种意义上也代表着会读多次内存)
    在这里插入图片描述

    对page table的理解

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    在操作系统,对于物理内存来说,每一个地址间隔所代表的的物理存储单位为 1B(一个字节,这里先不要考虑钻进64位本身占多少的错误思维(我自己之前就是));
    所以一个64位操作系统,不考虑其他因素,CPU的可寻址大小最大为 2^64 Byte(32位是4G,下面的例子拿32位距离比较好理解)
    将物理内存按照块或页分配,概念上理解成每 4KB 一页。即 2^12,即32位地址中,低的那12位可以用来表示对 page table 某页的 Offset, 即偏移。这里存在如下计算:

    1. 一个32位的地址占用 32 / 8 = 4 B 的空间,为了方便所以使用了 4B 作为页表项,这样一个页表就可以有 4KB / 4B = 1K 页表项(单位是页标项)
    2. 其他资料喜欢以1G做例,这里使用最大场景做例子: 一共可以分配出 2^64 / 2^12 = 2^52
    3. 这样对最基础的页表来说,就可以通过将 低位12位作为 offset 来根据虚拟地址求得真实物理地址。
    4. 问题:事实上,页表本身也是需要用物理内存来进行存储的,如果一个应用程序占用内存过大,其所需要生成或使用的页表本身也会占用非常大的内存空间,这样明显过于浪费资源;假如一个应用程序占用4GB内存,那其所需要的的页表就占用 4GB / 4KB * 4B == 4MB , 总内存就是 4G + 4MB;而如果是二级页表:
      4MB的页表还可以继续分配成 4MB / 4KB == 1K 页 --> 1K页 * 4B == 1KB, 占用 4G + 1KB
    相关笔记:顶级页表最多只能有一个页面。(这是规定)
    一个页面可以存放1K个页表项4KB/4B = 1K。
    所以顶级页表虽然只有一个页面但是可以存放1K个页表项,其中每一个页表项对应的是下一级的1K个页表项。所以可以存放的最大空间是1K* 1K *4KB = 4GB内存。
    
    • 1
    • 2
    • 3
  • 相关阅读:
    16 拉格朗日定理和函数的单调性
    设计模式使用场景实现示例及优缺点(行为型模式——策略模式)
    基金的利润分配与税收
    FreeRTOS 事件标志组 详解
    基于SpringBoot编写starter,自己写个类库引入使用
    MyBatis初级
    Java学习笔记 --- 作用域
    Python集合-set使用
    etcd集群部署实战
    使用ORL人脸库,通过GRNN网络和HOG特征提取的人脸识别算法matlab仿真
  • 原文地址:https://blog.csdn.net/qq_39274501/article/details/127812917