IT教程 ·

【克己操作体系10】内存治理体系

【区块链】关于ETH/BTC区块的监控

本章我们要完成开端的内存治理体系,终究简朴完成一个从内核内存池中猎取 3 页的内存如许一个函数完成。

一、到现在为止的程序流程图

为了让人人清晰现在的程序进度,画了到现在为止的程序流程图,以下。

图中赤色部份就是我们本章的代码在全局流程中的位置,下面蓝色部份是将 malloc_page 要领树状拆解开来看。不斟酌太多细节,本章就是完成一个可以 从内存中分派指定页数的内存(代码中为 3 页),并将肇端地点打印出来。下面我们看看要怎样完成这个功用。

二、先上代码

主要代码

 1 #include "print.h"
 2 #include "init.h"
 3 void main(void){
 4     put_str("I am kerneln");
 5     init_all();
 6     // 这就是我们本日主要完成的功用,从内核的内存池中请求到 3 页的内存页
 7     void* addr = get_kernel_pages(3);
 8     put_str("n get_kernel_pages start vaddr is ");
 9     put_int((uint32_t)addr);
10     put_str("n");
11     while(1);
12 }

main.c

  1 #include "memory.h"
  2 #include "bitmap.h"
  3 #include "stdint.h"
  4 #include "global.h"
  5 #include "print.h"
  6 #include "string.h"
  7 #include "interrupt.h"
  8 
  9 #define PG_SIZE 4096
 10 #define MEM_BITMAP_BASE 0xc009a000
 11 #define K_HEAP_START 0xc0100000
 12 
 13 #define PDE_IDX(addr) ((addr & 0xffc00000) >> 22) // 假造地点高10位,pde
 14 #define PTE_IDX(addr) ((addr & 0x003ff000) >> 12) // 假造地点中心10位,pte
 15 
 16 struct pool {
 17     struct bitmap pool_bitmap;
 18     uint32_t phy_addr_start; //本内存池治理的物理内存肇端
 19     uint32_t pool_size;
 20 };
 21 
 22 struct pool kernel_pool, user_pool;
 23 struct virtual_addr kernel_vaddr;
 24 
 25 // 在pf示意的假造内存池中请求pg_cnt个假造页,胜利返回肇端地点,失利返回NULL
 26 static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) {
 27     int vaddr_start = 0, bit_idx_start = -1;
 28     uint32_t cnt = 0;
 29     if (pf == PF_KERNEL) {
 30         bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap, pg_cnt);
 31         if (bit_idx_start == -1) {
 32             return NULL;
 33         }
 34         while(cnt < pg_cnt) {
 35             bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
 36         }
 37         vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE;
 38     } else {
 39         // 用户内存池,未来再说
 40     }
 41     return (void*)vaddr_start;
 42 }
 43 
 44 // 取得假造地点vaddr对应的pte指针
 45 uint32_t* pte_ptr(uint32_t vaddr) {
 46     uint32_t* pte = (uint32_t*)(0xffc00000 + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4);
 47     return pte;
 48 }
 49 
 50 // 取得假造地点vaddr对应的pde指针
 51 uint32_t* pde_ptr(uint32_t vaddr) {
 52     uint32_t* pde = (uint32_t*)((0xfffff000) + PDE_IDX(vaddr) * 4);
 53     return pde;
 54 }
 55 
 56 //在m_pool指向的物理内存池中分派1个物理页
 57 static void* palloc(struct pool* m_pool) {
 58     //找到一个物理页
 59     int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1);
 60     if (bit_idx == -1) {
 61         return NULL;
 62     }
 63     // 将此位置1将
 64     bitmap_set(&m_pool->pool_bitmap, bit_idx, 1);
 65     uint32_t page_phyaddr = ((bit_idx * PG_SIZE) + m_pool->phy_addr_start);
 66     return (void*)page_phyaddr;
 67 }
 68 
 69 // 页表中增加假造地点_vaddr与物理地点_page_phyaddr的映照
 70 static void page_table_add(void* _vaddr, void* _page_phyaddr) {
 71     uint32_t vaddr = (uint32_t)_vaddr;
 72     uint32_t page_phyaddr = (uint32_t)_page_phyaddr;
 73     uint32_t* pde = pde_ptr(vaddr);
 74     uint32_t* pte = pte_ptr(vaddr);
 75     
 76     // 推断页目次项的p位,为1示意该表已存在
 77     if (*pde & 0x00000001) {
 78         if(!(*pte & 0x00000001)) {
 79             *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
 80         } else {
 81             // pte repeat
 82         }
 83     } else {
 84         // 页目次项不存在,先竖立页目次项,再竖立页表项
 85         uint32_t pde_phyaddr = (uint32_t)palloc(&kernel_pool);
 86         *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
 87         memset((void*)((int)pte & 0xfffff000), 0, PG_SIZE);
 88         *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
 89     }
 90 }
 91 
 92 // 分派pg_cnt个页空间,胜利返回肇端假造地点,失利返回NULL
 93 void* malloc_page(enum pool_flags pf, uint32_t pg_cnt) {
 94     // 1 经由过程 vaddr_get 在假造内存池中请求假造地点
 95     // 2 经由过程 palloc 在物理内存池中请求物理页
 96     // 3 经由过程 page_table_add 将以上取得的假造地点和物理地点在页表中完成映照
 97     void* vaddr_start = vaddr_get(pf, pg_cnt);
 98     if (vaddr_start == NULL) {
 99         return NULL;
100     }
101     
102     uint32_t vaddr = (uint32_t)vaddr_start;
103     uint32_t cnt = pg_cnt;
104     struct pool* mem_pool = pf & PF_KERNEL ? &kernel_pool : &user_pool;
105     
106     // 假造地点和物理地点逐一映照
107     while (cnt-- > 0) {
108         void* page_phyaddr = palloc(mem_pool);
109         if (page_phyaddr == NULL) {
110             return NULL;
111         }
112         page_table_add((void*)vaddr, page_phyaddr);
113         vaddr += PG_SIZE;
114     }
115     return vaddr_start;
116 }
117 
118 // 从内核物理内存池中请求1页内存,胜利返回假造地点,失利NULL
119 void* get_kernel_pages(uint32_t pg_cnt) {
120     void* vaddr = malloc_page(PF_KERNEL, pg_cnt);
121     if (vaddr != NULL) {
122         memset(vaddr, 0, pg_cnt * PG_SIZE);
123     }
124     return vaddr;
125 }        
126 
127 // 初始化内存池
128 static void mem_pool_init(uint32_t all_mem) {
129     put_str("  mem_pool_init startn");
130     uint32_t page_table_size = PG_SIZE * 256;
131     uint32_t used_mem = page_table_size + 0x100000; // 低端1M内存 + 页表大小
132     uint32_t free_mem = all_mem - used_mem;
133     uint16_t all_free_pages = free_mem / PG_SIZE;
134     
135     uint16_t kernel_free_pages = all_free_pages / 2; // 用户和内核各分一半的可用内存
136     uint16_t user_free_pages = all_free_pages - kernel_free_pages;
137     uint32_t kbm_length = kernel_free_pages / 8;
138     uint32_t ubm_length = user_free_pages / 8;
139     uint32_t kp_start = used_mem; // 内核内存池肇端
140     uint32_t up_start = kp_start + kernel_free_pages * PG_SIZE;
141     
142     kernel_pool.phy_addr_start = kp_start;
143     user_pool.phy_addr_start = up_start;
144     
145     kernel_pool.pool_size = kernel_free_pages * PG_SIZE;
146     user_pool.pool_size = user_free_pages * PG_SIZE;
147     
148     kernel_pool.pool_bitmap.btmp_bytes_len = kbm_length;
149     user_pool.pool_bitmap.btmp_bytes_len = ubm_length;
150     
151     kernel_pool.pool_bitmap.bits = (void*)MEM_BITMAP_BASE;
152     user_pool.pool_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length);
153     
154     // 输出内存池信息
155     put_str(" kernel_pool_bitmap_start:"); 
156     put_int((int)kernel_pool.pool_bitmap.bits); 
157     put_str(" kernel_pool_phy_addr_start:"); 
158     put_int(kernel_pool.phy_addr_start); 
159     put_str("n"); 
160     put_str("user_pool_bitmap_start:"); 
161     put_int((int)user_pool.pool_bitmap.bits); 
162     put_str(" user_pool_phy_addr_start:"); 
163     put_int(user_pool.phy_addr_start); 
164     put_str("n");
165     
166     // 将位图直0
167     bitmap_init(&kernel_pool.pool_bitmap);
168     bitmap_init(&user_pool.pool_bitmap);
169     
170     // 初始化内核假造地点位图
171     kernel_vaddr.vaddr_bitmap.btmp_bytes_len = kbm_length;
172     kernel_vaddr.vaddr_bitmap.bits = (void*)(MEM_BITMAP_BASE + kbm_length + ubm_length);
173     kernel_vaddr.vaddr_start = K_HEAP_START;
174     bitmap_init(&kernel_vaddr.vaddr_bitmap);
175     put_str("   mem_pool_init donen");
176 }
177 
178 // 初始化内存
179 void mem_init() {
180     put_str("mem_init startn");
181     //uint32_t mem_bytes_total = (*(uint32_t*)(0xb00));
182     uint32_t mem_bytes_total = 32 * 1024 * 1024;
183     mem_pool_init(mem_bytes_total);
184     put_str("mem_init donen");
185 }

