|
|
@@ -0,0 +1,134 @@
|
|
|
+#include "process.h"
|
|
|
+#include "tss.h"
|
|
|
+#include "../kernel/debug.h"
|
|
|
+#include "../device/console.h"
|
|
|
+#include "../kernel/memory.h"
|
|
|
+#include "../kernel/interrupt.h"
|
|
|
+
|
|
|
+/// @brief 内核中断函数 intr_exit
|
|
|
+/// 实现见 kernel/kernel.S
|
|
|
+/// @param None
|
|
|
+extern void intr_exit(void);
|
|
|
+
|
|
|
+/// @brief 创建用户进程初始化上下文信息
|
|
|
+/// @param filename_ 进程文件名
|
|
|
+void start_process(void *filename_)
|
|
|
+{
|
|
|
+ void *function = filename_;
|
|
|
+ struct task_struct *cur = running_thread();
|
|
|
+
|
|
|
+ // 指向中断栈(intr_stack的低端,即低地址处),PCB布局回顾(从上到下表示内存地址从高到低)
|
|
|
+ // 1. intr_stack
|
|
|
+ // 2. thread_stack
|
|
|
+ // PCB的属性self_kstack在线程创建完毕后指向thread_stack的最底部
|
|
|
+ cur->self_kstack += sizeof(struct thread_stack);
|
|
|
+ struct intr_stack *proc_stack = (struct intr_stack *)cur->self_kstack;
|
|
|
+
|
|
|
+ proc_stack->edi = proc_stack->esi = proc_stack->ebp = proc_stack->esp_dummy = 0;
|
|
|
+ proc_stack->ebx = proc_stack->edx = proc_stack->ecx = proc_stack->eax = proc_stack->gs = 0;
|
|
|
+
|
|
|
+ // 为通过中断返回的方式进入 3 特权级做准备
|
|
|
+ proc_stack->ds = proc_stack->es = proc_stack->fs = SELECTOR_U_DATA;
|
|
|
+ proc_stack->eip = function; // 待执行的用户程序地址
|
|
|
+ proc_stack->cs = SELECTOR_U_CODE;
|
|
|
+ proc_stack->eflags = (EFLAGES_IOPL_0 | EFLAGES_MBS | EFLAGES_IF_1);
|
|
|
+ proc_stack->esp = (void *)(get_a_page(PF_USER, USER_STACK3_VADDR) + PG_SIZE);
|
|
|
+ proc_stack->ss = SELECTOR_U_DATA;
|
|
|
+ asm volatile("movl %0, %%esp; jmp intr_exit" ::"g"(proc_stack) : "memory");
|
|
|
+}
|
|
|
+/// @brief 激活页表, 页表切换
|
|
|
+/// @param p_thread
|
|
|
+void page_dir_activate(struct task_struct *p_thread)
|
|
|
+{
|
|
|
+ /*****************************************************
|
|
|
+ * 执行此函数时,当前任务可能是线程。
|
|
|
+ * 之所以对线程也要重新安装页表,原因是上一次被调度的可能是进程,
|
|
|
+ * 否则不恢复页表的话,线程就会使用进程的页表
|
|
|
+ ******************************************************/
|
|
|
+
|
|
|
+ /* 内核页表的物理地址,定义在boot.inc,进入保护模式时确定 */
|
|
|
+ uint32_t pagedir_pyh_addr = 0x100000;
|
|
|
+ if (p_thread->pgdir != NULL)
|
|
|
+ { // 用户态进程有自己的页目录表
|
|
|
+ pagedir_pyh_addr = addr_vaddr2phy((uintptr_t)p_thread->pgdir);
|
|
|
+ }
|
|
|
+ /* 更新页目录寄存器 cr3,使新页表生效 */
|
|
|
+ asm volatile("movl %0, %%cr3" ::"r"(pagedir_pyh_addr) : "memory");
|
|
|
+}
|
|
|
+/// @brief 激活线程或进程的页表,更新 tss 中的 esp0 为进程的特权级 0 的栈
|
|
|
+/// @param p_thread
|
|
|
+void process_activate(struct task_struct *p_thread)
|
|
|
+{
|
|
|
+ ASSERT(p_thread != NULL);
|
|
|
+ page_dir_activate(p_thread); // 激活页表
|
|
|
+
|
|
|
+ /* 内核线程特权级本身就是 0,处理器进入中断时并不会从 tss 中获取 0 的特权级栈地址,故不需要更新 esp0*/
|
|
|
+ if (p_thread->pgdir)
|
|
|
+ {
|
|
|
+ update_tss_esp(p_thread); // 更新 tss 中 esp0 为线程的特权级 0 的栈
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 创建页目录表,将当前页表的表示内核空间的 pde 复制
|
|
|
+/// @param
|
|
|
+/// @return 成功返回页目录的虚拟地址,否则返回 -1
|
|
|
+uint32_t *create_page_dir(void)
|
|
|
+{
|
|
|
+ //! 用户进程占据页目录表 0~767 目录项
|
|
|
+ //! 内核占据页目录表 768~1023 目录项, 即 0xc0000000 以上的地址空间
|
|
|
+ /* 用户进程的页表不能让用户直接访问到,所以在内核空间来申请 */
|
|
|
+ uint32_t *page_dir_vaddr = get_kernel_pages(1);
|
|
|
+ if (page_dir_vaddr == NULL)
|
|
|
+ {
|
|
|
+ console_put_str("create_page_dir failed: get_kernel_pages failed!");
|
|
|
+ return NULL;
|
|
|
+ }
|
|
|
+ /* 1. 先复制页表 ,将内核所在的页目录项复制到用户进程页目录表中同等位置 */
|
|
|
+ // 用户进程页目录的第 768~1023 个页目录项 = 内核页目录表的 768~1023 页目录项
|
|
|
+ // (uint32_t *)(page_dir_vaddr + 0x768*4) 是进程页目录表基地址 768 个页目录项的地方
|
|
|
+ // 用户进程的创建是在内核中完成的,因此当前是在内核的页表中,其中 0xfffff000 便是用来访问内核页目录表基地址(也是第 0 个页目录项)
|
|
|
+ _memcpy((uint32_t *)(page_dir_vaddr + 768 * 4), (uint32_t *)(0xfffff000 + 768 * 4), 1024); // 1024/4=256 个页目录项大小
|
|
|
+
|
|
|
+ /* 2. 更新页目录地址,把最后一个页目录项更新为用户进程自己的页目录表的物理地址 */
|
|
|
+ uint32_t new_page_dir_phy_addr = addr_vaddr2phy((uintptr_t)page_dir_vaddr);
|
|
|
+ page_dir_vaddr[1023] = new_page_dir_phy_addr | PG_US_U | PG_RW_W | PG_P_1;
|
|
|
+
|
|
|
+ return page_dir_vaddr;
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 创建用户进程虚拟虚拟内存池
|
|
|
+/// @param user_prog pcb of user
|
|
|
+void create_user_vaddr_bitmap(struct task_struct *user_process)
|
|
|
+{
|
|
|
+ user_process->userprog_addr.vaddr_start = USER_VADDR_START;
|
|
|
+ // 0xc0000000是内核虚拟地址起始处
|
|
|
+ uint32_t bitmap_page_count = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8, PG_SIZE);
|
|
|
+
|
|
|
+ user_process->userprog_addr.vaddr_bitmap.bits = get_kernel_pages(bitmap_page_count);
|
|
|
+ user_process->userprog_addr.vaddr_bitmap.btmp_bytes_len = (0xc0000000 - USER_VADDR_START) / PG_SIZE / 8;
|
|
|
+
|
|
|
+ bitmap_init(&user_process->userprog_addr.vaddr_bitmap);
|
|
|
+}
|
|
|
+
|
|
|
+/// @brief 创建用户进程
|
|
|
+/// @param filename 用户进程地址
|
|
|
+/// @param name 进程名
|
|
|
+void process_execute(void *filename, char *name)
|
|
|
+{
|
|
|
+ /*pcb 内核的数据结构,由内核来维护进程信息, 因此要在内核内存池中申请*/
|
|
|
+ struct task_struct *pcb = get_kernel_pages(1);
|
|
|
+ init_thread(pcb, name, default_prio);
|
|
|
+ create_user_vaddr_bitmap(pcb);
|
|
|
+ thread_create(pcb, start_process, filename);
|
|
|
+ pcb->pgdir = create_page_dir();
|
|
|
+
|
|
|
+ enum intr_status old_status = intr_disable();
|
|
|
+
|
|
|
+ ASSERT(!elem_find(&thread_ready_list, &pcb->general_tag))
|
|
|
+ list_append(&thread_ready_list, &pcb->general_tag);
|
|
|
+
|
|
|
+ ASSERT(!elem_find(&thread_all_list, &pcb->all_list_tag));
|
|
|
+ list_append(&thread_all_list, &pcb->all_list_tag);
|
|
|
+
|
|
|
+ intr_set_status(old_status);
|
|
|
+}
|