diff --git a/kern/fork.c b/kern/fork.c index 6a93772..5165f2b 100644 --- a/kern/fork.c +++ b/kern/fork.c @@ -1,10 +1,99 @@ #include #include #include +#include #include #include #include +#include +#include +#include +#include + +// get the flag of a lin addr in page table +static inline u32 get_flag(uintptr_t laddr, u32 cr3) { + assert(PGOFF(laddr) == 0); + uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3); + assert((pde_ptr[PDX(laddr)] & PTE_P) != 0); + phyaddr_t pte_phy = PTE_ADDR(pde_ptr[PDX(laddr)]); + uintptr_t *pte_ptr = (uintptr_t *)K_PHY2LIN(pte_phy); + assert((pte_ptr[PTX(laddr)] & PTE_P) != 0); + return PTE_FLAG(pte_ptr[PTX(laddr)]); +} + +static phyaddr_t +alloc_phy_page(struct page_node **page_list) +{ + phyaddr_t paddr = phy_malloc_4k(); + + struct page_node *new_node = kmalloc(sizeof(struct page_node)); + new_node->nxt = *page_list; + new_node->paddr = paddr; + new_node->laddr = -1; + *page_list = new_node; + + return paddr; +} + +static phyaddr_t +lin_mapping_phy(u32 cr3, + struct page_node **page_list, + uintptr_t laddr, + u32 pte_flag) +{ + assert(PGOFF(laddr) == 0); + + uintptr_t *pde_ptr = (uintptr_t *)K_PHY2LIN(cr3); + + if ((pde_ptr[PDX(laddr)] & PTE_P) == 0) { + phyaddr_t pte_phy = alloc_phy_page(page_list); + memset((void *)K_PHY2LIN(pte_phy), 0, PGSIZE); + pde_ptr[PDX(laddr)] = pte_phy | PTE_P | PTE_W | PTE_U; + } + + phyaddr_t pte_phy = PTE_ADDR(pde_ptr[PDX(laddr)]); + uintptr_t *pte_ptr = (uintptr_t *)K_PHY2LIN(pte_phy); + + phyaddr_t page_phy; + if ((pte_ptr[PTX(laddr)] & PTE_P) != 0) + warn("fork alloc: this page was mapped before, laddr: %x", laddr); + page_phy = alloc_phy_page(page_list); + (*page_list)->laddr = laddr; + pte_ptr[PTX(laddr)] = page_phy | pte_flag; + return page_phy; +} + +// modified from pmap.c::map_elf +// first alloc a new page list, together with a new cr3(with page directory page) +// second map kern +// third iterate over parent pages, +// for each parent page(except page table pages, child has its own) , map a page in child proc +// then copy content of this page to child +// finally, set child->page_list to this brand new one and set cr3 +// SET: child's page list and cr3 +static inline void copy_parent_pages(PROCESS_0 *child, PROCESS_0 *parent){ + phyaddr_t new_cr3 = phy_malloc_4k(); + memset((void *)K_PHY2LIN(new_cr3), 0, PGSIZE); + struct page_node *new_page_list = kmalloc(sizeof(struct page_node)); + new_page_list->nxt = NULL; + new_page_list->paddr = new_cr3; + new_page_list->laddr = -1; + // maybe redundant, let it be for now + map_kern(new_cr3, &new_page_list); + // iterate over parent pages + for (struct page_node* pa_page = parent->page_list; pa_page != NULL; pa_page = pa_page->nxt) { + // no need to take care of non-physical page, we have our own page table and cr3 + if (pa_page->laddr == -1) continue; + // first alloc a new page for the same laddr, and set flag same as parent + phyaddr_t new_paddr = lin_mapping_phy(new_cr3, &new_page_list, pa_page->laddr, get_flag(pa_page->laddr, parent->cr3)); + // copy parent page to child page using paddr + memcpy((void*)K_PHY2LIN(new_paddr), (const void*)K_PHY2LIN(pa_page->paddr), PGSIZE); + // kprintf("%x -> %x\n", K_PHY2LIN(pa_page->paddr), K_PHY2LIN(new_paddr)); + } + child->page_list = new_page_list; + child->cr3 = new_cr3; +} ssize_t kern_fork(PROCESS_0 *p_fa) @@ -21,23 +110,24 @@ kern_fork(PROCESS_0 *p_fa) while (xchg(&p_fa->lock, 1) == 1) schedule(); // fork的第一步你需要找一个空闲(IDLE)的进程作为你要fork的子进程 - PROCESS_0 *p_child = NULL; + PROCESS *proc_child = NULL; int i = 0; //? maybe cli will be better when preserving a proc resource DISABLE_INT(); for (i = 1; i < PCB_SIZE; ++ i) { if (proc_table[i].pcb.statu == IDLE) { proc_table[i].pcb.statu = INITING; - p_child = &proc_table[i]; + proc_child = &proc_table[i]; break; } } ENABLE_INT(); - if (p_child == NULL) { + if (proc_child == NULL) { // NO hell. no free proc_table found. xchg(&p_fa->lock, 0); return ENOMEM; } + PROCESS_0 *p_child = &proc_child->pcb; // 再之后你需要做的是好好阅读一下pcb的数据结构,搞明白结构体中每个成员的语义 // 别光扫一遍,要搞明白这个成员到底在哪里被用到了,具体是怎么用的 // 可能exec和exit系统调用的代码能够帮助你对pcb的理解,不先理解好pcb你fork是无从下手的 @@ -55,31 +145,65 @@ kern_fork(PROCESS_0 *p_fa) // 这需要你对调度整个过程都掌握比较清楚) //? Start to COPY //? 1. COPY PCB - *p_child = *p_fa; // anyway, first copy all - p_child->cr3 = 0; - p_child->fork_tree.p_fa = NULL; - p_child->fork_tree.sons = NULL; - p_child->page_list = NULL; - // the null before is just a reminder to change them - p_child->pid = i; // well, for simplicity, let pid be index in proc table - p_child->user_regs.eax = 0; //! maybe, set user_ctx.eax as the retval? + // p_fa shares the same base addr as its padding + // anyways, copy the whole proc_table item(8K?) + DISABLE_INT(); // make sure this process is never interrupted + memset(proc_child, 0, sizeof(PROCESS)); // clear child's kernel stack + // the commented fields below will be set later + // p_child->cr3 + p_child->exit_code = p_fa->exit_code; + // p_child->fork_tree + // p_child->kern_regs + p_child->lock = 0; + // p_child->page_list + p_child->pid = i; + p_child->priority = p_fa->priority; + p_child->statu = INITING; //! important!!! if you copied this from parent, haha, waste one hour + p_child->ticks = p_fa->ticks; + p_child->user_regs = p_fa->user_regs; //? 2. ALLOC PAGES AND COPY PHYSICAL MEMORY - //TODO see how to copy the pages from parent - //? 3. SET OTHER CHILD STATUS + copy_parent_pages(p_child, p_fa); + // panic("Unimplement! soul torture1"); + ENABLE_INT(); + //? 3. SET restart point + // //! maybe, kern stack different, use offset relevant to stack base addr to set child's kern esp + // u32 esp_off = p_fa->kern_regs.esp - (u32)p_fa; + // p_child->kern_regs.esp = (u32)p_child + esp_off; + // u32 ebp_off = p_fa->kern_regs.ebp - (u32)p_fa; + // p_child->kern_regs.ebp = (u32)p_child + ebp_off; + // kprintf("%x %x\n", p_child->kern_regs.esp, p_fa->kern_regs.esp); + // kprintf("%x %x\n", p_child->user_regs.kernel_esp, p_fa->user_regs.kernel_esp); + //! kern_ctx is not part of our FSM model of user process, never copy it from parent + //! or 1 another hour will be wasted + // just set it as a new process from scratch, directly jump to restart + p_child->kern_regs.esp = (u32)(proc_child + 1) - 8; + *(u32 *)(p_child->kern_regs.esp + 0) = (u32)restart; + *(u32 *)(p_child->kern_regs.esp + 4) = (u32)p_child; + p_child->user_regs.eax = 0; // eax as the retval // 别忘了维护进程树,将这对父子进程关系添加进去 - panic("Unimplement! maintain process tree"); - + // ? maintain process tree + p_child->fork_tree.p_fa = p_fa; + // malloc a son_node + struct son_node* p_son = (struct son_node*)kmalloc(sizeof(struct son_node)); + p_son->p_son = p_child; + // head insert into parent's son list + p_son->pre = NULL; + p_son->nxt = p_fa->fork_tree.sons; + if (p_fa->fork_tree.sons != NULL) { + p_son->nxt->pre = p_son; + } + p_fa->fork_tree.sons = p_son; // 最后你需要将子进程的状态置为READY,说明fork已经好了,子进程准备就绪了 - panic("Unimplement! change status to READY"); - + p_child->statu = READY; + xchg(&p_fa->lock, 0); // 在你写完fork代码时先别急着运行跑,先要对自己来个灵魂拷问 // 1. 上锁上了吗?所有临界情况都考虑到了吗?(永远要相信有各种奇奇怪怪的并发问题) // 2. 所有错误情况都判断到了吗?错误情况怎么处理?(RTFM->`man 2 fork`) // 3. 你写的代码真的符合fork语义吗? - panic("Unimplement! soul torture"); - - return 0; + // panic("Unimplement! soul torture"); + // ? return to parent proc by the normal syscall return machanism + return p_child->pid; } ssize_t diff --git a/user/testfork.c b/user/testfork.c index 23c2b0c..781bff0 100644 --- a/user/testfork.c +++ b/user/testfork.c @@ -6,7 +6,7 @@ int main() int pid = fork(); if (pid) { while (1) { - printf("I'm fa, son pid = %d", pid); + printf("\x1b[93mI'm fa, son pid = %d\x1b[0m", pid); fflush(); for (int i = 0 ; i < (int)1e8 ; i++) ;//do nothing