lab 2 memory management

lab 2

内存管理的主要几个模块如下:

image-20220419213943718

用户内存空间与内核空间(都是虚拟地址)

image-20220419215128165

0. 实验介绍#

在这个实验中,你将为你的操作系统编写一个内存管理代码。主要分为两大部分:

  • 编写一个物理内存分配器以实现物理内存的分配与回收。
  • 虚拟内存的管理,将内核态与用户态的虚拟地址映射到物理内存的地址。在x86系统中需要硬件提供MMU(Memory Management Unit,内存管理单元)来实现虚拟内存映射。参考:MMU (一) - blackBox - 博客园 (cnblogs.com)

Lab 2 重点关注以下几个文件:

  • inc/memlayout.h 包含了jos中内存管理的各种宏定义
  • kern/pmap.c
  • kern/pmap.h
  • kern/kclock.h
  • kern/kclock.c

1. Part1: Physical Page Management#

操作系统必须trace物理 RAM 的哪些部分是空闲的,哪些部分当前正在使用。 JOS 以页粒度管理 PC 的物理内存,以便它可以使用 MMU 实现地址转换内存保护

Exercise 1

需要实现在 kern/pmap.c的函数有:

boot_alloc()
mem_init() (only up to the call to check_page_free_list(1))
page_init()
page_alloc()
page_free()

1.1 boot_alloc()#

This simple physical memory allocator is used only while JOS is setting up its virtual memory system. page_alloc() is the real allocator.
If n>0, allocates enough pages of contiguous physical memory to hold 'n' bytes. Doesn't initialize the memory. Returns a kernel virtual address.
If n==0, returns the address of the next free page without allocating anything.
If we're out of memory, boot_alloc should panic.
This function may ONLY be used during initialization, before the page_free_list list has been set up.

按照需求来做就行,从描述可知,这是一个初始的内存分配器,一般只在内存初始化的时候会使用一次。在lab1中,我们了解到PC的启动过程,在内核执行的kern/init.c中会调用mem_init,然后在mem_init里面会调用boot_alloc,显然这个函数只在内存初始化的时候分配物理内存空间。

要做这个题,要熟悉几个概念:

  1. end:定义在kern/kernel.ld,是指向内核的.bss段的末尾的一个指针,也就是第一块未使用的虚拟地址。.bss 可以通过objdump -h obj/kern/kernel看出:

    image-20220420100922774
  2. ROUNDUP:宏,正如字面意思,向上取整,定义在inc/types.h中:

    // Rounding operations (efficient when n is a power of 2)
    // Round down to the nearest multiple of n
    #define ROUNDDOWN(a, n)                                         \
    ({                                                              \
            uint32_t __a = (uint32_t) (a);                          \
            (typeof(a)) (__a - __a % (n));                          \
    })
    // Round up to the nearest multiple of n
    #define ROUNDUP(a, n)                                           \
    ({                                                              \
            uint32_t __n = (uint32_t) (n);                          \
            (typeof(a)) (ROUNDDOWN((uint32_t) (a) + __n - 1, __n)); \
    })
    

boot_alloc就是在.bss之上分配制定大小为n的区域。注意这里都是在虚拟内存地址下进行的操作

  • 如果n为0则直接返回nextfree。也就是把.bss向上取整(为了都符合一页一页的存储形式。)
  • 如果不为0则为他分配内存。其实就是把nextfree的地址往后移动 (n * PGSIZE)。返回当前的第一个空闲nextfree
static void *
boot_alloc(uint32_t n)
{
        static char *nextfree;  // virtual address of next byte of free memory
        char *result;

        // Initialize nextfree if this is the first time.
        // 'end' is a magic symbol automatically generated by the linker,
        // which points to the end of the kernel's bss segment:
        // the first virtual address that the linker did *not* assign
        // to any kernel code or global variables.
        if (!nextfree) {
                extern char end[];
                nextfree = ROUNDUP((char *) end, PGSIZE);
        }

        // Allocate a chunk large enough to hold 'n' bytes, then update
        // nextfree.  Make sure nextfree is kept aligned
        // to a multiple of PGSIZE.
        //
        // LAB 2: Your code here.
        if (n == 0) {
			return nextfree;
		} 
		// n > 0
        result = nextfree;
        nextfree = ROUNDUP(result + n, PGSIZE);
        //cprintf("boot_alloc memory at %x, next memory allocate at %x\n", result, nextfree);
        return result;
}

