XV6 source code reader - Virtual Memory Management

Exercise1 source code reader

1. memory management: kalloc.c vm.c and other documents related to the code

  • kalloc.c: char * kalloc (void) is responsible for the user space, the kernel stack, page tables and page buffer allocation of physical memory when needed, the virtual address into a physical address is returned, the physical page size is 4k. void kfree (char * v) receives a virtual address, to find the corresponding physical address to be released. Use xv6 front portion of the free memory as a pointer field to point to the next free memory, memory management is the physical page (4K) is allocated in units. In each physical page of memory free space, there is a pointer field (virtual addresses) point to a free page, and finally a free page is NULL, in this way, only holds freelist kmem address on virtual address space i.e. can;
// 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;
  • xv6 so that each process has its own page table structure, when switching process is always necessary to switch the page table, switchkvm set cr3 register is kpgdir first address, kpgdir used only scheduler kernel thread. Page tables and kernel stack of each process is unique, xv6 use proc structure will unify them, when the process of switching, they also tend to be switched with the switching process, the kernel simulate a kernel thread, it exclusive kernel stack and kernel page table kpgdir, it is the basis for all process scheduling. switchuvm by passing proc structure responsible for switching processes related to unique data structure, which includes TSS related operations, and then the process specific page tables loaded cr3 register, complete the setup process associated virtual address space environment;
// 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();
}
  • Process's page table often needs to be initialized before use, which must include the mapping of the kernel code, this process eliminates the need to switch again when the page table into the kernel, the process virtual address space of the low part of the address, high address portion reserved for the kernel, The main interfaces:
    • pde_t * setupkvm (void) dispensed through kalloc memory as a page directory, then in accordance with the texture mapping data kmap kernel virtual address space to physical address space, the utility function call mappages period;
    • int allocuvm (pde_t * pgdir, uint oldsz, uint newsz) allocated virtual addresses provided to the page table while oldsz newsz of memory units of pages;
    • int deallocuvm (pde_t * pgdir, uint oldsz, uint newsz) will newsz oldsz corresponding to the virtual address space into an idle memory;
    • int loaduvm (pde_t * pgdir, char * addr, struct inode * ip, uint offset, uint sz) the i-node system reads the contents of the file loaded onto the corresponding address, the user interface process by allocuvm allocate memory and setting page table, and then call loaduvm interface program on the file system is loaded into memory, it can provide an interface for the exec system call to prepare for the formal operation of user processes;
    • When the process requires the destruction of recovered memory, call the void freevm (pde_t * pgdir) Clear user process memory-related environment, the first call will be recovered 0 to KERNBASE virtual address space, and then destroy the whole process's page table;
    • pde_t * copyuvm (pde_t * pgdir, uint sz) is responsible for copying a new page tables and allocate new memory, new and old memory layout is exactly the same, xv6 use this function as a fork () the underlying implementation.

Exercise2 problems with reading

When after 1.XV6 initialized to perform main.c, memory layout is like (which has what)?

  • Kernel code exist in the physical address 0x100000 at low address, the page table array entrypgdir main.c file, wherein the virtual address low 4M 4M low physical address mapping, the virtual address [KERNBASE, KERNBASE + 4MB) mapped to the physical address [0 , 4MB);

  • Then the end of the call to the kernel initialization kinit1 4M physical memory space of the physical memory is not used, then call kinit2 to initialize the kernel space remaining unused PHYSTOP. kinit1 called before the first page or use the table (that is, that the above memory layout), can only initialize 4M, and because of also using the page table translation mechanism when the latter re-build a new page table to find the actual physical storage page table memory space, which constitutes a bootstrap problem, xv6 by the main function at the beginning of the end of the kernel release to 4Mb of space to allocate page tables, as at the beginning of multi-core CPU has not started, so do not set the lock mechanism. After kinit2 in a kernel build a new page table, full access to the kernel virtual address space, so here we initialize all physical memory, and began locking mechanism for the protection of free memory linked list;
  • Then main kernel initialization function to achieve new page table by calling void kvmalloc (void) function;
  • Finally, address space and memory layout is as follows: at the end of the kernel memory space of the physical address to the physical address PHYSTOP unused, the virtual address space mapped to the corresponding KERNBASE more portions of the physical memory location of a lower address.

// 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
};
……

How 2.XV6 dynamic memory management is done? There is a kmem (list), used to manage the physical memory pages can be allocated. (vend = 0x00400000, which is the maximum that can be allocated memory pages to 4Mb)
See "Exercise 1 source code to read" part, we have made a complete answer.

How 3.XV6 virtual memory is initialized? Draw virtual memory layout diagram XV6, please say what each part of the corresponding content yes. See note on kmap memlayout.h and vm.c of?

  • The main function by calling void kinit1 (void * vstart, void * vend), void kinit2 (void * vstart, void * vend), void kvmalloc (void) initialize the kernel functions to achieve new page table. Conversion interface virtual addresses and physical addresses:
// 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
  • Memory layout:

4.关于XV6 的内存页式管理。发生中断时,用哪个页表? 一个内存页是多大? 页目录有多少项? 页表有多少项? 最大支持多大的内存? 画出从虚拟地址到物理地址的转换图。在XV6中,是如何将虚拟地址与物理地址映射的(调用了哪些函数实现了哪些功能)?

  • 发生中断时,将换入cpu的进程的页表首地址存入cr3寄存器;一个内存页为4k;XV6页表采用的二级目录,一级目录有\(2^{10}\)条,二级目录有\(2^{10} * 2^{10}\)条;页表项为\(2^2\)Bytes,故页表有\(2^{12} / 2^2 = 2^{10} = 1024\)项;最大支持4G内存;

  • 物理内存页的申请与释放,虚拟地址与物理地址如何映射等在“Exercise 1 源代码阅读”都已经详述了,在此主要说下mappages接口,虚拟地址 * va与物理地址 * pa映射size个字节,同时赋予该页的权限perm,如下:
// 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

Guess you like

Origin www.cnblogs.com/icoty23/p/10993861.html