-void spawn_lxinit() {
- struct proc_info kinit;
- uint32_t* kstack = (uint32_t*)KSTACK_TOP - 4 * 5;
-
- memset(&kinit, 0, sizeof(kinit));
- kinit.page_table = (void*) cpu_rcr3();
- kinit.parent = -1;
- kinit.pid = 1;
- kinit.intr_ctx = (isr_param) {
- .registers.esp = kstack,
- .cs = KCODE_SEG,
- .eip = (void*)_kernel_post_init,
- .ss = KDATA_SEG,
- .eflags = cpu_reflags()
- };
-
- /*
- 因为schedule从设计上是需要在中断环境中执行的
- 可是我们需要在这里手动调用 schedule,从而使我们的init能够被执行。
- 所以需要模拟中断产生时的栈里内容。
- */
- kstack[2] = kinit.intr_ctx.eip;
- kstack[3] = kinit.intr_ctx.cs;
- kstack[4] = kinit.intr_ctx.eflags;
-
- push_process(&kinit);
-
- schedule();
-}
+void
+spawn_proc0()
+{
+ struct proc_info* proc0 = alloc_process();
+
+ /**
+ * @brief
+ * 注意:这里和视频中说的不一样,属于我之后的一点微调。
+ * 在视频中,spawn_proc0是在_kernel_post_init的末尾才调用的。并且是直接跳转到_proc0
+ *
+ * 但是我后来发现,上述的方法会产生竞态条件。这是因为spawn_proc0被调用的时候,时钟中断已经开启,
+ * 而中断的产生会打乱栈的布局,从而使得下面的上下文设置代码产生未定义行为(Undefined
+ * Behaviour)。 为了保险起见,有两种办法:
+ * 1. 在创建proc0进程前关闭中断
+ * 2. 将_kernel_post_init搬进proc0进程
+ * (_kernel_post_init已经更名为init_platform)
+ *
+ * 目前的解决方案是2
+ */
+
+ proc0->intr_ctx = (isr_param){ .registers = { .ds = KDATA_SEG,
+ .es = KDATA_SEG,
+ .fs = KDATA_SEG,
+ .gs = KDATA_SEG } };
+ proc0->parent = proc0;
+
+ // 方案1:必须在读取eflags之后禁用。否则当进程被调度时,中断依然是关闭的!
+ // cpu_disable_interrupt();
+
+ /* Ok... 首先fork进我们的零号进程,而后由那里,我们fork进init进程。 */
+
+ // 把当前虚拟地址空间(内核)复制一份。
+ proc0->page_table = vmm_dup_vmspace(proc0->pid);
+
+ // 直接切换到新的拷贝,进行配置。
+ cpu_lcr3(proc0->page_table);