/* Adjust the address for the data segment to the next page */ . = ALIGN(0x1000);
.data : { *(.data) }
PROVIDE(edata = .);
.bss : { *(.bss) }
PROVIDE(end = .);
/* Place debugging symbols so that they can be found by * the kernel debugger. * Specifically, the four words at 0x200000 mark the beginning of * the stabs, the end of the stabs, the beginning of the stabs * string table, and the end of the stabs string table, respectively. */
int copy_range(pde_t *to, pde_t *from, uintptr_t start, uintptr_t end, bool share) { assert(start % PGSIZE == 0 && end % PGSIZE == 0); assert(USER_ACCESS(start, end)); // copy content by page unit. do { //call get_pte to find process A's pte according to the addr start pte_t *ptep = get_pte(from, start, 0), *nptep; if (ptep == NULL) { start = ROUNDDOWN(start + PTSIZE, PTSIZE); continue ; } //call get_pte to find process B's pte according to the addr start. If pte is NULL, just alloc a PT if (*ptep & PTE_P) { if ((nptep = get_pte(to, start, 1)) == NULL) { return -E_NO_MEM; } uint32_t perm = (*ptep & PTE_USER); //get page from ptep structPage *page = pte2page(*ptep); // alloc a page for process B structPage *npage=alloc_page(); assert(page!=NULL); assert(npage!=NULL); int ret=0; /* LAB5:EXERCISE2 YOUR CODE * replicate content of page to npage, build the map of phy addr of nage with the linear addr start * * Some Useful MACROs and DEFINEs, you can use them in below implementation. * MACROs or Functions: * page2kva(struct Page *page): return the kernel vritual addr of memory which page managed (SEE pmm.h) * page_insert: build the map of phy addr of an Page with the linear addr la * memcpy: typical memory copy function * * (1) find src_kvaddr: the kernel virtual address of page * (2) find dst_kvaddr: the kernel virtual address of npage * (3) memory copy from src_kvaddr to dst_kvaddr, size is PGSIZE * (4) build the map of phy addr of nage with the linear addr start */ void *src_kvaddr = page2kva(page); void *dst_kvaddr = page2kva(npage); memcpy(dst_kvaddr, src_kvaddr, PGSIZE); ret = page_insert(to, npage, start, perm); assert(ret == 0); } start += PGSIZE; } while (start != 0 && start < end); return0; }
在 copy_range 中,最终完成了父进程的内存空间拷贝到子进程中的工作,实现思路比较简单。参数的 to 是子进程的页目录表,from 则是父进程的页目录表,start 是空间开始的虚拟地址,end 是空间结束的虚拟地址。在这个函数中,主要就是遍历每一个父进程中的页表项,然后为子进程分配新页,使用 memcpy 函数复制内存,最后插入到子进程的页表中。
如果要实现 Copy On Write 机制,则直接省去拷贝的操作,直接将父进程的页表项插入到子进程中。但是需要将 PTE_W 清零。这样,在两个进程访问到共享页面的时候,则会触发 Page Fault,而处理页面异常的例程发现虽然页表项不可写,但是所在的虚拟内存空间可写的话,就知道这是一次 Copy On Write 操作,到时候再进行复制即可。