+ for (size_t i = 0; i <= pg_size; i += 4096) {
+ uintptr_t pa = pmm_alloc_page(KERNEL_PID, PP_FGPERSIST);
+ vmm_set_mapping(
+ PD_REFERENCED, PROC_START + i, pa, PG_PREM_RW, VMAP_NULL);
+ }
+
+ sched_ctx = (struct scheduler){ ._procs = (struct proc_info*)PROC_START,
+ .ptable_len = 0,
+ .procs_index = 0 };
+}
+
+void
+run(struct proc_info* proc)
+{
+ proc->state = PS_RUNNING;
+
+ /*
+ 将tss.esp0设置为上次调度前的esp值。
+ 当处理信号时,上下文信息是不会恢复的,而是保存在用户栈中,然后直接跳转进位于用户空间的sig_wrapper进行
+ 信号的处理。当用户自定义的信号处理函数返回时,sigreturn的系统调用才开始进行上下文的恢复(或者说是进行
+ 另一次调度。
+ 由于这中间没有进行地址空间的交换,所以第二次跳转使用的是同一个内核栈,而之前默认tss.esp0的值是永远指向最顶部
+ 这样一来就有可能会覆盖更早的上下文信息(比如嵌套的信号捕获函数)
+ */
+ tss_update_esp(proc->intr_ctx.registers.esp);
+
+ apic_done_servicing();
+
+ asm volatile("pushl %0\n"
+ "jmp switch_to\n" ::"r"(proc)
+ : "memory"); // kernel/asm/x86/interrupt.S
+}
+
+int
+can_schedule(struct proc_info* proc)
+{
+ if (__SIGTEST(proc->sig_pending, _SIGCONT)) {
+ __SIGCLEAR(proc->sig_pending, _SIGSTOP);
+ } else if (__SIGTEST(proc->sig_pending, _SIGSTOP)) {
+ // 如果进程受到SIGSTOP,则该进程不给予调度。
+ return 0;
+ }
+
+ return 1;
+}
+
+void
+check_sleepers()
+{
+ struct proc_info* leader = &sched_ctx._procs[0];
+ struct proc_info *pos, *n;
+ time_t now = clock_systime();
+ llist_for_each(pos, n, &leader->sleep.sleepers, sleep.sleepers)
+ {
+ if (PROC_TERMINATED(pos->state)) {
+ goto del;
+ }
+
+ time_t wtime = pos->sleep.wakeup_time;
+ time_t atime = pos->sleep.alarm_time;
+
+ if (wtime && now >= wtime) {
+ pos->sleep.wakeup_time = 0;
+ pos->state = PS_READY;
+ }
+
+ if (atime && now >= atime) {
+ pos->sleep.alarm_time = 0;
+ __SIGSET(pos->sig_pending, _SIGALRM);
+ }
+
+ if (!wtime && !atime) {
+ del:
+ llist_delete(&pos->sleep.sleepers);
+ }
+ }