1.2 mem_init()#

正如上面提到的mem_init中会初始调用boot_alloc,按照提示,第一次是初始化页目录(建议了解一下分页存储),第二次就是我们需要补充的代码,分配一个pages用来存储PageInfo这个页面结构,定义如下:

/*
 * Page descriptor structures, mapped at UPAGES.
 * Read/write to the kernel, read-only to user programs.
 *
 * Each struct PageInfo stores metadata for one physical page.
 * Is it NOT the physical page itself, but there is a one-to-one
 * correspondence between physical pages and struct PageInfo's.
 * You can map a struct PageInfo * to the corresponding physical address
 * with page2pa() in kern/pmap.h.
 */
struct PageInfo {
	// Next page on the free list.
  //pp_link 是下一项地址
	struct PageInfo *pp_link;

	// pp_ref is the count of pointers (usually in page table entries)
	// to this page, for pages allocated using page_alloc.
	// Pages allocated at boot time using pmap.c's
	// boot_alloc do not have valid reference count fields.
	//pp_ref 是引用数量,凡是调用page_alloc 分配了该页的进程都会引用
	uint16_t pp_ref;
};

struct PageInfo *pages;		// Physical page state array
size_t npages;			// Amount of physical memory (in pages)

也就是说,PageInfo该结构体就是一个能够与物理内存映射的页面。pages数组就是每一个分页的信息,可以通过函数将其转换成物理地址,换句话说该数组中的每一个元素都映射到一个物理页面。我们这里就将所有物理内存做一个到pages中的映射。

        // create initial page directory.
        kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
        memset(kern_pgdir, 0, PGSIZE);

        // Recursively insert PD in itself as a page table, to form
        // a virtual page table at virtual address UVPT.
        // (For now, you don't have understand the greater purpose of the
        // following line.)

        // Permissions: kernel R, user R
        kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;

     
        // Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
        // The kernel uses this array to keep track of physical pages: for
        // each physical page, there is a corresponding struct PageInfo in this
        // array.  'npages' is the number of physical pages in memory.  Use memset
        // to initialize all fields of each struct PageInfo to 0.
        // Your code goes here:
        pages = (struct PageInfo*) boot_alloc(npages * sizeof(struct PageInfo));
        memset(pages, 0,sizeof(struct PageInfo) * npages);

在做这个题的时候一直有个问题,boot_alloc不是在虚拟内存地址下操作的嘛,怎么这里的npages说是物理内存页的页数,原来在后面用到的boot_map_region会将虚拟地址映射到物理地址,这部分是现在虚拟内存中的内核建立pages。

1.2 page_init()#

按照提示来写:

