美文网首页
理解进程调度时机跟踪分析进程调度与进程切换的过程

理解进程调度时机跟踪分析进程调度与进程切换的过程

作者: 梅花小筑 | 来源:发表于2016-04-17 21:07 被阅读194次

一.进程调度

现代的操作系统是多道的,这必然涉及到进程的调度,调度需要许多的调度算法。
1.需要多种调度算法的理由:

  • 不同的进程对于计算机的资源(CPU、IO等)的需求是不同的,比如有些需要频繁的IO,有些需要一直占CPU。那么他们之间的调度成为提升计算机效率的关键。
  • 不同进程还可以分类成批处理进程,例如编译程序、科学计算等,这类进程特点是不需要和用户交互,而是直接在后台埋头苦干,还有一点就是不需要实时。还可以分类成实时进程,例如视频音频、机械控制等,特点是要及时稳定的响应。还可以分类为交互式进程,例如shell、文本编辑程序、图形应用程序等,特点就是经常和用户交互,因此会长时间的阻塞,不过响应还是要快。
    基于这两点,linux对不同进程使用不同调度策略。调度策略就是操作系统从进程就绪队列中选一个使之获得CPU执行权的算法。那么操作系统具体是怎么做的呢?就是它根据某些信息由算法计算出一个值,这个值就代表了进程的优先级。
    2.进程调度的时机:
    一个进程调用schedule()函数会发生进程调度(理解:切换到另一个进程),该函数在内核态,而且不是系统调用,因此用户态程序无法主动调用它,只能被调度。
  • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();比如sleep后,立即会调用schedule。
  • 内核线程(只有内核态没有用户态的特殊进程,虽然不会系统调用,但是仍然可以有时钟中断、IO中断等)可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
  • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。
    3.进程的切换
    为了控制进程的执行,内核必须有能力挂起正在CPU上执行的进程,并恢复以前挂起的某个进程的执行,这叫做进程切换、任务切换、上下文切换;
    挂起正在CPU上执行的进程,与中断时保存现场是不同的,中断前后是在同一个进程上下文中,只是由用户态转向内核态执行;
    进程上下文包含了进程执行需要的所有信息,包括:
  • 用户地址空间:�包括程序代码,数据,用户堆栈等
  • 控制信息�:进程描述符,内核堆栈等
  • 硬件上下文(注意中断也要保存硬件上下文只是保存的方法不同)
    schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文切换
    next = pick_next_task(rq, prev);//进程调度算法都封装这个函数内部
    context_switch(rq, prev, next);//进程上下文切换
    switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程。使得当前进程的内核堆栈切换为下一个要执行进程的内核堆栈;然后切换到下一个进程的EIP。

二.简单跟踪 Linux 系统切换过程.

1.首先在schedule,pick_next_task,context_switch,switch_to等函数处设置断点,如下图.

设置断点

中断过程如下:

1 2 3

三.具体分析

下面具体分析一下switch_to的代码:

简单看一下这个宏和函数的被调用关系:

schedule() --> context_switch() --> switch_to --> __switch_to()

这里面,schedule是主调度函数,涉及到一些调度算法,这里不讨论。当schedule()需要暂停A进程的执行而继续B进程的执行时,就发生了进程之间的切换。进程切换主要有两部分:1、切换全局页表项;2、切换内核堆栈和硬件上下文。这个切换工作由context_switch()完成。其中switch_to和__switch_to()主要完成第二部分。更详细的,__switch_to()主要完成硬件上下文切换,switch_to主要完成内核堆栈切换。

阅读switch_to时请注意:这是一个宏,不是函数,它的参数prev, next, last不是值拷贝,而是它的调用者context_switch()的局部变量。局部变量是通过%ebp寄存器来索引的,也就是通过n(%ebp),n是编译时决定的,在不同的进程的同一段代码中,同一局部变量的n是相同的。在switch_to中,发生了堆栈的切换,即ebp发生了改变,所以要格外留意在任一时刻的局部变量属于哪一个进程。关于__switch_to()这个函数的调用,并不是通过普通的call来实现,而是直接jmp,函数参数也并不是通过堆栈来传递,而是通过寄存器来传递。

四、实验总结:

通过实验可知schedule()函数用来选择一个新的进程来运行,并调用context_switch()进行上下文的切换,这个宏调用switch_to()来进行关键上下文切换,其中pick_next_task()函数封装了进程调度算法。中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

相关文章

网友评论

      本文标题:理解进程调度时机跟踪分析进程调度与进程切换的过程

      本文链接:https://www.haomeiwen.com/subject/iywolttx.html