memory.c

 1  #include "bitmap.h" 
 2  #include "stdint.h" 
 3  #include "string.h" 
 4  #include "print.h" 
 5  #include "interrupt.h" 
 6  
 7  // 位图初始化,把每一位都设置为0
 8  void bitmap_init(struct bitmap* btmp) {
 9      memset(btmp->bits, 0, btmp->btmp_bytes_len);
10  }
11  
12  // 推断bit_idx位是不是为1,若为1,则返回true
13  bool bitmap_scan_test(struct bitmap* btmp, uint32_t bit_idx) {
14      uint32_t byte_idx = bit_idx / 8;
15      uint32_t bit_odd = bit_idx % 8;
16      return (btmp->bits[byte_idx] & (1 << bit_odd));
17  }
18  
19  // 在位图中请求一连cnt个位,胜利则返回肇端位下标,失利返回-1
20  int bitmap_scan(struct bitmap* btmp, uint32_t cnt) {
21      uint32_t idx_byte = 0;
22      // 逐一字节比较
23      while((0xff == btmp->bits[idx_byte]) && (idx_byte < btmp->btmp_bytes_len)) {
24          idx_byte++;
25      }
26      //未找到余暇位,返回-1
27      if(idx_byte == btmp->btmp_bytes_len) {
28          return -1;
29      }
30      // 某字节中找到了余暇位
31      int idx_bit = 0;
32      while((uint8_t)(1 << idx_bit) & btmp->bits[idx_byte]) {
33          idx_bit++;
34      }
35      int bit_idx_start = idx_byte * 8 + idx_bit;
36      // 只须要1位直接返回
37      if (cnt == 1) {
38          return bit_idx_start;
39      }
40      // 须要多于1位,还得继承推断
41      uint32_t bit_left = (btmp->btmp_bytes_len * 8 - bit_idx_start);
42      uint32_t next_bit = bit_idx_start + 1;
43      uint32_t count = 1; // 已找到的余暇位
44      
45      bit_idx_start = -1;
46      while(bit_left-- > 0) {
47          if(!(bitmap_scan_test(btmp, next_bit))) {
48              count++;
49          } else {
50              count = 0;
51          }
52          // 找到一连的cnt个空位
53          if(count == cnt) {
54              bit_idx_start = next_bit - cnt + 1;
55              break;
56          }
57          next_bit++;
58      }
59      return bit_idx_start;
60  }
61      
62  // 将位图btmp的bit_idx位设置为value
63  void bitmap_set(struct bitmap* btmp, uint32_t bit_idx, int8_t value) {
64      uint32_t byte_idx = bit_idx / 8;
65      uint32_t bit_odd = bit_idx % 8;
66      
67      if(value) {
68          // value为1
69          btmp->bits[byte_idx] |= (1 << bit_odd);
70      } else {
71          btmp->bits[byte_idx] &= ~(1 << bit_odd);
72      }
73  }