The example code here marks all physical pages as free. However this is not truly the case. What memory is free?

  1. Mark physical page 0 as in use. This way we preserve the real-mode IDT and BIOS structures in case we ever need them. (Currently we don't, but...) pages[0].pp_ref = 1;
  1. The rest of base memory, [PGSIZE, npages_basemem * PGSIZE) is free.
  2. Then comes the IO hole [IOPHYSMEM, EXTPHYSMEM), which must never be allocated.
  3. Then extended memory [EXTPHYSMEM, ...). Some of it is in use, some is free. Where is the kernel in physical memory? Which pages are already in use for
    page tables and other data structures?
void
page_init(void)
{
        size_t i;
        for (i = 0; i < npages; i++) {
                // 1. mark物理页page 0已用,其中保存了IDT(中断操作符表)和BIOS结构
                // 地址范围0x000~0xFFF
                if(i == 0){
                        pages[i].pp_ref = 1;
                        pages[i].pp_link = NULL;
                }
                // 2. mark剩下的base memory可用
                // 地址范围0x1000 ~ 0xA0000
                else if(i>=1 && i<npages_basemem){
                        pages[i].pp_ref = 0;
                        pages[i].pp_link = page_free_list;
                        page_free_list = &pages[i];
                }
                // 3. 从memlayou.h可以查看IOPHYSMEM == 0x0A0000  EXTPHYSMEM == 0x100000,这>部分包括VGA display等硬件信息
                // 地址范围0xA000 ~ 0x100000
                else if(i>=IOPHYSMEM/PGSIZE && i < EXTPHYSMEM/PGSIZE){
                        pages[i].pp_ref = 1;
                        pages[i].pp_link = NULL;
                }
                // 4.Then extended memory 中用到的内存空间,这段内存用来存放kern
                else if(i >= EXTPHYSMEM/ PGSIZE && i < (PADDR(boot_alloc(0)))/PGSIZE){
                        pages[i].pp_ref = 1;
                        pages[i].pp_link = NULL;
                }
                // 4.Then extended memory 中没有用到的内存空间
                else{
                        pages[i].pp_ref = 0;
                        pages[i].pp_link = page_free_list;
                        page_free_list = &pages[i];
                }

        }
}

下面的图来自于:https://icegrave0391.github.io/2020/03/04/joslab2/

page_init就是将初始化物理内存并将其分成多个内存块,所以上面PADDR(boot_alloc(0)) 是内核结束第一个没被使用到的物理内存空间?

image-20200304125040479

1.3 page_alloc()#

要求:

Allocates a physical page. If (alloc_flags & ALLOC_ZERO), fills the entire returned physical page with '\0' bytes. Does NOT increment the reference count of the page - the caller must do these if necessary (either explicitly or via page_insert).

Be sure to set the pp_link field of the allocated page to NULL so page_free can check for double-free bugs. Returns NULL if out of free memory.

Hint: use page2kva and memset

在page_init() 物理内存页已经初始化了,page_free_list 通过前插法已经形成了一个双向链表类似如下:

image-20220420124931257

还需要知道page2kva()的含义,它是将pageinfo 对应的物理地址转为虚拟地址。

struct PageInfo *
page_alloc(int alloc_flags)
{
        // Fill this function in
        struct PageInfo *allocated_page = page_free_list;
        if(allocated_page == NULL){
                cprintf("page_alloc: out of free memory\n");
                return NULL;
        }
        //这里page_free_list 会自从往后指?
        //因为前面init 已经链表链好了
        page_free_list = page_free_list->pp_link;
        allocated_page->pp_link = NULL;
        if(alloc_flags & ALLOC_ZERO){
                memset(page2kva(allocated_page), 0,PGSIZE);
        }
        return allocated_page;
}

1.4 page_free()#

要求:

Return a page to the free list.
(This function should only be called when pp->pp_ref reaches 0.)

Hint: You may want to panic if pp->pp_ref is nonzero or
pp->pp_link is not NULL.

基于对链表这种数据结构的理解,这两题都很简单。

void
page_free(struct PageInfo *pp)
{
        // Fill this function in
        // Hint: You may want to panic if pp->pp_ref is nonzero or
        // pp->pp_link is not NULL.
        if(pp->pp_ref != 0 || pp->pp_link != NULL){
                panic("ARE YOU SERIOUS? Double check failed when dealloc page");
        }
        pp->pp_link = page_free_list;
        page_free_list = pp;
}

总结:Part 1 实现了一个物理页面分配器,boot_alloc(n)在内核启动时首先分配了n个byte的整数个PGSIZE的空间,page_init( )初始化页面,标记了哪些页表可以用哪些不能用,并将空闲的内存块用双向链表链接起来,page_alloc用来分配那些空闲的内存块,page_free用来回收或者称作释放那些已被分配的内存块。引用了PageInfo结构体,用来对应一个物理块(物理页面)。

在lab1中,手写了一个程序/kern/entrygdir.c用于映射虚拟地址空间的地址范围:0xf0000000 - 0xf0400000 和0x00000000 - 0x00400000到物理地址范围:0x00000000 - 0x00400000上面。所以boot_alloc虽然是在虚拟内存地址开辟整数个PGSIZE的空间,但实际上,在物理内存上也会开辟整数个PGSIZE的空间。暂时是这样理解。。

2 Part 2 Virtual Memory#

做这一部分前,需要了解x86保护模式内存管理:即分段与分页存储管理。

中间一大段巴拉巴拉。。可以参考别的文章翻译,了解一下。

Exercise 4

这一部分要求我们实现页表管理:插入和删除线性到物理的映射,在需要的时候创建页表。这个是JOS所用的32位虚拟地址的分布:

image-20220420153644758

下面就是虚拟地址的翻译过程。这个学过os的应该非常熟悉了吧。这里的page_dirpage_table其实就是一个二级页表。的多级索引非常简单。

整体过程就是我们先通过CR3寄存器找到PAGE_DIR所在的位置,然后通过虚拟地址的前10位在PAGE_DIR中获取到下一级页表,也就是PAGE_TABLE的地址。随后通过虚拟地址的12-21这10位去找到对应的PAGE_FRAME的地址。从里面获取到ppa的地址结合OFFSET就可以得到最终的物理地址了。

image-20220420153702121

2.1 pgdir_walk()#

要求:

Given 'pgdir', a pointer to a page directory, pgdir_walk returns a pointer to the page table entry (PTE) for linear address 'va'. This requires walking the two-level page table structure.
The relevant page table page might not exist yet. If this is true, and create == false, then pgdir_walk returns NULL. Otherwise, pgdir_walk allocates a new page table page with page_alloc.

  • If the allocation fails, pgdir_walk returns NULL.

  • Otherwise, the new page's reference count is incremented, the page is cleared, and pgdir_walk returns a pointer into the new page table page.

    Hint 1: you can turn a PageInfo * into the physical address of the page it refers to with page2pa() from kern/pmap.h.
    Hint 2: the x86 MMU checks permission bits in both the page directory and the page table, so it's safe to leave permissions in the page directory more permissive than strictly necessary.
    Hint 3: look at inc/mmu.h for useful macros that manipulate page table and page directory entries.

提示三中inc/mmu.h 定义了许多可以调用的宏

这段注释注意如下几个宏的使用:

PDX:一个虚拟地址的页目录索引,也是地址的前10位。

PTX:一个虚拟地址的页表索引,也是地址的中间10位。

PGOFF:一个虚拟地址的页偏移,也是地址的后12位。


// A linear address 'la' has a three-part structure as follows:
//
// +--------10------+-------10-------+---------12----------+
// | Page Directory |   Page Table   | Offset within Page  |
// |      Index     |      Index     |                     |
// +----------------+----------------+---------------------+
//  \--- PDX(la) --/ \--- PTX(la) --/ \---- PGOFF(la) ----/
//  \---------- PGNUM(la) ----------/

这一段就是表示页目录以及页表索引的各种状态。(存在,可写,用户,不允许缓存,可达,脏读,页面大小,是否全局等)

// Page table/directory entry flags.
#define PTE_P           0x001   // Present
#define PTE_W           0x002   // Writeable
#define PTE_U           0x004   // User
#define PTE_PWT         0x008   // Write-Through
#define PTE_PCD         0x010   // Cache-Disable
#define PTE_A           0x020   // Accessed
#define PTE_D           0x040   // Dirty
#define PTE_PS          0x080   // Page Size
#define PTE_G           0x100   // Global

按照注释中所写,整个程序的步骤如下:

当页目录索引内不存在 va 对应的表项时,即虚拟地址没有对应的物理地址,需要根据create判断是否要为其分配一个物理页面用作二级页表,create为1即分配一个物理页面做页表,这里需要设置权限,由于一级页表和二级页表都有权限控制,所以一般的做法是,放宽一级页表的权限,主要由二级页表来控制权限,在提示2中写道,要注意去掉页目录索引(PDX)和页表索引(PTX)的权限位以保证安全操作。

当页目录索引内存在 va 对应的表项时,即虚拟地址有对应的物理地址,该页面的指针数量应该加一,页面清空,并返回一个pte的指针

pte_t *
pgdir_walk(pde_t *pgdir, const void *va, int create)
{
	pde_t * dir_entry = pgdir + PDX(va);
	uintptr_t page = PTX(va);
	uintptr_t offset = PGOFF(va);
	if(!(*dir_entry & PTE_P)){
		if(create){
			struct PageInfo* newPage = page_alloc(true);
			if(newPage == NULL){
				return NULL;
			}
			(newPage->pp_ref)++;
			//不太懂这段 为什么要把页的物理地址存到dir_entry里面
			*dir_entry = (pde_t)page2pa(newPage) | PTE_P | PTE_U | PTE_W;
			return (pte_t*)page2kva(newPage) + PTX(va);
		}
		return NULL;
	}
	return (pte_t*)KADDR(PTE_ADDR(*dir_entry)) + PTX(va);
}

2.2 boot_map_region()#

要求:

Map [va, va+size) of virtual address space to physical [pa, pa+size) in the page table rooted at pgdir. Size is a multiple of PGSIZE, and va and pa are both page-aligned.

Use permission bits perm|PTE_P for the entries.

This function is only intended to set up the ``static'' mappings above UTOP. As such, it should not change the pp_ref field on the mapped pages.

Hint: the TA solution uses pgdir_walk

将虚拟地址[va, va+size)映射到物理地址[pa, pa+size],使用权限位为perm|PTE_P,只是在UTOP上方做静态映射,所以不能修改pp_ref字段。提示使用pgdir_walk,那就比较简单了,也就是修改上一个函数返回的pte里面的内容与权限。

static void
boot_map_region(pde_t *pgdir, uintptr_t va, size_t size, physaddr_t pa, int perm)
{
	uintptr_t start;
	for(start = 0; start < size;  va += PGSIZE, pa += PGSIEZ, start += PGSIZE ){
		pte_t * pte = pgdir_walk(pgdir, (void *)va, 1);
		*pte = pa | perm | PTE_P;
	}
}

2.3 page_lookup()#

要求:

Return the page mapped at virtual address 'va'. If pte_store is not zero, then we store in it the address of the pte for this page. This is used by page_remove and can be used to verify page permissions for syscall arguments, but should not be used by most callers.

Return NULL if there is no page mapped at va.

Hint: the TA solution uses pgdir_walk and pa2page.

返回一个虚拟地址va映射的page页面。如果pte_store不是0,那么将对应的页表项地址存到pte_store的地址里(用于结果返回)。如果没有页面映射在va那么返回NULL。

struct PageInfo *
page_lookup(pde_t *pgdir, void *va, pte_t **pte_store)
{
	pte_t* pte = pgdir_walk(pgdir, (void*)va, 0);
	if(!pte || !(*pte & PTE_P)){
		return NULL;
	}
	if(pte_store){
		*pte_store = pte; 
	}
	return pa2page(PTE_ADR(*pte));
}

定义在inc/mmu.h中的宏PTE_ADDR

// Address in page table or page directory entry
#define PTE_ADDR(pte)   ((physaddr_t) (pte) & ~0xFFF)

这个宏如何理解???

2.4 page_remove()#

要求:

Unmaps the physical page at virtual address 'va'.

If there is no physical page at that address, silently does nothing.

Details:

  • The ref count on the physical page should decrement.
  • The physical page should be freed if the refcount reaches 0.
  • The pg table entry corresponding to 'va' should be set to 0.
    (if such a PTE exists)
  • The TLB must be invalidated if you remove an entry from
    the page table.

Hint: The TA solution is implemented using page_lookup, tlb_invalidate, and page_decref.

取消映射虚拟地址“va”处的物理页面。

void
page_remove(pde_t *pgdir, void *va)
{
    //这句没搞懂?
    pte_t *pte;
	struct PageInfo *un_page = page_lookup(pgdir, va, &pte);
    if(un_page){
        return;
    }
    //这句是什么?
    *pte = 0;
    tlb_invalidate(pgdir,va);
	page_decref(page);  
}

2.5 page_insert()#

要求:

Map the physical page 'pp' at virtual address 'va'.

The permissions (the low 12 bits) of the page table entry should be set to 'perm|PTE_P'.

Requirements

  • If there is already a page mapped at 'va', it should be page_remove()d.

    • If necessary, on demand, a page table should be allocated and inserted into 'pgdir'.
  • pp->pp_ref should be incremented if the insertion succeeds.

  • The TLB must be invalidated if a page was formerly present at 'va'.

Corner-case hint: Make sure to consider what happens when the same pp is re-inserted at the same virtual address in the same pgdir.

However, try not to distinguish this case in your code, as this frequently leads to subtle bugs; there's an elegant way to handle everything in one code path.

RETURNS:

​ 0 on success
​ -E_NO_MEM, if page table couldn't be allocated

Hint: The TA solution is implemented using pgdir_walk, page_remove, and page2pa.

把物理页pp映射在虚拟地址va,页表项权限设置为perm|PTE_P。

  1. 如果存在物理页面对应的va,应将其删除;
  2. 如有必要,应按需分配页表并将其插入“ pgdir”。插入成功pp->ref应该自增。如果以前有页面位于“ va”,则TLB必须无效。这一步其实就是pgdir_walk做的,将其create设置为1就能将分配的页表插入“pgdir”。
  3. 使TLB无效其实是page_remove做的。
int
page_insert(pde_t *pgdir, struct PageInfo *pp, void *va, int perm)
{
	pte_t* pte = pgdir_walk(pgdir, (void*)va, 1);
	if(!pte){
		reutrn -E_NO_MEM;
	}
	pp->pp_ref++;
    if(*pte & PTE_P){
    	page_remove(pgdir, va);
    }
    *pte = page2pa(pp) | perm | PTE_P;
    return 0;
	
}

3. Part3 : Kernel Address Space#

Exercise 5.

补齐 mem_init() 中的函数。

    // Make 'envs' point to an array of size 'NENV' of 'struct Env'.
    // LAB 3: Your code here.
    envs = (struct Env*) boot_alloc(NENV * sizeof(struct Env));
    memset(envs, 0, NENV * sizeof(struct Env));
    
    boot_map_region(kern_pgdir, UPAGES, PTSIZE, PADDR(envs), PTE_U);
    
    boot_map_region(kern_pgdir, KSTACKTOP - KSTKSIZE, KSTKSIZE, PADDR(bootstack), PTE_W);
    
    boot_map_region(kern_pgdir, KERNBASE, 0xffffffff - KERNBASE, 0, PTE_W);

3.2 Question#

Entry Base Virtual Address Points to (logically):
1023 0xff000000 Page table for top 4MB of phys memory
1022 ? ?
959 0xefc00000 cpu0's kernel stack(0xefff8000),cpu1's kernel stack(0xeffe8000)
956 0xef000000 npages of PageInfo(0xef000000)
952 0xee000000 bootstack
2 0x00800000 Program Data & Heap
1 0x00400000 Empty
0 0x00000000 [see next question]
  1. What entries (rows) in the page directory have been filled in at this point? What addresses do they map and where do they point? In other words, fill out this table as much as possible:

    第一个问题就是在找页面对应的虚拟地址以及映射的物理地址范围的对应。那么就不得不再提一遍说过的刚才的虚拟地址的结构,前10位是页面,所以一共有1024个目录项,每一个目录项的大小是2^10 个页,最后每个页面有2^12个条目。一个页面能够映射4MB的物理内存(PTSIZE = 4MB)。

  2. We have placed the kernel and user environment in the same address space. Why will user programs not be able to read or write the kernel's memory? What specific mechanisms protect the kernel memory?

    由于页表可以设置权限位,如果没有将 PTE_U 置 0 则用户无权限读写

  3. What is the maximum amount of physical memory that this operating system can support? Why?

    所有的空闲的物理页面一开始都储存在了pages这样一个数组中,这个数组存储的基本单元是PageInfo,那么一个PageInfo的大小是sizeof(struct PageInfo))=8 Byte,也就是说最多512K个页面,每个页面容量 2 ^ 12 = 4K。所以512K*4K=2GB。

  4. How much space overhead is there for managing memory, if we actually had the maximum amount of physical memory? How is this overhead broken down?

    刚才通过上一问,所有的 PageInfo 结构组成的数组的大小是 4MB,最多有 512 K 个页面,那么就有 0.5M 个页面,一条地址信息32位,也就是4B,那么就是页面信息要用4B*0.5M=2MB 记录,目录项本身4K,所以要6MB+4KB。

  5. Revisit the page table setup in kern/entry.S and kern/entrypgdir.c. Immediately after we turn on paging, EIP is still a low number (a little over 1MB). At what point do we transition to running at an EIP above KERNBASE? What makes it possible for us to continue executing at a low EIP between when we enable paging and when we begin running at an EIP above KERNBASE? Why is this transition necessary?

    语句jmp *%eax即转到 eax 所存的地址执行,在这里完成了跳转。relocated 部分代码主要设置了栈指针以及调用 kern/init.c。由于在 kern/entrypgdir.c 中将 0~4MB 和 KERNBASE ~ KERNBASE + 4 MB 的虚拟地址都映射到了 0~4MB 的物理地址上,因此无论 EIP 在高位和低位都能执行。必需这么做是因为如果只映射高位地址,那么在开启分页机制的下一条语句就会crash。

3.3 Challenge#

//TODO........

参考:(38条消息) 《MIT JOS Lab2: Memory Management》实验报告_雪原学长的博客-CSDN博客

posted @   tios  阅读(56)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示
主题色彩