/***************************************************** * fork.c //add by visual 2016.5.25 *系统调用fork()功能实现部分sys_fork() ********************************************************/ #include "type.h" #include "const.h" #include "protect.h" #include "string.h" #include "proc.h" #include "global.h" #include "proto.h" #include "stdio.h" static int fork_mem_cpy(u32 ppid, u32 pid); static int fork_pcb_cpy(PROCESS *p_child); static int fork_update_info(PROCESS *p_child); /********************************************************** * sys_fork //add by visual 2016.5.25 *系统调用sys_fork的具体实现部分 *************************************************************/ int sys_fork() { PROCESS *p_child; char *p_reg; // point to a register in the new kernel stack, added by xw, 17/12/11 /*****************申请空白PCB表**********************/ p_child = alloc_PCB(); if (0 == p_child) { kprintf("\x1b[31;47mPCB NULL,fork faild!\x1b[m"); return -1; } else { /****************初始化子进程高端地址页表(内核部分)***********************/ // 这个页表可以复制父进程的! init_page_pte(p_child->task.pid); // 这里面已经填写了该进程的cr3寄存器变量 /************复制父进程的PCB部分内容(保留了自己的标识信息)**************/ fork_pcb_cpy(p_child); /**************复制线性内存,包括堆、栈、代码数据等等***********************/ fork_mem_cpy(p_proc_current->task.pid, p_child->task.pid); /**************更新进程树标识info信息************************/ fork_update_info(p_child); /************修改子进程的名字***************/ strcpy(p_child->task.p_name, "fork"); // 所有的子进程都叫fork /*************子进程返回值在其eax寄存器***************/ p_child->task.regs.eax = 0; // return child with 0 p_reg = (char *)(p_child + 1); // added by xw, 17/12/11 *((u32 *)(p_reg + EAXREG - P_STACKTOP)) = p_child->task.regs.eax; // added by xw, 17/12/11 /****************用户进程数+1****************************/ u_proc_sum += 1; // kprintf("\x1b[32mfork success %s \x1b[0m\n", p_proc_current->task.p_name); // anything child need is prepared now, set its state to ready. added by xw, 17/12/11 p_child->task.stat = READY; } return p_child->task.pid; } /********************************************************** * fork_mem_cpy //add by visual 2016.5.24 *复制父进程的一系列内存数据 *************************************************************/ static int fork_mem_cpy(u32 ppid, u32 pid) { u32 addr_lin; // 复制代码,代码是共享的,直接将物理地址挂载在子进程的页表上 for (addr_lin = p_proc_current->task.memmap.text_lin_base; addr_lin < p_proc_current->task.memmap.text_lin_limit; addr_lin += num_4K) { // lin_mapping_phy(addr_lin, // 线性地址 // get_page_phy_addr(ppid, addr_lin), // 物理地址,为MAX_UNSIGNED_INT时,由该函数自动分配物理内存 // pid, // 要挂载的进程的pid,子进程的pid // PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 // PG_P | PG_USU | PG_RWR); // 页表属性,代码是只读的 lin_mapping_phy(SharePageBase, 0, ppid, PG_P | PG_USU | PG_RWW, 0); // 使用前必须清除这个物理页映射 lin_mapping_phy(SharePageBase, MAX_UNSIGNED_INT, ppid, PG_P | PG_USU | PG_RWW, PG_P | PG_USU | PG_RWW); // 利用父进程的共享页申请物理页 memcpy((void *)SharePageBase, (void *)(addr_lin & 0xFFFFF000), num_4K); // 将数据复制到物理页上,注意这个地方是强制一页一页复制的 lin_mapping_phy(addr_lin, // 线性地址 get_page_phy_addr(ppid, SharePageBase), // 物理地址,获取共享页的物理地址,填进子进程页表 pid, // 要挂载的进程的pid,子进程的pid PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 PG_P | PG_USU | PG_RWW); // 页表属性,数据是可读写的 } // 复制数据,数据不共享,子进程需要申请物理地址,并复制过来 for (addr_lin = p_proc_current->task.memmap.data_lin_base; addr_lin < p_proc_current->task.memmap.data_lin_limit; addr_lin += num_4K) { lin_mapping_phy(SharePageBase, 0, ppid, PG_P | PG_USU | PG_RWW, 0); // 使用前必须清除这个物理页映射 lin_mapping_phy(SharePageBase, MAX_UNSIGNED_INT, ppid, PG_P | PG_USU | PG_RWW, PG_P | PG_USU | PG_RWW); // 利用父进程的共享页申请物理页 memcpy((void *)SharePageBase, (void *)(addr_lin & 0xFFFFF000), num_4K); // 将数据复制到物理页上,注意这个地方是强制一页一页复制的 lin_mapping_phy(addr_lin, // 线性地址 get_page_phy_addr(ppid, SharePageBase), // 物理地址,获取共享页的物理地址,填进子进程页表 pid, // 要挂载的进程的pid,子进程的pid PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 PG_P | PG_USU | PG_RWW); // 页表属性,数据是可读写的 } // 复制保留内存,保留内存不共享,子进程需要申请物理地址,并复制过来 for (addr_lin = p_proc_current->task.memmap.vpage_lin_base; addr_lin < p_proc_current->task.memmap.vpage_lin_limit; addr_lin += num_4K) { lin_mapping_phy(SharePageBase, 0, ppid, PG_P | PG_USU | PG_RWW, 0); // 使用前必须清除这个物理页映射 lin_mapping_phy(SharePageBase, MAX_UNSIGNED_INT, ppid, PG_P | PG_USU | PG_RWW, PG_P | PG_USU | PG_RWW); // 利用父进程的共享页申请物理页 memcpy((void *)SharePageBase, (void *)(addr_lin & 0xFFFFF000), num_4K); // 将数据复制到物理页上,注意这个地方是强制一页一页复制的 lin_mapping_phy(addr_lin, // 线性地址 get_page_phy_addr(ppid, SharePageBase), // 物理地址,获取共享页的物理地址,填进子进程页表 pid, // 要挂载的进程的pid,子进程的pid PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 PG_P | PG_USU | PG_RWW); // 页表属性,保留内存是可读写的 } // 复制堆,堆不共享,子进程需要申请物理地址,并复制过来 for (addr_lin = p_proc_current->task.memmap.heap_lin_base; addr_lin < p_proc_current->task.memmap.heap_lin_limit; addr_lin += num_4K) { lin_mapping_phy(SharePageBase, 0, ppid, PG_P | PG_USU | PG_RWW, 0); // 使用前必须清除这个物理页映射 lin_mapping_phy(SharePageBase, MAX_UNSIGNED_INT, ppid, PG_P | PG_USU | PG_RWW, PG_P | PG_USU | PG_RWW); // 利用父进程的共享页申请物理页 memcpy((void *)SharePageBase, (void *)(addr_lin & 0xFFFFF000), num_4K); // 将数据复制到物理页上,注意这个地方是强制一页一页复制的 lin_mapping_phy(addr_lin, // 线性地址 get_page_phy_addr(ppid, SharePageBase), // 物理地址,获取共享页的物理地址,填进子进程页表 pid, // 要挂载的进程的pid,子进程的pid PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 PG_P | PG_USU | PG_RWW); // 页表属性,堆是可读写的 } // 复制栈,栈不共享,子进程需要申请物理地址,并复制过来(注意栈的复制方向) for (addr_lin = p_proc_current->task.memmap.stack_lin_base; addr_lin > p_proc_current->task.memmap.stack_lin_limit; addr_lin -= num_4K) { lin_mapping_phy(SharePageBase, 0, ppid, PG_P | PG_USU | PG_RWW, 0); // 使用前必须清除这个物理页映射 lin_mapping_phy(SharePageBase, MAX_UNSIGNED_INT, ppid, PG_P | PG_USU | PG_RWW, PG_P | PG_USU | PG_RWW); // 利用父进程的共享页申请物理页 memcpy((void *)SharePageBase, (void *)(addr_lin & 0xFFFFF000), num_4K); // 将数据复制到物理页上,注意这个地方是强制一页一页复制的 lin_mapping_phy(addr_lin, // 线性地址 get_page_phy_addr(ppid, SharePageBase), // 物理地址,获取共享页的物理地址,填进子进程页表 pid, // 要挂载的进程的pid,子进程的pid PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 PG_P | PG_USU | PG_RWW); // 页表属性,栈是可读写的 } // 复制参数区,参数区不共享,子进程需要申请物理地址,并复制过来 for (addr_lin = p_proc_current->task.memmap.arg_lin_base; addr_lin < p_proc_current->task.memmap.arg_lin_limit; addr_lin += num_4K) { lin_mapping_phy(SharePageBase, 0, ppid, PG_P | PG_USU | PG_RWW, 0); // 使用前必须清除这个物理页映射 lin_mapping_phy(SharePageBase, MAX_UNSIGNED_INT, ppid, PG_P | PG_USU | PG_RWW, PG_P | PG_USU | PG_RWW); // 利用父进程的共享页申请物理页 memcpy((void *)SharePageBase, (void *)(addr_lin & 0xFFFFF000), num_4K); // 将数据复制到物理页上,注意这个地方是强制一页一页复制的 lin_mapping_phy(addr_lin, // 线性地址 get_page_phy_addr(ppid, SharePageBase), // 物理地址,获取共享页的物理地址,填进子进程页表 pid, // 要挂载的进程的pid,子进程的pid PG_P | PG_USU | PG_RWW, // 页目录属性,一般都为可读写 PG_P | PG_USU | PG_RWW); // 页表属性,参数区是可读写的 } return 0; } /********************************************************** * fork_pcb_cpy //add by visual 2016.5.26 *复制父进程PCB表,但是又马上恢复了子进程的标识信息 *************************************************************/ static int fork_pcb_cpy(PROCESS *p_child) { int pid; u32 eflags, selector_ldt, cr3_child; char *p_reg; // point to a register in the new kernel stack, added by xw, 17/12/11 // char* esp_save_int, esp_save_context; //It's not what you want! damn it. char *esp_save_int, *esp_save_context; // use to save corresponding field in child's PCB. // 暂存标识信息 pid = p_child->task.pid; // eflags = p_child->task.regs.eflags; p_reg = (char *)(p_child + 1); // added by xw, 17/12/11 eflags = *((u32 *)(p_reg + EFLAGSREG - P_STACKTOP)); // added by xw, 17/12/11 selector_ldt = p_child->task.ldt_sel; cr3_child = p_child->task.cr3; // 复制PCB内容 // modified by xw, 17/12/11 // modified begin //*p_child = *p_proc_current; // esp_save_int and esp_save_context must be saved, because the child and the parent // use different kernel stack! And these two are importent to the child's initial running. // Added by xw, 18/4/21 esp_save_int = p_child->task.esp_save_int; esp_save_context = p_child->task.esp_save_context; p_child->task = p_proc_current->task; // note that syscalls can be interrupted now! the state of child can only be setted // READY when anything else is well prepared. if an interruption happens right here, // an error will still occur. p_child->task.stat = IDLE; p_child->task.esp_save_int = esp_save_int; // esp_save_int of child must be restored!! p_child->task.esp_save_context = esp_save_context; // same above // p_child->task.esp_save_context = (char*)(p_child + 1) - P_STACKTOP - 4 * 6; memcpy(((char *)(p_child + 1) - P_STACKTOP), ((char *)(p_proc_current + 1) - P_STACKTOP), 18 * 4); // modified end // 恢复标识信息 p_child->task.pid = pid; // p_child->task.regs.eflags = eflags; p_reg = (char *)(p_child + 1); // added by xw, 17/12/11 *((u32 *)(p_reg + EFLAGSREG - P_STACKTOP)) = eflags; // added by xw, 17/12/11 p_child->task.ldt_sel = selector_ldt; p_child->task.cr3 = cr3_child; return 0; } /********************************************************** * fork_update_info //add by visual 2016.5.26 *更新父进程和子进程的进程树标识info *************************************************************/ static int fork_update_info(PROCESS *p_child) { /************更新父进程的info***************/ // p_proc_current->task.info.type; //当前是进程还是线程 // p_proc_current->task.info.real_ppid; //亲父进程,创建它的那个进程 // p_proc_current->task.info.ppid; //当前父进程 p_proc_current->task.info.child_p_num += 1; // 子进程数量 p_proc_current->task.info.child_process[p_proc_current->task.info.child_p_num - 1] = p_child->task.pid; // 子进程列表 // p_proc_current->task.info.child_t_num; //子线程数量 // p_proc_current->task.info.child_thread[NR_CHILD_MAX];//子线程列表 // p_proc_current->task.text_hold; //是否拥有代码 // p_proc_current->task.data_hold; //是否拥有数据 /************更新子进程的info***************/ p_child->task.info.type = p_proc_current->task.info.type; // 当前进程属性跟父进程一样 p_child->task.info.real_ppid = p_proc_current->task.pid; // 亲父进程,创建它的那个进程 p_child->task.info.ppid = p_proc_current->task.pid; // 当前父进程 p_child->task.info.child_p_num = 0; // 子进程数量 // p_child->task.info.child_process[NR_CHILD_MAX] = pid;//子进程列表 p_child->task.info.child_t_num = 0; // 子线程数量 // p_child->task.info.child_thread[NR_CHILD_MAX];//子线程列表 p_child->task.info.text_hold = 0; // 是否拥有代码,子进程不拥有代码 p_child->task.info.data_hold = 1; // 是否拥有数据,子进程拥有数据 return 0; }