BigOS/kernel/fork.c
2023-01-02 16:15:14 +08:00

241 lines
14 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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