当前位置: > Linux教程 > Linux学习 >

Linux中进程调度与切换过程

时间:2019-05-05 13:25来源:linux.it.net.cn 作者:IT
    今天我们要学习的是Linux中进程调度与切换过程。有关进程的知识在前面的博客中已经提到了,有不懂的地方请参考我前面的博客,今天我直接从进程调度和切换开始讲。
 
       Linux一个较大的优势就是进程调度,因为Linux是一个多进程系统,它怎么进行进程调度直接影响这个系统的性能,而Linux系统的一个优势就是它的系统在进程调度这里做的很好。
 
      在讲进程调度前,我们先来看下有关Linux知识(以下4张图片摘自孟宁老师课件)。
 
 
                                              图1.Linux内核架构
 
 

 
                                              图2.Linux执行过程
 
 

 
                                             图3.CPU执行指令角度
 
 

 
                                        图4.从内存角度看待Linux系统执行
 
 
 
       首先一起来看下Linux的进程的类型,一般将进程分为三种,一种为I/O消耗型进程,另一种是处理器消耗型进程,还有一种是混合型,也就是I/O消耗型进程和处理器消耗型进程混合在一起的。从他们的名字可以看出,这是以进程消耗资源的种类来进行分类的。
 
       在Linux系统中,是按照什么规则来进行调度的呢?我们所知的有优先级调度,还有时间片调度。其中优先级指的是进程的优先级,而时间片则指的是进程所需要消耗的时间。
 
       那么Linux系统中进程调度的过程到底是一个什么流程呢?主要是以下几个方面
 
       1.从schedule()函数开始,进行调度选择
 
       2.从CPU的值变化上,解读switch_to宏执行分析
 
       3.到堆栈发生切换位置,在切换堆栈前后,current_thread_info变化
 
       4.再到地址空间发生切换,解释地址空间的切换不会影响后续切换代码的执行
 
       5.Current宏代表的进程发生变化的源码位置
 
       6.任务状态段中关于内核堆栈的信息发生变化的源码位置
 
 
 
      下面来详细的讲解一下各个环节
 
      在Linux内核中,schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换。部分函数具体代码如下,一个调度新进程,一个是进行上下文切换,还有相关堆栈信息的保存。
 
      next= pick_next_task(rq, prev);//进程调度算法都封装这个函数内部
 
     context_switch(rq,prev, next);//进程上下文切换
 
     switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程
 
 
 
      Schedule:主要负责帮助系统选定下一个执行的进程
 
      调度时机:
 
       1.进程状态转换的时刻,进程终止、进程睡眠。
 
       2.当前进程的时间片用完时。
 
       3.设备驱动程序调用。
 
       4.进程从中断、异常及系统调用返回到用户态时。
 
 
 
     进程的从睡眠状态到唤醒状态,完成了一次进程的调度,中间有保存相应的进程信息,有相应的队列进行保存。
 
     进程调度中还有一个现象是抢占,就是优先级高的进程进行抢占低优先级的执行机会,用户抢占发生在两种情况下,一个是从系统调用返回用户空间,另一个是从中断处理程序返回用户空间。
 
 
 
     而内核抢占发生在:
 
     1.当从中断处理程序正在执行,且返回内核空间之前;
 
     2.当内核代码再一次具有可抢占性的时候;
 
     3.如果内核中的任务显式调用;
 
     4.内核中的任务被阻塞。
 
    上下文的切换也是进程调用中一个比较重要的问题,其中有一个context_switch()函数完成以下工作,switch_mm()——该函数负责把虚拟内存从上一个进程映射切换到新进程中。而switch_to()——负责从上一个进程的处理器状态切换到新进程的处理器状态。切换的过程包括保存、恢复栈信息和寄存器信息。
 
     其中各种进程之间的调度又有很多方法,主要有先进先出、时间片轮转等方式,这里就不具体分析,有兴趣的可以自行查阅相应的调度方法。
 
     下面来看下具体实验过程:
 
      

 
                                    图5.相关指令操作,创建文件
 
      
 
                                    图6.文件内部修改处
 
       

 
                                  图7.文件内部修改处
 
      
 
                                 图8.调试启动
 
      
 
                               图9.设置断点
      
 
                               图10.查看context_switch处相关点代码
     
 
                                 图11.中途的代码调试过程
 
         总结:从上面可以看出,Linux系统的进程切换的一般执行过程是这样的,从进程X转向进程Y的过程是这样的。
 
         1.正在运行的用户态进程X 。
 
         2.发生中断——save cs:eip/esp/eflags(current)to kernel stack,then load cs:eip(entry of a specific ISR) and 
 
            ss:esp(point tokernel stack)。
 
         3.SAVE_ALL//保存现场。
 
         4.中断处理过程中或中断返回前调用了schedule(),其中的switch_to做了关键的进程上下文切换。
 
         5.标号1之后开始运行用户态进程Y(这里Y曾经通过以上步骤被切换出去过因此可以从标号1继续执行)。
 
         6.restore_all  //恢复现场。
 
         7.iret- pop cs:eip/ss:esp/eflags from kernel stack //恢复。
 
         8.继续运行用户态进程Y
 
        好了,从上面可以看到,整个Linux系统的进程切换的执行流程就是这个样子的!
 


 


(责任编辑:IT)
------分隔线----------------------------