问题描述:
请问各位大虾
Windows进程调度的方式有那几种?
谢谢了
解析:
高级调度:又称作业调度。其主要功能是根据一定的算法,从输人的一批作业中选出若干个作业,分配必要的资源,如内存、外设等,为它建立相应的用户作业进程和为其服务的系统进程(如输人、输出进程),最后把它们的程序和数据调人内存,等待进程调度程序对其执行调度,并在作业完成后作善后处理工作。
低级调度:又称进程调度。其主要功能是根据一定的算法将CPU分派给就绪队列中的一个进程。执行低级调度功能的程序称做进程调度程序,由它实现CPU在进程间的切换。进程调度的运行频率很高,在分时系统中往往几十毫秒就要运行一次。进程调度是 *** 作系统中最基本的一种调度。在一般类型的 *** 作系统中都必须有进程调度,而且它的策略的优劣直接影响整个系统的计能。
中级调度:又称交换调度。为了使内存中同时存放的进程数目不至于太多,有时就需要把某些进程从内存中移到外存上,以减少多道程序的数目,为此设立了中级调度。特别在采用虚拟存储技术的系统或分时系统中,往往增加中级调度这一级。所以中级调度的功能是在内存使用情况紧张时,将一些暂时不能运行的讲程从内存对换到外存上等待。当以后内存有足够的空闲空间时,再将合适的进程重新换人内存,等待进程调度。引人中级调度的主要目的是为了提高内存的利用率和系统吞吐量。它实际上就是存储器管理中的对换功能
下面说说进程调度的策略问题(引用参考资料内容):
首先硬件机制上如何保证 *** 作系统的内核调度进程可以一定的时机可以获得CPU,来进行进程调度.?
通常我们会在软件层次上找答案.其实,是通过在CPU的硬件处理机制上实现的.CPU在执行完每个指令的周期后回扫描CPU的内部的一个中断寄存器,查询是否存在中断发生,若没有,则继续执行指令若有,则保存当前的CPU工作环境,跳转到中断服务列程,CPU执行中断服务程序,在推出中断后,跳转到内核调度程序(这是个内核程序,但是是对所有的进程共享的,包括用户进程)此时,内核调度程序占据CPU,进行进程的调度,以决定下个将占用CPU的进程.
接下来就要谈谈什么时候会需要进行进程调度?
在教科书书说到的有几种情况:1时间片到,即每个进程所分配的时间片用完后,要跳转到调度程序2 占用CPU的当前运行进程提出I/O *** 作,发起对内核的系统调用时,在系统调用结束后,跳转到调度程序3 我自己的想法: 当前运行进程对所有内核系统调用的结束时都要跳转到调度程序,根据当前的调度信息来决定下一个可以占用CPU的进程. 我所指的系统调用也包括中断列程.不过对与具体的调度时机,很多书上都写的不清不楚,真不知道他们不懂,还是不屑于写出来告诉我们. 其实除了在大多数硬件中断的触发后跳转到调度程序, 每个时钟中断发生的时候,我觉得都需要跳转到调度程序.(在进入时钟中断列程中,要对进程表中的所有的进程的调度信息进行更新和对各个进程队列的处理),对更新后的进程信息进行处理以决定调度哪个进程. 通常的教科书中都将硬件物理的处理机制和软件的调度处理机制分开,在物理和逻辑两个层次上分开谈,不利于我们理解.最好是把这两个结合起来理解进程调度的工作机制.目前需要解决的是:在什么时候需要内核调度程序占据CPU来调度? 至于调度的算法那就是逻辑层次上要考虑的东西.
其实看了这么多,我也有了些小论文的想法, 因为做的方向是应用在电子电力电路上的嵌入系统控制.该应用对嵌入 *** 作系统的性能就有些特殊的需求:首先体积要小,速度快内核就要小,进程调度要实现抢占式任务调度,且调度切换要快.它的进程调度与通用 *** 作系统的进程调度不同,这是因为它们的要求不一样,嵌入式通常是要求是实时,且严格的讲在电路上的控制系统应该是硬实时,而不象通用系统是非实时,或者是软实时.这跟它们对实时性的要求不同.所以我初步定个题目 "嵌入式系统和通用系统在进程调度上比较和分析,并针对特定的电路控制嵌入实时系统提出一个调度策略". 我想我从明天开始就要准备这方面的资料,分析分析,比较比较,弄篇小论文出来,,不然我都快给它凡死了.
*** 作系统-----进程调度
[/color][color=Gray][/color][color=Blue][/color][color=Lime] 要求:实现按优先级与时间片相结合的进程调度算法
内容:
1:设计进程控制快,进程队列结构(包括:就绪队列,等待队列,运行队列)等必要的数据结构。
2:模拟 *** 作系统进程调度的功能,编写进程调度程序,模拟的处理机分派程序,进程等待函数和进程唤醒函数。
3:编写用户程序,创建6个用户进程。
进程调度的设计方法
1。数据结构
(1)优先级与时间片的设计
◆进程因等待放弃CPU时,优先级置为1(高优先级)
◆进程因时间片到放弃CPU时,优先级置为0(低优先级)
◆优先1对应时间片4;优先级0对应时间片10。
(2)进程控制块(PCB)的内容
进程标识3---9
进程优先级 0,1
进程优先级 0,1
进程等待时间 20
链接指针
2:程序算法
(1)PCB结构,变量与主程序
struct PCB
{
int pname
int pri
int runtime
int waitting
struct PCB*next
}
pcb[7]
struct PCB*running,ready,wait
int sin=0
main()
{ 创建PCB[3]--PCB[9]并插入ready队列;/*pname分别为3--9,
pri=0,runtime=10,waittime=0 */
for()/*系统程序,完成初始化和处理机分派功能 */
{cast{sig=0:swtch
sig=1:waiter
sig=3:proc3
sig=4:proc4
sig=5:proc5
sig=6:proc6
sig=7:proc7
sig=8:proc8
sig=9:proc9}
}
}
(2) 进程调度程序
swtch()
{
while(ready==NULL)wakeup()
移出就绪队列第一个PCB
送running指针;
若pri=1,则runntime=4,否则runtime=10
将running→pname 送sig
}
(3) 将进程等待函数
wait()
{将运行进程插入wait队列,优先数置1;
sig=0
}
(4) 进程唤醒函数
wakeup()
{
将wait队列中所有的PCB中waittime减1;
将wait队列中的所有的waittime=0的PCB揭除;
插入到ready队列中第一个优先级为0的PCB前面
}
1,SCHED_OTHER 分时调度策略,
2,SCHED_FIFO实时调度策略,先到先服务
3,SCHED_RR实时调度策略,时间片轮转
实时进程将得到优先调用,实时进程根据实时优先级决定调度权值,分时进程则通过nice和counter值决定权值,nice越小,counter越大,被调度的概率越大,也就是曾经使用了cpu最少的进程将会得到优先调度。
SHCED_RR和SCHED_FIFO的不同:
当采用SHCED_RR策略的进程的时间片用完,系统将重新分配时间片,并置于就绪队列尾。放在队列尾保证了所有具有相同优先级的RR任务的调度公平。
SCHED_FIFO一旦占用cpu则一直运行。一直运行直到有更高优先级任务到达或自己放弃。
如果有相同优先级的实时进程(根据优先级计算的调度权值是一样的)已经准备好,FIFO时必须等待该进程主动放弃后才可以运行这个优先级相同的任务。而RR可以让每个任务都执行一段时间。
相同点:
RR和FIFO都只用于实时任务。
创建时优先级大于0(1-99)。
按照可抢占优先级调度算法进行。
就绪态的实时任务立即抢占非实时任务。
所有任务都采用linux分时调度策略时。
1,创建任务指定采用分时调度策略,并指定优先级nice值(-20~19)。
2,将根据每个任务的nice值确定在cpu上的执行时间(counter)。
3,如果没有等待资源,则将该任务加入到就绪队列中。
4,调度程序遍历就绪队列中的任务,通过对每个任务动态优先级的计算(counter+20-nice)结果,选择计算结果最大的一个去运行,当这个时间片用完后(counter减至0)或者主动放弃cpu时,该任务将被放在就绪队列末尾(时间片用完)或等待队列(因等待资源而放弃cpu)中。
5,此时调度程序重复上面计算过程,转到第4步。
6,当调度程序发现所有就绪任务计算所得的权值都为不大于0时,重复第2步。
所有任务都采用FIFO时,
1,创建进程时指定采用FIFO,并设置实时优先级rt_priority(1-99)。
2,如果没有等待资源,则将该任务加入到就绪队列中。
3,调度程序遍历就绪队列,根据实时优先级计算调度权值(1000+rt_priority),选择权值最高的任务使用cpu,该FIFO任务将一直占有cpu直到有优先级更高的任务就绪(即使优先级相同也不行)或者主动放弃(等待资源)。
4,调度程序发现有优先级更高的任务到达(高优先级任务可能被中断或定时器任务唤醒,再或被当前运行的任务唤醒,等等),则调度程序立即在当前任务堆栈中保存当前cpu寄存器的所有数据,重新从高优先级任务的堆栈中加载寄存器数据到cpu,此时高优先级的任务开始运行。重复第3步。
5,如果当前任务因等待资源而主动放弃cpu使用权,则该任务将从就绪队列中删除,加入等待队列,此时重复第3步。
所有任务都采用RR调度策略时
1,创建任务时指定调度参数为RR,并设置任务的实时优先级和nice值(nice值将会转换为该任务的时间片的长度)。
2,如果没有等待资源,则将该任务加入到就绪队列中。
3,调度程序遍历就绪队列,根据实时优先级计算调度权值(1000+rt_priority),选择权值最高的任务使用cpu。
4,如果就绪队列中的RR任务时间片为0,则会根据nice值设置该任务的时间片,同时将该任务放入就绪队列的末尾。重复步骤3。
5,当前任务由于等待资源而主动退出cpu,则其加入等待队列中。重复步骤3。
系统中既有分时调度,又有时间片轮转调度和先进先出调度
1,RR调度和FIFO调度的进程属于实时进程,以分时调度的进程是非实时进程。
2,当实时进程准备就绪后,如果当前cpu正在运行非实时进程,则实时进程立即抢占非实时进程。
3,RR进程和FIFO进程都采用实时优先级做为调度的权值标准,RR是FIFO的一个延伸。FIFO时,如果两个进程的优先级一样,则这两个优先级一样的进程具体执行哪一个是由其在队列中的未知决定的,这样导致一些不公正性(优先级是一样的,为什么要让你一直运行?),如果将两个优先级一样的任务的调度策略都设为RR,则保证了这两个任务可以循环执行,保证了公平。 调度程序运行时,要在所有处于可运行状态的进程之中选择最值得运行的进程投入运行。选择进程的依据是什么呢?在每个进程的task_struct 结构中有这么四项:
policy, priority , counter, rt_priority
这四项就是调度程序选择进程的依据.其中,policy是进程的调度策略,用来区分两种进程-实时和普通;priority是进程(实时和普通)的优先级;counter 是进程剩余的时间片,它的大小完全由priority决定;rt_priority是实时优先级,这是实时进程所特有的,用于实时进程间的选择。
首先,Linux 根据policy从整体上区分实时进程和普通进程,因为实时进程和普通进程度调度是不同的,它们两者之间,实时进程应该先于普通进程而运行,然后,对于同一类型的不同进程,采用不同的标准来选择进程:
对于普通进程,Linux采用动态优先调度,选择进程的依据就是进程counter的大小。进程创建时,优先级priority被赋一个初值,一般为0~70之间的数字,这个数字同时也是计数器counter的初值,就是说进程创建时两者是相等的。字面上看,priority是“优先级”、counter是“计数器”的意思,然而实际上,它们表达的是同一个意思-进程的“时间片”。Priority代表分配给该进程的时间片,counter表示该进程剩余的时间片。在进程运行过程中,counter不断减少,而priority保持不变,以便在counter变为0的时候(该进程用完了所分配的时间片)对counter重新赋值。当一个普通进程的时间片用完以后,并不马上用priority对counter进行赋值,只有所有处于可运行状态的普通进程的时间片(p->;;counter==0)都用完了以后,才用priority对counter重新赋值,这个普通进程才有了再次被调度的机会。这说明,普通进程运行过程中,counter的减小给了其它进程得以运行的机会,直至counter减为0时才完全放弃对CPU的使用,这就相对于优先级在动态变化,所以称之为动态优先调度。至于时间片这个概念,和其他不同 *** 作系统一样的,Linux的时间单位也是“时钟滴答”,只是不同 *** 作系统对一个时钟滴答的定义不同而已(Linux为10ms)。进程的时间片就是指多少个时钟滴答,比如,若priority为20,则分配给该进程的时间片就为20个时钟滴答,也就是20*10ms=200ms。Linux中某个进程的调度策略(policy)、优先级(priority)等可以作为参数由用户自己决定,具有相当的灵活性。内核创建新进程时分配给进程的时间片缺省为200ms(更准确的,应为210ms),用户可以通过系统调用改变它。
对于实时进程,Linux采用了两种调度策略,即FIFO(先来先服务调度)和RR(时间片轮转调度)。因为实时进程具有一定程度的紧迫性,所以衡量一个实时进程是否应该运行,Linux采用了一个比较固定的标准。实时进程的counter只是用来表示该进程的剩余时间片,并不作为衡量它是否值得运行的标准,这和普通进程是有区别的。上面已经看到,每个进程有两个优先级,实时优先级就是用来衡量实时进程是否值得运行的。
这一切看来比较麻烦,但实际上Linux中的实现相当简单。Linux用函数goodness()来衡量一个处于可运行状态的进程值得运行的程度。该函数综合了上面提到的各个方面,给每个处于可运行状态的进程赋予一个权值(weight),调度程序以这个权值作为选择进程的唯一依据。
Linux根据policy的值将进程总体上分为实时进程和普通进程,提供了三种调度算法:一种传统的Unix调度程序和两个由POSIX.1b(原名为POSIX.4) *** 作系统标准所规定的“实时”调度程序。但这种实时只是软实时,不满足诸如中断等待时间等硬实时要求,只是保证了当实时进程需要时一定只把CPU分配给实时进程。
非实时进程有两种优先级,一种是静态优先级,另一种是动态优先级。实时进程又增加了第三种优先级,实时优先级。优先级是一些简单的整数,为了决定应该允许哪一个进程使用CPU的资源,用优先级代表相对权值-优先级越高,它得到CPU时间的机会也就越大。
? 静态优先级(priority)-不随时间而改变,只能由用户进行修改。它指明了在被迫和其他进程竞争CPU之前,该进程所应该被允许的时间片的最大值(但很可能的,在该时间片耗尽之前,进程就被迫交出了CPU)。
? 动态优先级(counter)-只要进程拥有CPU,它就随着时间不断减小;当它小于0时,标记进程重新调度。它指明了在这个时间片中所剩余的时间量。
? 实时优先级(rt_priority)-指明这个进程自动把CPU交给哪一个其他进程;较高权值的进程总是优先于较低权值的进程。如果一个进程不是实时进程,其优先级就是0,所以实时进程总是优先于非实时进程的(但实际上,实时进程也会主动放弃CPU)。
当policy分别为以下值时:
1) SCHED_OTHER:这是普通的用户进程,进程的缺省类型,采用动态优先调度策略,选择进程的依据主要是根据进程goodness值的大小。这种进程在运行时,可以被高goodness值的进程抢先。
2) SCHED_FIFO:这是一种实时进程,遵守POSIX1.b标准的FIFO(先入先出)调度规则。它会一直运行,直到有一个进程因I/O阻塞,或者主动释放CPU,或者是CPU被另一个具有更高rt_priority的实时进程抢先。在Linux实现中,SCHED_FIFO进程仍然拥有时间片-只有当时间片用完时它们才被迫释放CPU。因此,如同POSIX1.b一样,这样的进程就象没有时间片(不是采用分时)一样运行。Linux中进程仍然保持对其时间片的记录(不修改counter)主要是为了实现的方便,同时避免在调度代码的关键路径上出现条件判断语句 if (!(current->;;policy&;;SCHED_FIFO)){...}-要知道,其他大量非FIFO进程都需要记录时间片,这种多余的检测只会浪费CPU资源。(一种优化措施,不该将执行时间占10%的代码的运行时间减少到50%;而是将执行时间占90%的代码的运行时间减少到95%。0.9+0.1*0.5=0.95>;;0.1+0.9*0.9=0.91)
3) SCHED_RR:这也是一种实时进程,遵守POSIX1.b标准的RR(循环round-robin)调度规则。除了时间片有些不同外,这种策略与SCHED_FIFO类似。当SCHED_RR进程的时间片用完后,就被放到SCHED_FIFO和SCHED_RR队列的末尾。
只要系统中有一个实时进程在运行,则任何SCHED_OTHER进程都不能在任何CPU运行。每个实时进程有一个rt_priority,因此,可以按照rt_priority在所有SCHED_RR进程之间分配CPU。其作用与SCHED_OTHER进程的priority作用一样。只有root用户能够用系统调用sched_setscheduler,来改变当前进程的类型(sys_nice,sys_setpriority)。
此外,内核还定义了SCHED_YIELD,这并不是一种调度策略,而是截取调度策略的一个附加位。如同前面说明的一样,如果有其他进程需要CPU,它就提示调度程序释放CPU。特别要注意的就是这甚至会引起实时进程把CPU释放给非实时进程。 真正执行调度的函数是schedule(void),它选择一个最合适的进程执行,并且真正进行上下文切换,使得选中的进程得以执行。而reschedule_idle(struct task_struct *p)的作用是为进程选择一个合适的CPU来执行,如果它选中了某个CPU,则将该CPU上当前运行进程的need_resched标志置为1,然后向它发出一个重新调度的处理机间中断,使得选中的CPU能够在中断处理返回时执行schedule函数,真正调度进程p在CPU上执行。在schedule()和reschedule_idle()中调用了goodness()函数。goodness()函数用来衡量一个处于可运行状态的进程值得运行的程度。此外,在schedule()函数中还调用了schedule_tail()函数;在reschedule_idle()函数中还调用了reschedule_idle_slow()。这些函数的实现对理解SMP的调度非常重要,下面一一分析这些函数。先给出每个函数的主要流程图,然后给出源代码,并加注释。
goodness()函数分析
goodness()函数计算一个处于可运行状态的进程值得运行的程度。一个任务的goodness是以下因素的函数:正在运行的任务、想要运行的任务、当前的CPU。goodness返回下面两类值中的一个:1000以下或者1000以上。1000或者1000以上的值只能赋给“实时”进程,从0到999的值只能赋给普通进程。实际上,在单处理器情况下,普通进程的goodness值只使用这个范围底部的一部分,从0到41。在SMP情况下,SMP模式会优先照顾等待同一个处理器的进程。不过,不管是UP还是SMP,实时进程的goodness值的范围是从1001到1099。
goodness()函数其实是不会返回-1000的,也不会返回其他负值。由于idle进程的counter值为负,所以如果使用idle进程作为参数调用goodness,就会返回负值,但这是不会发生的。
goodness()是个简单的函数,但是它是linux调度程序不可缺少的部分。运行队列中的每个进程每次执行schedule时都要调度它,因此它的执行速度必须很快。
//在/kernel/sched.c中
static inline int goodness(struct task_struct * p, int this_cpu, struct mm_struct *this_mm)
{ int weight;
if (p->;;policy != SCHED_OTHER) {/*如果是实时进程,则*/
weight = 1000 + p->;;rt_priority;
goto out;
}
/* 将counter的值赋给weight,这就给了进程一个大概的权值,counter中的值表示进程在一个时间片内,剩下要运行的时间.*/
weight = p->;;counter;
if (!weight) /* weight==0,表示该进程的时间片已经用完,则直接转到标号out*/
goto out;
#ifdef __SMP__
/*在SMP情况下,如果进程将要运行的CPU与进程上次运行的CPU是一样的,则最有利,因此,假如进程上次运行的CPU与当前CPU一致的话,权值加上PROC_CHANGE_PENALTY,这个宏定义为20。*/
if (p->;;processor == this_cpu)
weight += PROC_CHANGE_PENALTY;
#endif
if (p->;;mm == this_mm) /*进程p与当前运行进程,是同一个进程的不同线程,或者是共享地址空间的不同进程,优先选择,权值加1*/
weight += 1;
weight += p->;;priority; /* 权值加上进程的优先级*/
out:
return weight; /* 返回值作为进程调度的唯一依据,谁的权值大,就调度谁运行*/
}
schedule()函数分析
schedule()函数的作用是,选择一个合适的进程在CPU上执行,它仅仅根据'goodness'来工作。对于SMP情况,除了计算每个进程的加权平均运行时间外,其他与SMP相关的部分主要由goodness()函数来体现。
流程:
①将prev和next设置为schedule最感兴趣的两个进程:其中一个是在调用schedule时正在运行的进程(prev),另外一个应该是接着就给予CPU的进程(next)。注意:prev和next可能是相同的-schedule可以重新调度已经获得cpu的进程.
②中断处理程序运行“下半部分”.
③内核实时系统部分的实现,循环调度程序(SCHED_RR)通过移动“耗尽的”RR进程-已经用完其时间片的进程-到队列末尾,这样具有相同优先级的其他RR进程就可以获得CPU了。同时,这补充了耗尽进程的时间片。
④由于代码的其他部分已经决定了进程必须被移进或移出TASK_RUNNING状态,所以会经常使用schedule,例如,如果进程正在等待的硬件条件已经发生,所以如果必要,这个switch会改变进程的状态。如果进程已经处于TASK_RUNNING状态,它就无需处理了。如果它是可以中断的(等待信号),并且信号已经到达了进程,就返回TASK_RUNNING状态。在所以其他情况下(例如,进程已经处于TASK_UNINTERRUPTIBLE状态了),应该从运行队列中将进程移走。
⑤将p初始化为运行队列的第一个任务;p会遍历队列中的所有任务。
⑥c记录了运行队列中所有进程最好的“goodness”-具有最好“goodness”的进程是最易获得CPU的进程。goodness的值越高越好。
⑦遍历执行任务链表,跟踪具有最好goodness的进程。
⑧这个循环中只考虑了唯一一个可以调度的进程。在SMP模式下,只有任务不在cpu上运行时,即can_schedule宏返回为真时,才会考虑该任务。在UP情况下,can_schedule宏返回恒为真.
⑨如果循环结束后,得到c的值为0。说明运行队列中的所有进程的goodness值都为0。goodness的值为0,意味着进程已经用完它的时间片,或者它已经明确说明要释放CPU。在这种情况下,schedule要重新计算进程的counter;新counter的值是原来值的一半加上进程的静态优先级(priortiy),除非进程已经释放CPU,否则原来counter的值为0。因此,schedule通常只是把counter初始化为静态优先级。(中断处理程序和由另一个处理器引起的分支在schedule搜寻goodness最大值时都将增加此循环中的计数器,因此由于这个原因计数器可能不会为0。显然,这很罕见。)在counter的值计算完成后,重新开始执行这个循环,找具有最大goodness的任务。
⑩如果schedule已经选择了一个不同于前面正在执行的进程来调度,那么就必须挂起原来的进程并允许新的进程运行。这时调用switch_to来进行切换。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)