1.内存管理部分: kalloc.c vm.c 以及相关其他文件代码
// kalloc.c
// Physical memory allocator, intended to allocate
// memory for user processes, kernel stacks, page table pages,
// and pipe buffers. Allocates 4096-byte pages.
void freerange(void *vstart, void *vend);
extern char end[]; // first address after kernel loaded from ELF file
struct run {
struct run *next;
};
struct {
struct spinlock lock;
int use_lock;
struct run *freelist;
} kmem;
// vm.c
……
// Switch h/w page table register to the kernel-only page table,
// for when no process is running.
void
switchkvm(void)
{
lcr3(v2p(kpgdir)); // switch to the kernel page table
}
// Switch TSS and h/w page table to correspond to process p.
void
switchuvm(struct proc *p)
{
pushcli();
cpu->gdt[SEG_TSS] = SEG16(STS_T32A, &cpu->ts, sizeof(cpu->ts)-1, 0);
cpu->gdt[SEG_TSS].s = 0;
cpu->ts.ss0 = SEG_KDATA << 3;
cpu->ts.esp0 = (uint)proc->kstack + KSTACKSIZE;
ltr(SEG_TSS << 3);
if(p->pgdir == 0)
panic("switchuvm: no pgdir");
lcr3(v2p(p->pgdir)); // switch to new address space
popcli();
}
1.XV6初始化之后到执行main.c时,内存布局是怎样的(其中已有哪些内容)?
内核代码存在于物理地址低地址的0x100000处,页表为main.c文件中的entrypgdir数组,其中虚拟地址低4M映射物理地址低4M,虚拟地址 [KERNBASE, KERNBASE+4MB) 映射到 物理地址[0, 4MB);
最后内存布局和地址空间如下:内核末尾物理地址到物理地址PHYSTOP的内存空间未使用,虚拟地址空间KERNBASE以上部分映射到物理内存低地址相应位置。
// kalloc.c
// Initialization happens in two phases.
// 1. main() calls kinit1() while still using entrypgdir to place just
// the pages mapped by entrypgdir on free list.
// 2. main() calls kinit2() with the rest of the physical pages
// after installing a full page table that maps them on all cores.
void
kinit1(void *vstart, void *vend)
{
initlock(&kmem.lock, "kmem");
kmem.use_lock = 0;
freerange(vstart, vend);
}
void
kinit2(void *vstart, void *vend)
{
freerange(vstart, vend);
kmem.use_lock = 1;
}
// kmap.c
……
// This table defines the kernel's mappings, which are present in
// every process's page table.
static struct kmap {
void *virt;
uint phys_start;
uint phys_end;
int perm;
} kmap[] = {
{ (void*)KERNBASE, 0, EXTMEM, PTE_W}, // I/O space
{ (void*)KERNLINK, V2P(KERNLINK), V2P(data), 0}, // kern text+rodata
{ (void*)data, V2P(data), PHYSTOP, PTE_W}, // kern data+memory
{ (void*)DEVSPACE, DEVSPACE, 0, PTE_W}, // more devices
};
……
2.XV6 的动态内存管理是如何完成的? 有一个kmem(链表),用于管理可分配的物理内存页。(vend=0x00400000,也就是可分配的内存页最大为4Mb)
详见“Exercise 1 源代码阅读”部分,已经作出完整解答。
3.XV6的虚拟内存是如何初始化的? 画出XV6的虚拟内存布局图,请说出每一部分对应的内容是什么。见memlayout.h和vm.c的kmap上的注释?
// memlayout.h
// Memory layout
#define EXTMEM 0x100000 // Start of extended memory
#define PHYSTOP 0xE000000 // Top physical memory
#define DEVSPACE 0xFE000000 // Other devices are at high addresses
// Key addresses for address space layout (see kmap in vm.c for layout)
#define KERNBASE 0x80000000 // First kernel virtual address
#define KERNLINK (KERNBASE+EXTMEM) // Address where kernel is linked
#ifndef __ASSEMBLER__
static inline uint v2p(void *a) { return ((uint) (a)) - KERNBASE; }
static inline void *p2v(uint a) { return (void *) ((a) + KERNBASE); }
#endif
#define V2P(a) (((uint) (a)) - KERNBASE)
#define P2V(a) (((void *) (a)) + KERNBASE)
#define V2P_WO(x) ((x) - KERNBASE) // same as V2P, but without casts
#define P2V_WO(x) ((x) + KERNBASE) // same as V2P, but without casts
4.关于XV6 的内存页式管理。发生中断时,用哪个页表? 一个内存页是多大? 页目录有多少项? 页表有多少项? 最大支持多大的内存? 画出从虚拟地址到物理地址的转换图。在XV6中,是如何将虚拟地址与物理地址映射的(调用了哪些函数实现了哪些功能)?
// vm.c
……
// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa. va and size might not
// be page-aligned.
static int
mappages(pde_t *pgdir, void *va, uint size, uint pa, int perm)
{
char *a, *last;
pte_t *pte;
a = (char*)PGROUNDDOWN((uint)va);
last = (char*)PGROUNDDOWN(((uint)va) + size - 1);
for(;;){
if((pte = walkpgdir(pgdir, a, 1)) == 0)
return -1;
if(*pte & PTE_P)
panic("remap");
*pte = pa | perm | PTE_P;
if(a == last)
break;
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
……
[1] xv6虚拟内存-博客园
[2] xv6 virtual memory-hexo
[3] xv6内存管理-简书
[4] xv6内存管理-CSDN
原文:https://www.cnblogs.com/icoty23/p/10993861.html