From 099b24539a8aef2e3c531110ba65c560b54d4e32 Mon Sep 17 00:00:00 2001 From: fslongjin Date: Thu, 5 May 2022 14:14:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84execve=EF=BC=8C=E4=BD=BF?= =?UTF-8?q?=E5=85=B6=E8=83=BD=E5=8A=A0=E8=BD=BD=E7=94=A8=E6=88=B7=E7=A8=8B?= =?UTF-8?q?=E5=BA=8F=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- kernel/driver/disk/ahci/ahci.c | 4 +- kernel/mm/mm.c | 8 +- kernel/mm/mm.h | 2 +- kernel/process/process.c | 176 +++++++++++++++++++++++++++------ kernel/process/process.h | 11 ++- kernel/sched/sched.c | 2 +- 6 files changed, 161 insertions(+), 42 deletions(-) diff --git a/kernel/driver/disk/ahci/ahci.c b/kernel/driver/disk/ahci/ahci.c index 372651fa..82514149 100644 --- a/kernel/driver/disk/ahci/ahci.c +++ b/kernel/driver/disk/ahci/ahci.c @@ -362,7 +362,7 @@ static bool ahci_write(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_ port->ci = 1; // Issue command current_pcb->flags |= PF_NEED_SCHED; - //sched_cfs(); + sched_cfs(); int retval = AHCI_SUCCESS; while (1) @@ -383,7 +383,7 @@ static bool ahci_write(HBA_PORT *port, uint32_t startl, uint32_t starth, uint32_ kerror("Write disk error"); retval = E_TASK_FILE_ERROR; } - kdebug("ahci write retval=%d", retval); + // kdebug("ahci write retval=%d", retval); enter_syscall_int(SYS_AHCI_END_REQ, 0, 0, 0, 0, 0, 0, 0, 0); return retval; } diff --git a/kernel/mm/mm.c b/kernel/mm/mm.c index 116669fe..ca8633e7 100644 --- a/kernel/mm/mm.c +++ b/kernel/mm/mm.c @@ -622,9 +622,9 @@ void mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul * @param length 要映射的区域的长度(字节) * @param user 用户态是否可访问 */ -void mm_map_proc_page_table(ul *proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user) +void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user) { - + // kdebug("proc_page_table_addr=%#018lx",proc_page_table_addr); // 计算线性地址对应的pml4页表项的地址 ul *tmp; if (is_phys) @@ -632,14 +632,14 @@ void mm_map_proc_page_table(ul *proc_page_table_addr, bool is_phys, ul virt_addr else tmp = (ul *)((ul)proc_page_table_addr & (~0xfffUL)) + ((virt_addr_start >> PAGE_GDT_SHIFT) & 0x1ff); - kdebug("tmp = %#018lx", tmp); + // kdebug("tmp = %#018lx", tmp); if (*tmp == 0) { ul *virt_addr = kmalloc(PAGE_4K_SIZE, 0); memset(virt_addr, 0, PAGE_4K_SIZE); set_pml4t(tmp, mk_pml4t(virt_2_phys(virt_addr), (user ? PAGE_USER_PGT : PAGE_KERNEL_PGT))); } - kdebug("*tmp = %#018lx", *tmp); + // kdebug("*tmp = %#018lx", *tmp); if (is_phys) tmp = phys_2_virt((ul *)(*tmp & (~0xfffUL)) + ((virt_addr_start >> PAGE_1G_SHIFT) & 0x1ff)); diff --git a/kernel/mm/mm.h b/kernel/mm/mm.h index a4b4d569..4f28e2fa 100644 --- a/kernel/mm/mm.h +++ b/kernel/mm/mm.h @@ -373,7 +373,7 @@ void mm_map_phys_addr(ul virt_addr_start, ul phys_addr_start, ul length, ul flag * @param length 要映射的区域的长度(字节) * @param user 用户态是否可访问 */ -void mm_map_proc_page_table(ul *proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user); +void mm_map_proc_page_table(ul proc_page_table_addr, bool is_phys, ul virt_addr_start, ul phys_addr_start, ul length, ul flags, bool user); void mm_map_phys_addr_user(ul virt_addr_start, ul phys_addr_start, ul length, ul flags); \ No newline at end of file diff --git a/kernel/process/process.c b/kernel/process/process.c index 3cc19fc2..98765fbc 100644 --- a/kernel/process/process.c +++ b/kernel/process/process.c @@ -44,6 +44,59 @@ struct process_control_block *initial_proc[MAX_CPU_NUM] = {&initial_proc_union.p // 为每个核心初始化初始进程的tss struct tss_struct initial_tss[MAX_CPU_NUM] = {[0 ... MAX_CPU_NUM - 1] = INITIAL_TSS}; +/** + * @brief 拷贝当前进程的标志位 + * + * @param clone_flags 克隆标志位 + * @param pcb 新的进程的pcb + * @return uint64_t + */ +uint64_t process_copy_flags(uint64_t clone_flags, struct process_control_block *pcb); + +/** + * @brief 拷贝当前进程的文件描述符等信息 + * + * @param clone_flags 克隆标志位 + * @param pcb 新的进程的pcb + * @return uint64_t + */ +uint64_t process_copy_files(uint64_t clone_flags, struct process_control_block *pcb); + +/** + * @brief 回收进程的所有文件描述符 + * + * @param pcb 要被回收的进程的pcb + * @return uint64_t + */ +uint64_t process_exit_files(struct process_control_block *pcb); + +/** + * @brief 拷贝当前进程的内存空间分布结构体信息 + * + * @param clone_flags 克隆标志位 + * @param pcb 新的进程的pcb + * @return uint64_t + */ +uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb); + +/** + * @brief 释放进程的页表 + * + * @param pcb 要被释放页表的进程 + * @return uint64_t + */ +uint64_t process_exit_mm(struct process_control_block *pcb); + +/** + * @brief 拷贝当前进程的线程结构体 + * + * @param clone_flags 克隆标志位 + * @param pcb 新的进程的pcb + * @return uint64_t + */ +uint64_t process_copy_thread(uint64_t clone_flags, struct process_control_block *pcb, uint64_t stack_start, uint64_t stack_size, struct pt_regs *current_regs); + +void process_exit_thread(struct process_control_block *pcb); /** * @brief 切换进程 * @@ -273,9 +326,10 @@ void user_level_function() * @brief 使当前进程去执行新的代码 * * @param regs 当前进程的寄存器 + * @param path 可执行程序的路径 * @return ul 错误码 */ -ul do_execve(struct pt_regs *regs) +ul do_execve(struct pt_regs *regs, char *path) { // 选择这两个寄存器是对应了sysexit指令的需要 regs->rip = 0x800000; // rip 应用层程序的入口地址 这里的地址选择没有特殊要求,只要是未使用的内存区域即可。 @@ -287,35 +341,75 @@ ul do_execve(struct pt_regs *regs) regs->rax = 1; regs->es = 0; - // kdebug("do_execve is running..."); + kdebug("do_execve is running..."); - // 映射起始页面 - // mm_map_proc_page_table(get_CR3(), true, 0x800000, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE, true); + // 当前进程正在与父进程共享地址空间,需要创建 + // 独立的地址空间才能使新程序正常运行 + if (current_pcb->flags & PF_VFORK) + { + kdebug("proc:%d creating new mem space", current_pcb->pid); + // 分配新的内存空间分布结构体 + struct mm_struct *new_mms = (struct mm_struct *)kmalloc(sizeof(struct mm_struct), 0); + memset(new_mms, 0, sizeof(struct mm_struct)); + current_pcb->mm = new_mms; - uint64_t addr = 0x800000UL; - /* - unsigned long *tmp = phys_2_virt((unsigned long *)((unsigned long)get_CR3() & (~0xfffUL)) + ((addr >> PAGE_GDT_SHIFT) & 0x1ff)); + // 分配顶层页表, 并设置顶层页表的物理地址 + new_mms->pgd = (pml4t_t *)virt_2_phys(kmalloc(PAGE_4K_SIZE, 0)); - unsigned long *virtual = kmalloc(PAGE_4K_SIZE, 0); - set_pml4t(tmp, mk_pml4t(virt_2_phys(virtual), PAGE_USER_PGT)); + // 由于高2K部分为内核空间,在接下来需要覆盖其数据,因此不用清零 + memset(phys_2_virt(new_mms->pgd), 0, PAGE_4K_SIZE / 2); - tmp = phys_2_virt((unsigned long *)(*tmp & (~0xfffUL)) + ((addr >> PAGE_1G_SHIFT) & 0x1ff)); - virtual = kmalloc(PAGE_4K_SIZE, 0); - set_pdpt(tmp, mk_pdpt(virt_2_phys(virtual), PAGE_USER_DIR)); + // 拷贝内核空间的页表指针 + memcpy(phys_2_virt(new_mms->pgd) + 256, phys_2_virt(initial_proc[proc_current_cpu_id]) + 256, PAGE_4K_SIZE / 2); + } - tmp = phys_2_virt((unsigned long *)(*tmp & (~0xfffUL)) + ((addr >> PAGE_2M_SHIFT) & 0x1ff)); - struct Page *p = alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED); - set_pdt(tmp, mk_pdt(p->addr_phys, PAGE_USER_PAGE)); + /** + * @todo: 加载elf文件并映射对应的页 + * + */ + // 映射1个2MB的物理页 + unsigned long code_start_addr = 0x800000; + unsigned long stack_start_addr = 0xa00000; - flush_tlb(); - */ + // mm_map_phys_addr_user(code_start_addr, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE); + mm_map_proc_page_table((uint64_t)current_pcb->mm->pgd, true, code_start_addr, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE, true); - mm_map_phys_addr_user(addr, alloc_pages(ZONE_NORMAL, 1, PAGE_PGT_MAPPED)->addr_phys, PAGE_2M_SIZE, PAGE_USER_PAGE); + process_switch_mm(current_pcb); + // 为用户态程序设置地址边界 if (!(current_pcb->flags & PF_KTHREAD)) current_pcb->addr_limit = USER_MAX_LINEAR_ADDR; + + current_pcb->mm->code_addr_start = code_start_addr; + current_pcb->mm->code_addr_end = 0; + current_pcb->mm->data_addr_start = 0; + current_pcb->mm->data_addr_end = 0; + current_pcb->mm->rodata_addr_start = 0; + current_pcb->mm->rodata_addr_end = 0; + current_pcb->mm->bss_start = 0; + current_pcb->mm->bss_end = 0; + current_pcb->mm->brk_start = 0; + current_pcb->mm->brk_end = 0; + current_pcb->mm->stack_start = stack_start_addr; + + // 关闭之前的文件描述符 + process_exit_files(current_pcb); + // 清除进程的vfork标志位 + current_pcb->flags &= ~PF_VFORK; + + int fd_num = enter_syscall_int(SYS_OPEN, path, ATTR_READ_ONLY, 0, 0, 0, 0, 0, 0); + if (fd_num < 0) + return fd_num; + memset((void *)code_start_addr, 0, PAGE_2M_SIZE); + // 将程序代码拷贝到对应的内存中 - memcpy((void *)0x800000, user_level_function, 1024); + int retval = enter_syscall_int(SYS_READ, fd_num, code_start_addr, PAGE_2M_SIZE, 0, 0, 0, 0, 0); + if (retval) + { + enter_syscall_int(SYS_CLOSE, fd_num, 0, 0, 0, 0, 0, 0, 0); + return retval; + } + retval = enter_syscall_int(SYS_CLOSE, fd_num, 0, 0, 0, 0, 0, 0, 0); // kdebug("program copied!"); return 0; @@ -336,6 +430,13 @@ ul initial_kernel_thread(ul arg) current_pcb->thread->rip = (ul)ret_from_system_call; current_pcb->thread->rsp = (ul)current_pcb + STACK_SIZE - sizeof(struct pt_regs); + current_pcb->thread->fs = USER_DS|0x3; + current_pcb->thread->gs = USER_DS|0x3; + + // 主动放弃内核线程身份 + current_pcb->flags &= (~PF_KTHREAD); + + // current_pcb->mm->pgd = kmalloc(PAGE_4K_SIZE, 0); // memset((void*)current_pcb->mm->pgd, 0, PAGE_4K_SIZE); @@ -343,9 +444,11 @@ ul initial_kernel_thread(ul arg) // kdebug("current_pcb->thread->rsp=%#018lx", current_pcb->thread->rsp); current_pcb->flags = 0; // 将返回用户层的代码压入堆栈,向rdx传入regs的地址,然后jmp到do_execve这个系统调用api的处理函数 这里的设计思路和switch_proc类似 + // 加载用户态程序:init.bin __asm__ __volatile__("movq %1, %%rsp \n\t" "pushq %2 \n\t" "jmp do_execve \n\t" ::"D"(current_pcb->thread->rsp), + "S"("/init.bin"), "m"(current_pcb->thread->rsp), "m"(current_pcb->thread->rip) : "memory"); @@ -437,9 +540,9 @@ void process_init() for (int i = 256; i < 512; ++i) { uint64_t *tmp = idle_pml4t_vaddr + i; - if(*tmp==0) + if (*tmp == 0) { - void* pdpt = kmalloc(PAGE_4K_SIZE,0); + void *pdpt = kmalloc(PAGE_4K_SIZE, 0); memset(pdpt, 0, PAGE_4K_SIZE); set_pml4t(tmp, mk_pml4t(virt_2_phys(pdpt), PAGE_KERNEL_PGT)); } @@ -671,16 +774,18 @@ uint64_t process_copy_files(uint64_t clone_flags, struct process_control_block * */ uint64_t process_exit_files(struct process_control_block *pcb) { - // 与父进程共享文件描述符 - if (pcb->flags & PF_VFORK) - return 0; - - for (int i = 0; i < PROC_MAX_FD_NUM; ++i) + // 不与父进程共享文件描述符 + if (!(pcb->flags & PF_VFORK)) { - if (pcb->fds[i] == NULL) - continue; - kfree(pcb->fds[i]); + + for (int i = 0; i < PROC_MAX_FD_NUM; ++i) + { + if (pcb->fds[i] == NULL) + continue; + kfree(pcb->fds[i]); + } } + // 清空当前进程的文件描述符列表 memset(pcb->fds, 0, sizeof(struct vfs_file_t *) * PROC_MAX_FD_NUM); } @@ -697,7 +802,9 @@ uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb // 与父进程共享内存空间 if (clone_flags & CLONE_VM) { + kdebug("copy_vm\t\t current_pcb->mm->pgd=%#018lx", current_pcb->mm->pgd); pcb->mm = current_pcb->mm; + return retval; } @@ -711,6 +818,8 @@ uint64_t process_copy_mm(uint64_t clone_flags, struct process_control_block *pcb // 分配顶层页表, 并设置顶层页表的物理地址 new_mms->pgd = (pml4t_t *)virt_2_phys(kmalloc(PAGE_4K_SIZE, 0)); + // 由于高2K部分为内核空间,在接下来需要覆盖其数据,因此不用清零 + memset(phys_2_virt(new_mms->pgd), 0, PAGE_4K_SIZE / 2); // 拷贝内核空间的页表指针 memcpy(phys_2_virt(new_mms->pgd) + 256, phys_2_virt(initial_proc[proc_current_cpu_id]) + 256, PAGE_4K_SIZE / 2); @@ -859,4 +968,13 @@ uint64_t process_copy_thread(uint64_t clone_flags, struct process_control_block thd->rip = (uint64_t)ret_from_system_call; kdebug("new proc's ret addr = %#018lx\tchild_regs->rsp = %#018lx", child_regs->rbx, child_regs->rsp); return 0; +} + +/** + * @brief todo: 回收线程结构体 + * + * @param pcb + */ +void process_exit_thread(struct process_control_block *pcb) +{ } \ No newline at end of file diff --git a/kernel/process/process.h b/kernel/process/process.h index c1eacc6f..2cb2693e 100644 --- a/kernel/process/process.h +++ b/kernel/process/process.h @@ -279,11 +279,12 @@ struct process_control_block *process_get_pcb(long pid); * @param next 下一个进程的pcb * */ -#define process_switch_mm(prev, next) \ - do \ - { \ - asm volatile("movq %0, %%cr3 \n\t" ::"r"(next->mm->pgd) \ - : "memory"); \ +#define process_switch_mm(next_pcb) \ + do \ + { \ + asm volatile("movq %0, %%cr3 \n\t" ::"r"(next_pcb->mm->pgd) \ + : "memory"); \ + flush_tlb(); \ } while (0) // 获取当前cpu id diff --git a/kernel/sched/sched.c b/kernel/sched/sched.c index 98b114fa..cfa1149a 100644 --- a/kernel/sched/sched.c +++ b/kernel/sched/sched.c @@ -75,7 +75,7 @@ void sched_cfs() } } // kdebug("before switch, next.rip = %#018lx\tnext->gs=%#018lx", proc->thread->rip, proc->thread->gs); - process_switch_mm(current_pcb, proc); + process_switch_mm(proc); switch_proc(current_pcb, proc); } else // 不进行切换