LINUX源代码阅读报告
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
进程调度代码分析
——关于LINUX源代码中进程调度部分的读书报告
在多进程的操作系统中,进程调度是一个全局性、关键性的问题,它对系统的总体设计、系统的的实现、功能设置以及各个方面的性能都有着决定性的影响。根据调度的结果所作的进程切换的速度,也是衡量一个操作系统性能的重要指标。进程调度机制的设计,还对系统的复杂性有着极大的影响,常常会由于实现的复杂程度而在功能与性能方面作出必要的权衡和让步。
一.进程调度的基本原理
一个好的系统的进程调度机制,应当考虑以下几个问题:
(1)公平:保证每个进程得到合理的CPU 时间。
(2)高效:使CPU 保持忙碌状态,即总是有进程在CPU 上运行。
(3)响应时间:使交互用户的响应时间尽可能短。
(4)周转时间:使批处理用户等待输出的时间尽可能短。
(5)吞吐量:使单位时间内处理的进程数量尽可能多。
显然,要同时满足这几个方面是不可能的,所以,形成一个操作系统就必须在这几个方面中做出权衡和必要的取舍,从而确定自己的调度算法。例如:UNIX采用动态优先数调度,5.3BSD采用舵机反馈队列调度,windows采用抢先多任务调度。
为了满足以上五个方面,设计一个进程调度机制时要考虑以下问题:
(1)调度的时机:在什么情况什么时候进行调度;
(2)调度的“政策”(policy):根据什么准则挑选下一个进入运行的进程;
(3)调度的方式:是抢占的,还是非抢占的。
对于一个进程,其状态转换关系图具体如下所示:
Linux 的调度程序是一个叫Schedule()的函数,这个函数被调用的频率很高,由它来决定是否要进行进程的切换,如果要切换的话,切换到哪个进程等。即调度时机:Linux 调度时机主要有:
(1)进程状态转换的时刻:进程终止、进程睡眠;
(2)当前进程的时间片用完时(current->counter=0);
(3)设备驱动程序;
(4)进程从中断、异常及系统调用返回到用户态时。
时机1,进程要调用sleep()或exit()等函数进行状态转换,这些函数会主动调用调度程序进行进程调度。
时机2,由于进程的时间片是由时钟中断来更新的,因此,这种情况和时机4 是一样的。
时机3,当设备驱动程序执行长而重复的任务时,直接调用调度程序。在每次反复循环中,驱动程序都检查need_resched 的值,如果必要,则调用调度程序schedule()主动放弃CPU。
时机4,不管是从中断、异常还是系统调用返回,最终都调ret_from_sys_call(),由这个函数进行调度标志的检测,如果必要,则调用调用调度程序。考虑到效率,则必须从系统调用返回时要调用调度程序。从系统调用返回意味着要离开内核态而返回到用户态,而状态的转换要花费一定的时间,因此,在返回到用户态前,系统把在内核态该处理的事全部做完。
在操作系统课程当中,我们已经了解到了六种调度算法,下面进行简单的介绍:
A.先到先服务(first come ,first served, FCFS)
B.最短作业优先调度(shortest-job-first, SJF)
(a)抢占的SJF;
(b)非抢占的SJF;
C.优先权调度(priority-scheduling algorithm)
D.轮转法(round-robin, RR)
E.多级队列调度(multilevel queue-scheduling algorithm)
F.多级反馈队列调度(multilevel feedback queue scheduling algorithm)
在实时系统中,广泛采用抢占调度方式,特别是对于那些要求严格的实时系统。因为这种调度方式既具有较大的灵活性,又能获得很小的调度延迟;但是这种调度方式也比较复杂。二.LINUX进程调度的源代码分析
在LINUX内核中,所有的调度方式基本上都是从UNIX继承下来的以优先级为基础的调度。内核为系统中的每个进程计算出一个反映其运行资格的权值(即优先级),然后挑选其权值最高的进程投入运行。在运行过程中,当前进程的资格随时间而递减,从而在下一次调度的时候原来资格较低的进程可能就更有资格运行了。到所有的权值都为0时,则重新计算所有进程的资格。
为了适应各种不同的应用的需要,内核在此基础上实现了三种不同的调度策略:SCHED_FIFO、SCHED_RR以及SCHED_OTHER。每个进程都有自己适用的调度算法,并且,进程还可以通过系统调用sched_setscheduler()设定自己使用的调度政策。其中SCHED_FIFO适合于时间性要求比较强,但每一次运行所需的时间比较短的进程,实时的应用大都具有这样的特点。SCHED_RR适合每次运行需要时间很长的进程。SCHED_OTHER则是传统的调度算法,比较适合于交互式的分时应用。
下面是对内核代码中的sched.c进行分段的分析:
由于这个函数被非常频繁的执行,因而此函数中使用了较多的goto语句,运行效率较高。在task_struct结构中有两个mm_struct指针。一个是mm,指向代表着进程的虚拟内存空间的数据结构。如果当前进程为内核线程,则无用户空间,mm指针为0,运行时就要暂时借用在它之前运行的那个进程的active_mm。对于当前进程,在进入schedule()时,其active_mm一定不为0(515行)。
Schedule()只能由进程在内核中主动调用,或者在当前进程从系统空间返回用户空间之前被动的调用,而不能在一个中断服务程序的内部发生。那么,如果在执行过程中发生了嵌套中断,那么当从嵌套的中断返回时要调用schedule()吗?其实,从嵌套的中断返回时不会调用schedule(),因为此时的中断返回并不是返回到用户空间。如果在某个中断服务程序内部调用schedule(),则需接着看scheduling_in_interrupt():