process.c 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. #include "process.h"
  2. #include "tss.h"
  3. #include "../kernel/debug.h"
  4. #include "../device/console.h"
  5. #include "../kernel/memory.h"
  6. #include "../kernel/interrupt.h"
  7. /// @brief 内核中断函数 intr_exit
  8. /// 实现见 kernel/kernel.S
  9. /// @param None
  10. extern void intr_exit(void);
  11. /// @brief 创建用户进程初始化上下文信息
  12. /// @param filename_ 进程文件名
  13. void start_process(void *filename_)
  14. {
  15. void *function = filename_;
  16. struct task_struct *cur = running_thread();
  17. // 指向中断栈(intr_stack的低端,即低地址处),PCB布局回顾(从上到下表示内存地址从高到低)
  18. // 1. intr_stack
  19. // 2. thread_stack
  20. // PCB的属性self_kstack在线程创建完毕后指向thread_stack的最底部
  21. cur->self_kstack += sizeof(struct thread_stack);
  22. struct intr_stack *proc_stack = (struct intr_stack *)cur->self_kstack;
  23. proc_stack->edi = proc_stack->esi = proc_stack->ebp = proc_stack->esp_dummy = 0;
  24. proc_stack->ebx = proc_stack->edx = proc_stack->ecx = proc_stack->eax = proc_stack->gs = 0;
  25. // 为通过中断返回的方式进入 3 特权级做准备
  26. proc_stack->ds = proc_stack->es = proc_stack->fs = SELECTOR_U_DATA;
  27. proc_stack->eip = function; // 待执行的用户程序地址
  28. proc_stack->cs = SELECTOR_U_CODE;
  29. proc_stack->eflags = (EFLAGES_IOPL_0 | EFLAGES_MBS | EFLAGES_IF_1);
  30. proc_stack->esp = (void *)(get_a_page(PF_USER, USER_STACK3_VADDR) + PG_SIZE);
  31. proc_stack->ss = SELECTOR_U_DATA;
  32. asm volatile("movl %0, %%esp; jmp intr_exit" ::"g"(proc_stack) : "memory");
  33. }
  34. /// @brief 激活页表, 页表切换
  35. /// @param p_thread
  36. void page_dir_activate(struct task_struct *p_thread)
  37. {
  38. /*****************************************************
  39. * 执行此函数时,当前任务可能是线程。
  40. * 之所以对线程也要重新安装页表,原因是上一次被调度的可能是进程,
  41. * 否则不恢复页表的话,线程就会使用进程的页表
  42. ******************************************************/
  43. /* 内核页表的物理地址,定义在boot.inc,进入保护模式时确定 */
  44. uint32_t pagedir_pyh_addr = 0x100000;
  45. if (p_thread->pgdir != NULL)
  46. { // 用户态进程有自己的页目录表
  47. pagedir_pyh_addr = addr_vaddr2phy((uintptr_t)p_thread->pgdir);
  48. }
  49. /* 更新页目录寄存器 cr3,使新页表生效 */
  50. asm volatile("movl %0, %%cr3" ::"r"(pagedir_pyh_addr) : "memory");
  51. }
  52. /// @brief 激活线程或进程的页表,更新 tss 中的 esp0 为进程的特权级 0 的栈
  53. /// @param p_thread
  54. void process_activate(struct task_struct *p_thread)
  55. {
  56. ASSERT(p_thread != NULL);
  57. page_dir_activate(p_thread); // 激活页表
  58. /* 内核线程特权级本身就是 0,处理器进入中断时并不会从 tss 中获取 0 的特权级栈地址,故不需要更新 esp0*/
  59. if (p_thread->pgdir)
  60. {
  61. update_tss_esp(p_thread); // 更新 tss 中 esp0 为线程的特权级 0 的栈
  62. }
  63. }
  64. /// @brief 创建页目录表,将当前页表的表示内核空间的 pde 复制
  65. /// @param
  66. /// @return 成功返回页目录的虚拟地址,否则返回 -1
  67. uint32_t *create_page_dir(void)
  68. {
  69. //! 用户进程占据页目录表 0~767 目录项
  70. //! 内核占据页目录表 768~1023 目录项, 即 0xc0000000 以上的地址空间
  71. /* 用户进程的页表不能让用户直接访问到,所以在内核空间来申请 */
  72. uint32_t *page_dir_vaddr = get_kernel_pages(1);
  73. // put_str("create_page_dir: page_dir_vaddr: ");
  74. // put_int((uintptr_t)page_dir_vaddr);
  75. if (page_dir_vaddr == NULL)
  76. {
  77. console_put_str("create_page_dir failed: get_kernel_pages failed!");
  78. return NULL;
  79. }
  80. /* 1. 先复制页表 ,将内核所在的页目录项复制到用户进程页目录表中同等位置 */
  81. // 用户进程页目录的第 768~1023 个页目录项 = 内核页目录表的 768~1023 页目录项
  82. // (uint32_t *)(page_dir_vaddr + 0x768*4) 是进程页目录表基地址 768 个页目录项的地方
  83. // 用户进程的创建是在内核中完成的,因此当前是在内核的页表中,其中 0xfffff000 便是用来访问内核页目录表基地址(也是第 0 个页目录项)
  84. _memcpy((uint32_t *)(page_dir_vaddr + 768 * 4), (uint32_t *)(0xfffff000 + 768 * 4), 1024); // 1024/4=256 个页目录项大小
  85. /* 2. 更新页目录地址,把最后一个页目录项更新为用户进程自己的页目录表的物理地址 */
  86. uint32_t new_page_dir_phy_addr = addr_vaddr2phy((uintptr_t)page_dir_vaddr);
  87. page_dir_vaddr[1023] = new_page_dir_phy_addr | PG_US_U | PG_RW_W | PG_P_1;
  88. return page_dir_vaddr;
  89. }
  90. /// @brief 创建用户进程虚拟虚拟内存池
  91. /// @param user_prog pcb of user
  92. void create_user_vaddr_bitmap(struct task_struct *user_process)
  93. {
  94. user_process->userprog_addr.vaddr_start = USER_VADDR_START;
  95. // 0xc0000000是内核虚拟地址起始处
  96. uint32_t bitmap_page_count = DIV_ROUND_UP((0xc0000000 - USER_VADDR_START) / PG_SIZE / 8, PG_SIZE);
  97. user_process->userprog_addr.vaddr_bitmap.bits = get_kernel_pages(bitmap_page_count);
  98. user_process->userprog_addr.vaddr_bitmap.btmp_bytes_len = (0xc0000000 - USER_VADDR_START) / PG_SIZE / 8;
  99. bitmap_init(&user_process->userprog_addr.vaddr_bitmap);
  100. }
  101. /// @brief 创建用户进程
  102. /// @param filename 用户进程地址
  103. /// @param name 进程名
  104. void process_execute(void *filename, char *name)
  105. {
  106. /*pcb 内核的数据结构,由内核来维护进程信息, 因此要在内核内存池中申请*/
  107. struct task_struct *pcb = get_kernel_pages(1);
  108. init_thread(pcb, name, default_prio);
  109. create_user_vaddr_bitmap(pcb);
  110. thread_create(pcb, start_process, filename);
  111. pcb->pgdir = create_page_dir();
  112. enum intr_status old_status = intr_disable();
  113. ASSERT(!elem_find(&thread_ready_list, &pcb->general_tag))
  114. list_append(&thread_ready_list, &pcb->general_tag);
  115. ASSERT(!elem_find(&thread_all_list, &pcb->all_list_tag));
  116. list_append(&thread_all_list, &pcb->all_list_tag);
  117. intr_set_status(old_status);
  118. }