bitmap.c

头文件及其他

 1 mbr.bin: mbr.asm
 2     nasm -I include/ -o out/mbr.bin mbr.asm -l out/mbr.lst
 3     
 4 loader.bin: loader.asm
 5     nasm -I include/ -o out/loader.bin loader.asm -l out/loader.lst
 6     
 7 kernel.bin: kernel/main.c
 8     gcc -I lib/kernel/ -I lib/ -I kernel/ -c -fno-builtin -o out/main.o kernel/main.c
 9     nasm -f elf -o out/print.o lib/kernel/print.asm -l out/print.lst
10     nasm -f elf -o out/kernel.o kernel/kernel.asm -l out/kernel.lst
11     gcc -I lib/kernel/ -I lib/ -I kernel/ -c -fno-builtin -o out/string.o lib/string.c
12     gcc -I lib/kernel/ -I lib/ -I kernel/ -c -fno-builtin -o out/interrupt.o kernel/interrupt.c
13     gcc -I lib/kernel/ -I lib/ -I kernel/ -c -fno-builtin -o out/init.o kernel/init.c
14     gcc -I lib/kernel/ -I lib/ -I kernel/ -c -fno-builtin -o out/bitmap.o kernel/bitmap.c
15     gcc -I lib/kernel/ -I lib/ -I kernel/ -c -fno-builtin -o out/memory.o kernel/memory.c
16     ld -Ttext 0xc0001500 -e main -o out/kernel.bin out/main.o out/init.o out/interrupt.o out/print.o out/kernel.o out/memory.o out/bitmap.o out/string.o
17     
18 os.raw: mbr.bin loader.bin kernel.bin
19     ../bochs/bin/bximage -hd -mode="flat" -size=60 -q target/os.raw
20     dd if=out/mbr.bin of=target/os.raw bs=512 count=1
21     dd if=out/loader.bin of=target/os.raw bs=512 count=4 seek=2
22     dd if=out/kernel.bin of=target/os.raw bs=512 count=200 seek=9
23     
24 run:
25     make install
26     make only-qemu-run
27     
28 brun:
29     make install
30     make only-bochs-run
31     
32 bdrun:
33     make install
34     make only-bochsdbg-run
35     
36 only-qemu-run:
37     qemu-system-i386 -m 512 target/os.raw
38     
39 only-bochs-run:
40     ../bochs/bin/bochs -f ../bochs/bochsrc.disk -q
41     
42 only-bochsdbg-run:
43     ../bochs/bin/bochs -f ../bochs/bochsrc.disk -q
44     
45 only-run-s:
46     qemu-system-i386 -s -S -m 512 target/os.raw --nographic
47     
48 install:
49     make clean
50     make -r os.raw
51     
52 clean:
53     rm -rf target/*
54     rm -rf out/*
55     rm -rf os.raw
56     rm -rf os.raw.lock
57     rm -rf bochs.out

Makefile

 1 #ifndef __KERNEL_MEMORY_H
 2 #define __KERNEL_MEMORY_H
 3 #include "stdint.h"
 4 #include "bitmap.h"
 5 
 6 enum pool_flags {
 7     PF_KERNEL = 1, // 内核内存池
 8     PF_USER = 2 // 用户内存池
 9 };
10 
11 #define PG_P_1  1    // 页表项或页目次项存在属性位
12 #define PG_P_0  0    // 页表项或页目次项存在属性位
13 #define PG_RW_R 0    // R/W 属性位值, 读/实行
14 #define PG_RW_W 2    // R/W 属性位值, 读/写/实行
15 #define PG_US_S 0    // U/S 属性位值, 体系级
16 #define PG_US_U 4    // U/S 属性位值, 用户级
17 
18 // 假造地点池,用于假造地点治理
19 struct virtual_addr {
20     struct bitmap vaddr_bitmap;
21     uint32_t vaddr_start;
22 };
23 
24 extern struct pool kernel_pool, user_pool;
25 void mem_init(void);
26 #endif

memory.h

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

bitmap.h

三、代码解读

整段代码肇端就做了两件事

  1. 初始化内存池,包含内核内存池用户内存池。每一个内存池离别有 物理的(kernel_pool、user_pool)和 假造的(kernel_vaddr、user_vaddr)两种,治理体式格局是经由过程 bitmap 这类数据构造完成的
  2. 完成请求内存函数,本章仅完成了 get_kernel_pages,即从内核物理内存池中请求1页内存,胜利返回假造地点,失利NULL

我把上面两件事画在了一张图里,左侧展现了我们的内存计划,以及一些症结的数据构造 bitmap 在内存中的位置。右侧是终究完成的函数 get_kernel_pages 要做的三件事,即

  1. vaddr_get,从假造地点中猎取一连可用内存
  2. palloc,从物理内存池中一个个猎取可用的物理内存页
  3. page_table_add,经由过程上面的假造地点和物理地点,竖立页表

 

下面我们把每一个症结部份拿出来解说,并附上症结代码。

初始化内存池

内存池是完成请求内存函数的基本,主要目标就是治理一段内存,申明哪块内存被占用了,哪块内存是余暇的。治理这些内存占用状况的数据构造,用的是 bitmap,每一个比特对应着一块 4K 的内存。

内存池一共分为四个,内核的物理地点内存池、用户的物理地点内存池、内核的假造地点内存池、用户的假造地点内存池。

治理物理地点的内存池的构造为 pool,两个内存池变量为 kernel_pooluser_pool

struct pool {
    struct bitmap pool_bitmap;
    uint32_t phy_addr_start; //本内存池治理的物理内存肇端
    uint32_t pool_size;
};

治理假造地点的内存池的构造为 virtual_addr,两个内存池变量本章我们只完成了一个 kernel_vaddr

struct virtual_addr {
    struct bitmap vaddr_bitmap;
    uint32_t vaddr_start; //本内存池治理的假造内存肇端
};

两个构造只是物理内存池构造比假造内存池构造多了一个 pool_size,因为物理地点是有限的,而假造地点可以相对来讲是无穷的。

mem_pool_init 函数就是将这两个构造的三个内存池变量赋好值,代码一览无余,各个值就是上述内存图中所表现的,就不睁开叙说了。

请求内存函数 get_kernel_pages 完成

该函数先是从假造内存池中猎取指定页数的一连内存(vaddr_get),猎取到以后,再循环挪用从物理内存池中猎取一页一页的物理内存(palloc),每猎取到一个物理内存,就将假造内存与物理内存的映照关联到场到页表(page_table_add)。

先看 vaddr_get 函数

 1 static void* vaddr_get(enum pool_flags pf, uint32_t pg_cnt) {
 2     int vaddr_start = 0, bit_idx_start = -1;
 3     uint32_t cnt = 0;
 4     if (pf == PF_KERNEL) {
 5         bit_idx_start = bitmap_scan(&kernel_vaddr.vaddr_bitmap, pg_cnt);
 6         if (bit_idx_start == -1) {
 7             return NULL;
 8         }
 9         while(cnt < pg_cnt) {
10             bitmap_set(&kernel_vaddr.vaddr_bitmap, bit_idx_start + cnt++, 1);
11         }
12         vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE;
13     } else {
14         // 用户内存池,未来再说
15     }
16     return (void*)vaddr_start;
17 }

该函数假如不斟酌 bitmap 底层完成,则异常轻易明白,就是应用 bitmap 的数据构造, 挪用 bitmap_scan 搜刮出一片一连的内存,再挪用 bitmap_set 将请求到的内存位图部份设置为 1(已用),末了经由过程公式

vaddr_start = kernel_vaddr.vaddr_start + bit_idx_start * PG_SIZE;

得出 所取得的假造地点的肇端的假造内存地点(好绕哈哈)

再看 palloc 函数

 1  //在m_pool指向的物理内存池中分派1个物理页
 2  static void* palloc(struct pool* m_pool) {
 3      //找到一个物理页
 4      int bit_idx = bitmap_scan(&m_pool->pool_bitmap, 1);
 5      if (bit_idx == -1) {
 6          return NULL;
 7      }
 8      // 将此位置1将
 9      bitmap_set(&m_pool->pool_bitmap, bit_idx, 1);
10      uint32_t page_phyaddr = ((bit_idx * PG_SIZE) + m_pool->phy_addr_start);
11      return (void*)page_phyaddr;
12  }

不多说了,跟上面的函数现实上是如出一辙的,只不过是取得一个物理页而不是多个,终究返回了 所取得的物理地点的肇端的物理地点

末了看 page_table_add 函数

 1 // 页表中增加假造地点_vaddr与物理地点_page_phyaddr的映照
 2 static void page_table_add(void* _vaddr, void* _page_phyaddr) {
 3     uint32_t vaddr = (uint32_t)_vaddr;
 4     uint32_t page_phyaddr = (uint32_t)_page_phyaddr;
 5     uint32_t* pde = pde_ptr(vaddr);
 6     uint32_t* pte = pte_ptr(vaddr);
 7     
 8     // 推断页目次项的p位,为1示意该表已存在
 9     if (*pde & 0x00000001) {
10         if(!(*pte & 0x00000001)) {
11             *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); 12         } else {
13             // pte repeat
14         }
15     } else {
16         // 页目次项不存在,先竖立页目次项,再竖立页表项
17         uint32_t pde_phyaddr = (uint32_t)palloc(&kernel_pool);
18         *pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1); 19         memset((void*)((int)pte & 0xfffff000), 0, PG_SIZE);
20         *pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1); 21     }
22 }

该函数也很好明白,前面两个函数已取得了一个个的假造内存,而且也取得了一个个的物理内存,这两个值作为入参进入本函数,终究竖立了一个个的页目次项(假如没有),和一个个的页表项

简朴说最主要的就是背面画黄线的两条赋值语句

*pde = (pde_phyaddr | PG_US_U | PG_RW_W | PG_P_1);
*pte = (page_phyaddr | PG_US_U | PG_RW_W | PG_P_1);

终究我们的 main 函数里是请求了 3 页的内存空间,所以 page_table_add 这个函数也会被挪用三次,我把这三次的症结值都打了出来

vaddr page_phyaddr 竖立页目次项 *pde *pte pde_value pte_value
第一次 C0100000 200000 0xFFFFFC00 0xFFF00400 已有页目次项,无需 200007
第二次 C0101000 201000 0xFFFFFC00 0xFFF00404 已有页目次项,无需 201007
第三次 C0102000 202000 0xFFFFFC00 0xFFF00408 已有页目次项,无需 202007

拿第一次举例,本函数就是要将假造地点 C0100000 和物理地点 200000 经由过程页表竖立关联,经由过程页表竖立关联要算出四个值

  1. 须要赋值的页目次项地点 *pde
  2. 须要给该页目次项赋的现实值 pde_value
  3. 须要赋值的页表项地点 *pte
  4. 须要给改页表项赋的现实值 pte_value

个中 2 和 4 的值好说,因为已存在页目次项,所以页目次项赋值这一步就省略了。然后页表项赋的值,就是终究要映照的物理地点的值的高 20 位以及须要的属性,也就是 200007。关于这块有疑问的,可以回忆一下,在这里我只把症结的页表图贴出来。

页目次项和页表项构造

 

 我们已赋值的页目次表和页表

 

假造地点到物理地点的转换

 

关于 1 和 3,也就是须要赋值的页目次项和页表项的地点,我以为是不太好读懂的代码

 1 // 取得假造地点vaddr对应的pte指针
 2 uint32_t* pte_ptr(uint32_t vaddr) {
 3     uint32_t* pte = (uint32_t*)(0xffc00000 + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4);
 4     return pte;
 5 }
 6 
 7 // 取得假造地点vaddr对应的pde指针
 8 uint32_t* pde_ptr(uint32_t vaddr) {
 9     uint32_t* pde = (uint32_t*)((0xfffff000) + PDE_IDX(vaddr) * 4);
10     return pde;
11 }

但我们先倒推一下照样很好明白的,照样拿第一次的数据举例,页目次项地点 *pde = 0xFFFFFC00,页表项地点 *pte = 0xFFF00400。起首你要明白的是,这是假造地点,经由过程我们之前总结出的页表映照关联

0x00000000-0x000fffff -> 0x000000000000-0x0000000fffff
0xc0000000-0xc00fffff -> 0x000000000000-0x0000000fffff
0xffc00000-0xffc00fff -> 0x000000101000-0x000000101fff
0xfff00000-0xffffefff -> 0x000000101000-0x0000001fffff
0xfffff000-0xffffffff -> 0x000000100000-0x000000100fff

可以得出它们对应的物理地点离别是 *pde = 0x100C00*pte = 0x101400。再把第二次和第三次都算出来,在页表图中的表现就是:

在已存在的页目次项 0x100C00 中,增加三个页表项,离别指向须要映照的物理地点。以下!

 

从效果上看,觉得恰是我们所须要的,在原有页表基本上,往下找位置插进去罢了。

插进去好新页表项后,页表映照关联变成了下面如许,赤色为新增。很好明白,因为第 0 个和第 768 个页目次项都对应着第一个页表,我们在第一个页表中增加了三个(一连的就被合并成一个映照关联展现了)页表项目,所以天然就多了两处地点映照关联

0x00000000-0x000fffff -> 0x000000000000-0x0000000fffff
0x00100000-0x00102fff -> 0x000000200000-0x000000202fff
0xc0000000-0xc00fffff -> 0x000000000000-0x0000000fffff
0xc0100000-0xc0102fff -> 0x000000200000-0x000000202fff
0xffc00000-0xffc00fff -> 0x000000101000-0x000000101fff
0xfff00000-0xffffefff -> 0x000000101000-0x0000001fffff
0xfffff000-0xffffffff -> 0x000000100000-0x000000100fff

倒推以后,再来品一品这个代码,这也处理了我们之前所说的,怎样经由过程一个假造地点,找到它地点的页目次表和页表。思绪是,我们起首可以经由过程这个 vaddr,能推出页目次项和页表项的物理地点。拿页目次项的物理地点来讲,我们须要拼凑出一个页目次项的假造地点,让其可以接见到此页目次项的物理地点,触及到了一些新鲜的技能。我这里不想睁开说这段代码了,只需晓得就好,想起来真的很烧脑。

 1 // 取得假造地点vaddr对应的pte指针  2 uint32_t* pte_ptr(uint32_t vaddr) {  3 uint32_t* pte = (uint32_t*)(0xffc00000 + ((vaddr & 0xffc00000) >> 10) + PTE_IDX(vaddr) * 4);  4 return pte;  5 }  6  7 // 取得假造地点vaddr对应的pde指针  8 uint32_t* pde_ptr(uint32_t vaddr) {  9 uint32_t* pde = (uint32_t*)((0xfffff000) + PDE_IDX(vaddr) * 4); 10 return pde; 11 }

 

四、运转

我们看到,我们胜利挪用函数,猎取了 3 个内核的内存页,肇端地点为 0xC0100000

 

写在末了:开源项目和课程计划

假如你对克己一个操作体系感兴致,无妨追随这个系列课程看下去,以至到场我们,一起来开发。

参考书本

《操作体系原形复原》这本书真的赞!强烈推荐

项目开源

项目开源地点:

当你看到该文章时,代码大概已比文章中的又多写了一些部份了。你可以经由过程提交纪录汗青来检察汗青的代码,我会逐步梳理提交汗青以及项目申明文档,争取给每一课都预备一个可实行的代码。固然文章中的代码也是全的,采纳复制粘贴的体式格局也是完全可以的。

假如你有兴致到场这个克己操作体系的雄师,也可以在留言区留下您的联系体式格局,或许在 gitee 私信我您的联系体式格局。

课程计划

本课程打算出系列课程,我写到哪以为可以写成一篇文章了就写出来分享给人人,终究会完成一个功用周全的操作体系,我以为这是最好的进修操作体系的体式格局了。所以中心碰到的种种坎也会写进去,假如你能延续跟进,随着我一块写,必定会有很好的收货。纵然没有,交个朋侪也是好的哈哈。

现在的系列包含

linux入门系列14--ssh服务及主机远程管理

参与评论