进程调度时机:中断处理过程(包括时钟中断,I/O中断,系统调用)中,直接调用schedule(),或者返回用户态根据need_resched标记(抢占?)调用schedule(),如sleep(),就直接内核态调用schedule();
中断上下文:cs.ip.sp.bp.flags寄存器等,保存在中断栈(存在与否取决于不同的架构)上;
流程如下:
用户态执行系统调用,通过软件中断int80,cpu从idt表中查找中断处理例程,获得GDT表获得相应地址,进行段选择时,进行权限合法检验,转换内核态就需要从tss冲获取esp和ss寄存器的值,并保存就特权级现场(esp,ss,cs,ip,eflag等)在内核堆栈上,装载上面的获得的地址进入中断入口,save_all,在内核态作系统调用处理,当需要sehedule()时,通过pick_next_tack(rq, prev)上获取next进程,context_switch(re, prev, next)进行进程切换,切换堆栈,修改pgd到cr3寄存器。
restore_all,通过iret弹出在内核栈上的cs.ip.esp.ss.eflag恢复用户态。
其中对于schedule()内部过程如下:
首先禁止抢占,获取当前CPU,该CPU的执行队列,队列上正在执行的进程,以及该进程的交换计数信息并释放该进程占用的锁。之后,对禁止中断,更新运行队列时钟,该队列的自旋时钟加锁,后清除当前进程的thread_flag中TIF_NEED_RESCHED,如果进程不在可运行状态,并且可被抢占,若进程处于非阻塞挂起,则将其改为可运行,否则调用deactivate_task()函数,并修改上下文交换次数。其中在deactive_task()函数中调用了denqueue_task()函数,进程调用属于自己调度类的dequeue_task()方法,将p从当前rp运行队列上移出,不移除则神对不同的进程调用不同调度算法,放入不同队列。