【进程管理】进程的调度与切换

在多进程的OS中,进程调度是一个全局性的,关键性的问题;它对系统的总体设计,实现,功能设置,以及各方面的性能有着决定性的影响;根据调度结果所作出的进程切换速度,也是衡量一个操作系统性能的重要指标;一个好的系统的进程调度进制,要兼顾着三种不同应用的需要:

(1)交互式应用;它偏重系统的响应速度,使用一个系统的各个用户(或各个应用程序)都能感觉到自己是独占一个OS;当响应延迟超过150ms,使用者就会明显的感觉到了;

(2)批处理应用;批处理应用往往是作为后台作业运行的,所以对响应速度并无要求,但是完成一个作业所需要的时间是一个重要因素;

(3)实时应用;不但要考虑响应时间(某个事件发生到系统对此作为反应开始执行有关程序之间所需要的时间),还要考虑执行时间(能否在规定时间内执行完);最重要的是要对程序执行要有可预测性;


首先自愿调度随时都可以进行;一个进程可以通过schedule()启动一次调度,当然也可以在schedule()之前设置本进程的状态为TASK_INTERRUPTABLE或TASK_UNINTERRUPTABLE,暂时放弃运行进入睡眠;在用户空间可通过系统调用pause()来达到同样的目的;也可以为暂时放弃CPU设置时间,如内核中有schedule_timeout();或用户空间的系统调用nanosleep()(sleep()是库函数,最终也是调用这个系统调用);内核中放弃运行是不可见的,而用户空间爱你放弃运行是可见的;同时调度也是可以非自愿的,即强制的发生在每次从系统调用前夕,以及每次从中断或异常处理程序返回前夕(这些返回用户空间意味着当CPU运行在用户空间发生的中断或异常才是可强制调度的,必要条件,这个限制也使得如果内核在系统空间多发生几次中断,不返回用户空间,可导致linux的实时性不好);在task_struct中有一个字段need_resched为非0时,会发生调度(可通过系统调用来设置,或调用中因某种原因受阻,或此进程运行时间太长了或唤醒了其他进程);


Linux内核的调度方式可以说是有条件的剥夺;当进程处于用户空间时,不管是否自愿,一旦有必要,内核就会剥夺它调度而使其他进程调度;但是到了系统空间,一直要到返回到用户空间的前夕才会剥夺其运行;调度政策主要是以优先级为基础的调度,挑选优先级最高的进程运行,而此进程会随着运行的时间而资格降低,到下一次调度的时候,可能原来优先级较低的进程就可运行了;到所有进程的优先级均变为0时,再重新计算一次所有进程的资格;但是还以通过sched_setscheduler设置其他三种调度政策(SCHED_FIFO适合时间性较强的,运行时间较短的,偏重实时进程,SCHED_RR轮流,适合运行时间比较长的进程,SCHED_OTHER适合传统的调度政策,交互式应用);尽管各个进程有自己不同的调度方式,但实际上最后都是考虑到不同的调度方式,归结到各个进程的权值上;

(1)在schedule()中,一个进程的active_mm必不为空(若是内核线程,需借用之前运行的那个进程);调度不可能在中断服务程序内部发生,内部,它只能设置need_resched字段来达到这种要求,嵌条中断的中断服务程序并不是返回到用户空间,当CPU从系统空间返回到用户空间时则一定是离开了具体的中断服务程序;若在中断服务程序发生了调度,内核会用BUG在/var/log/messages上添上一个错误信息;;

(2)接着查看softirq_active()和softirq_mask()查看是否有软中断在运行,有就会去执行;接着是一个sched_data指向一个schedule_data数据结构,用来保存下一次调度时使用的信息;其中cycles_t记录调度发生的时间;

