struct { structspinlocklock; int use_lock; structrun *freelist; } kmem;
// Free the page of physical memory pointed at by v, // which normally should have been returned by a // call to kalloc(). (The exception is when // initializing the allocator; see kinit above.) void kfree(char *v) { structrun *r;
if((uint)v % PGSIZE || v < end || V2P(v) >= PHYSTOP) panic("kfree");
// Fill with junk to catch dangling refs. memset(v, 1, PGSIZE);
// Allocate one page table for the machine for the kernel address // space for scheduler processes. void kvmalloc(void) { kpgdir = setupkvm(); switchkvm(); }
页表的内核部分由kmap定义,此处的data和之前的end一样,也是ld
script中定义的。
1 2 3 4 5 6 7 8 9 10 11 12 13
// This table defines the kernel's mappings, which are present in // every process's page table. staticstructkmap { 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 };
// 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 }
// Create PTEs for virtual addresses starting at va that refer to // physical addresses starting at pa. va and size might not // be page-aligned. staticint 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; } return0; }
// Return the address of the PTE in page table pgdir // that corresponds to virtual address va. If alloc!=0, // create any required page table pages. staticpte_t * walkpgdir(pde_t *pgdir, constvoid *va, int alloc) { pde_t *pde; pte_t *pgtab;
pde = &pgdir[PDX(va)]; if(*pde & PTE_P){ pgtab = (pte_t*)P2V(PTE_ADDR(*pde)); } else { if(!alloc || (pgtab = (pte_t*)kalloc()) == 0) return0; // Make sure all those PTE_P bits are zero. memset(pgtab, 0, PGSIZE); // The permissions here are overly generous, but they can // be further restricted by the permissions in the page table // entries, if necessary. *pde = V2P(pgtab) | PTE_P | PTE_W | PTE_U; } return &pgtab[PTX(va)]; }
// Create a new process copying p as the parent. // Sets up stack to return as if from system call. // Caller must set state of returned proc to RUNNABLE. int fork(void) { int i, pid; structproc *np; structproc *curproc = myproc();
// Look in the process table for an UNUSED proc. // If found, change state to EMBRYO and initialize // state required to run in the kernel. // Otherwise return 0. staticstruct proc* allocproc(void) { structproc *p; char *sp;
// Given a parent process's page table, create a copy // of it for a child. pde_t* copyuvm(pde_t *pgdir, uint sz) { pde_t *d; pte_t *pte; uint pa, i, flags; char *mem;
// this assignment to p->state lets other cores // run this process. the acquire forces the above // writes to be visible, and the lock is also needed // because the assignment might not be atomic. acquire(&ptable.lock);
p->state = RUNNABLE;
release(&ptable.lock); }
inituvm()
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// Load the initcode into address 0 of pgdir. // sz must be less than a page. void inituvm(pde_t *pgdir, char *init, uint sz) { char *mem;
if(sz >= PGSIZE) panic("inituvm: more than a page"); mem = kalloc(); memset(mem, 0, PGSIZE); mappages(pgdir, 0, PGSIZE, V2P(mem), PTE_W|PTE_U); memmove(mem, init, sz); }
// Allocate two pages at the next page boundary. // Make the first inaccessible. Use the second as the user stack. sz = PGROUNDUP(sz); if((sz = allocuvm(pgdir, sz, sz + 2*PGSIZE)) == 0) goto bad; clearpteu(pgdir, (char*)(sz - 2*PGSIZE)); sp = sz;
// Switch TSS and h/w page table to correspond to process p. void switchuvm(struct proc *p) { if(p == 0) panic("switchuvm: no process"); if(p->kstack == 0) panic("switchuvm: no kstack"); if(p->pgdir == 0) panic("switchuvm: no pgdir");
pushcli(); mycpu()->gdt[SEG_TSS] = SEG16(STS_T32A, &mycpu()->ts, sizeof(mycpu()->ts)-1, 0); mycpu()->gdt[SEG_TSS].s = 0; mycpu()->ts.ss0 = SEG_KDATA << 3; mycpu()->ts.esp0 = (uint)p->kstack + KSTACKSIZE; // setting IOPL=0 in eflags *and* iomb beyond the tss segment limit // forbids I/O instructions (e.g., inb and outb) from user space mycpu()->ts.iomb = (ushort) 0xFFFF; ltr(SEG_TSS << 3); lcr3(V2P(p->pgdir)); // switch to process's address space popcli(); }