第八周 进程的切换和系统的一般执行过程
一、进程切换关键代码switch_to
1.不同类型进程有不同调度需求——两种分类
2.调度策略——规则
- Linux中进程优先级是动态的,周期性调整。
3.时机
schedule()函数负责调度。
中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
4.进程上下文包含了进程执行需要的所有信息
用户地址空间:包括程序代码,数据,用户堆栈等
控制信息:进程描述符,内核堆栈等
硬件上下文(与中断保存硬件上下文的方法不同)
5.switch_to 切换寄存器的状态
schedule()函数选择一个新的进程来运行,调用switch_to来进行关键上下文切换
schedule() --> context_switch() --> switch_to --> __switch_to()
当schedule()需要暂停X进程的执行而继续Y进程的执行时,就发生了进程之间的切换。
- 进程切换主要有两部分:1、切换全局页表项;2、切换内核堆栈和硬件上下文。
switch_to时请注意:这是一个宏,不是函数。
- 不是通过普通的call来实现,而是直接jmp,函数参数也并不是通过堆栈来传递,而是通过寄存器来传递。
二、Linux系统的一般执行过程
1.从正在运行的用户态进程X到Y
X正在运行--->发生中断,可能陷入内核,CPU自动保存加载--->SAVE_ALL保存现场--->调用schedule,switch_to进程上下文切换--->标号1之后运行Y(之前有进行准备动作)--->restore_all恢复现场--->iret- pop cs:eip/ss:esp/eflags from kernel stack--->继续运行用户态进程Y
2.特殊情况
通过中断处理过程中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最一般的情况非常类似,只是内核线程运行过程中发生中断没有进程用户态和内核态的转换;
内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最一般的情况略简略;
创建子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
加载一个新的可执行程序后返回到用户态的情况,如execve;
3.内核——Taxi
- 内核地址空间的4G中3G以上是内核态可以访问的——是所有进程共享的。
三、Linux系统架构和执行过程
1.执行ls命令
- 涉及中断、终端控制台设备驱动的概念。
- 过程:
shell分析-->调用系统调用fork生成一个shell本身拷贝-->调用exec系统调用将ls可执行文件装入内存-->从系统调用返回
四、实践
schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,context_switch是一个宏,这个宏调用switch_to()函数来进行关键上下文切换
next = pick_next_task(rq, prev); //进程调度算法都封装这个函数内部
context_switch(rq, prev, next); //进程上下文切换
switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程
总结
linux系统的一般执行过程
X正在运行--->发生中断,可能陷入内核,CPU自动保存加载--->SAVE_ALL保存现场--->调用schedule,switch_to进程上下文切换--->标号1之后运行Y(之前有进行准备动作)--->restore_all恢复现场--->iret- pop cs:eip/ss:esp/eflags from kernel stack--->继续运行用户态进程Y