(3)下面就要涉及到可执行进程队列了;首先是对SCHED_RR进程处理,对此类进程每一个相同优先级的进程分配一个时间片,然后依次轮转;prev(开始指向当前进程)->counter代表着当前进程的运行时间分配额,其数值在每次时钟中断都要递减,对于SCHED_RR,递减到0时要从可执行队列runqueue中当前位置移到队列的末尾,同时恢复其最初的时间配额,其中NICE_TO_TICKS根据系统时钟的精度将进程的优先级别换算成可运行的时间配额,通过move_last_runqueue()将可执行队列中的当前位置移到队列的末尾,这也就意味着如果队列中没有一个资格更高的进程,但是有一个资格与它资格相同的进程,那么由于它靠前,所以会被选中;返回到刚才断掉的标号处;

(4)如果当前的进程不是TASK_RUNNING(如do_exit()进程状态改为了TASK_ZOMBE,sys_wait4()进程状态改为了TASK_INTERRUPTABLE),所以用del_from_runqueue()将它从可执行队列中撤下来;对于TASK_INTERRUPTABLE,如果对该进程有信号处理时,要将它状态改为TASK_RUNNING,若没有信号处理,就要把它给del_from_runqueue(),TASK_UNINTERRUPTABLE是对信号没有影响的;对于TASK_RUNNING,保持不变;将prev->need_resched变为0,来挑选一个进程来运行;

(5)next总是指向已知的最佳候选进程,是idle进程,c权值最低为-1000,表示没有其他进程运行时,它才运行;如果当前进程仍需要运行,则next总是指向它,就算出c;然后遍历runqueue队列中每个进程,使用goodness计算出来的进程运行权值,比较大小,选出最佳进程;

(6)当前进程的权值是通过goodness()计算出来的;如果一个进程调用了sched_yield()表示自愿放弃,那么它的权值为-1;对于SCHED_OTHER类型的进程,其权值根据其剩下的时间配额,为0,则返回该权值0,若没用完了,还根据它的优先级nice(取值是19~-20,其中越小为为越高,只有特权用户可把nice设为0,而20-nice,则转掉了它的方向成为1~40,这样也就变成了nice越大,优先级越高),因此权值是在时间配额尚未运完时是二者之和;若是一个内核线程或者其用户空间与当前进程相同,因而无需切换用户空间,为给它的权值nice加1;对于事实进程(SCHED_FIFO或SCHED_RR),则有另一种正向优先级,进程的权值至少为1000,由此可见实时进程的nice值,是与其优先级无关的,但是对SCHED_RR,是跟它的时间配额大小有关的,而对于rt_prioity对于实时进程之间的权值也是很重要的,sched_setscheduler()可设置rt_prioity;

(7)若调度队列中所有进程的权值都为0,一定是SCHED_OTHER类型的进程,不存在SCHED_FIFO或SCHED_RR类型的进程(他们至少为1000);若当前选择的进程权值为0,则重新计算所有进程(不局限于可执行队列)的时间分配额,根据p->counter(原有的时间配额)和p->nice;然后再回到repeat_schedule出重新挑选;

(8)现在挑选好了进程,如果挑选出来的进程就是当前的进程,那就返回了,如果此时need_resched又变为非0,说明一定是发生了中断,情况又变化了,又要调度了,所以再一次最开始的tq_scheduler_back再来一次;否则挑选出来的进程是不同的,那就要切换了;一个对用户虚存空间的处理,还要对进程的切换switch_to();

(9)首先是对用户虚存空间的处理,打开一个scope是因为堆栈中有了mm指向新进程next(最佳调度进程)的mm_struct,而oldmm为当前进程的active_mm;如果next也是一个不具备用户空间的内核线程,那么就要指向这个active_mm,不需要进行swith_mm(),而且不用这个用户空间,还是使用它的所有进程相同的系统空间;那么如果有自己的mm,那么就要swith_mm()了,将CR3窒息那个新进程页面目录的起始物理地址(CPU在系统空间运行时,所有进程的页面目录与系统空间相对应的目录项总是一致的,系统空间是共享的);

(10)最后到进程切换的关头,就是调用switch_to(),是堆栈的切换,切换点在switch_to(),切换后,新进程的返回用户空间的路线是共同的,不同的上下文取决于新进程的系统空间堆栈了;

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值