linux进程调度解析

1调度器的启动通常有两种方式:
A. 主动式
在核心应用中直接调用schedule()。这通常发生在因等待核心事件而需要将进程置于挂起(休眠)状态的时候--这时应该主动请求调度以方便其他进程使用CPU。下面就是一个主动调度的例子:
/* 节选自[drivers/input/mousedev.c] mousedev_read() */
add_wait_queue(&list->mousedev->wait, &wait);
current->state = TASK_INTERRUPTIBLE;
while (!list->ready) {
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (signal_pending(current)) {
retval = -ERESTARTSYS;
break;
}
schedule();
}
current->state = TASK_RUNNING; /* 这一句实际上可以省略,因为进程的状态在唤醒过程中就已经恢复到TASK_RUNNING了 */
remove_wait_queue(&list->mousedev->wait, &wait);
其过程通常可分为四步:
将进程添加到事件等待队列中;
置进程状态为TASK_INTERRUPTIBLE(或TASK_UNINTERRUPTIBLE);
在循环中检查等待条件是否满足,不满足则调用schedule(),满足了就退出循环;
将进程从事件等待队列中删除。
从"调度器工作流程"中我们知道,调度器会将处于休眠状态的进程从就绪队列中删除,而只有就绪队列中的进程才有可能被调度到。将该进程重新放到就绪队列中的动作是在事件发生时的"唤醒"过程中完成的。在以上所示的鼠标驱动中,鼠标中断将调用mousedev_event()函数,该函数的最后就会使用wake_up_interruptible()唤醒等待鼠标事件的所有进程。wake_up_interruptible()将最终调用try_to_wake_up()函数:
/* 节选自[kernel/sched.c] */
static inline int try_to_wake_up(struct task_struct * p, int synchronous)
{
unsigned long flags;
int success = 0;
spin_lock_irqsave(&runqueue_lock, flags);
p->state = TASK_RUNNING;
if (task_on_runqueue(p))
goto out;
add_to_runqueue(p); /* 添加到就绪队列中 */
if (!synchronous || !(p->cpus_allowed & (1 << smp_processor_id())))
reschedule_idle(p); /* 这种情况下调用wake_up(),synchronous总为0,此时,*/
/* 如果本CPU不适合运行该进程,则需要调用reschedule_idle()寻找合适的CPU */
success = 1;
out:
spin_unlock_irqrestore(&runqueue_lock, flags);
return success;
}
这时启动schedule()就是被动的了。 

 

2 进程调度方法

一〉调度策略

调度策略考虑的几个因素:

1>IO型任务与cpu密集型任务

   linux为IO密集型任务提供高优先级,cpu密集型任务提供低优先级,使得IO密集型任务在IO完成时得到迅速反映

2>进程优先级:

linux实施了动态优先级调度策略。有两种优先级参数:nice value和 real time 优先级。nice value由用户指定,范围在-19--20之间,real time优先级从0--99。拥有实时优先级的进程比基于nice value的进程优先级高,用于需要高度实时性的进程。基于这些优先级参数,linux在运行时动态调整进程的优先级

3)时间片

进程连续执行的一段时间称为时间片。长的时间片使得系统的交互性降低,过短的时间片增加了进程切换的开销,同时不利于cache的作用发挥。linux给高优先级的进程较长的时间片,低优先级的进程较短的时间片。

二〉数据结构

runqueue结构维护了一个cpu上的所有进程的信息。任何进程只属于一个runqueue。runqueue中包含active的优先级队列和iexpire优先级队列。对renqueue进行读写操作时,需要先对之加锁。

优先级对列数据结构为

struct prio_array{

 int nr_active; //该结构中所有进程的个数

 unsigned long bitmap[BITMAP_SIZE];//优先级位图,用于快速查找下一个运行的进程

struct list_head queue[MAX_PRIO];//优先级对列向量。向量中的每个元素为对应优先级的进程队列头

};

 

三种调度策略
(1)SCHED_OTHER。SCHED_OTHER是面向普通进程的时间片轮转策略。采用该策略时,系统为处于TASK_RUNNING状态的每个进程分配一个时间片。当时间片用完时,进程调度程序再选择下一个优先级相对较高的进程,并授予CPU使用权。
(2)SCHED_FIFO。SCHED_FIFO策略适用于对响应时间要求比较高,运行所需时间比较短的实时进程。采用该策略时,各实时进程按其进入可运行队列的顺序依次获得CPU。除了因等待某个事件主动放弃CPU,或者出现优先级更高的进程而剥夺其CPU之外,该进程将一直占用CPU运行。
(3)SCHED_RR。SCHED_RR策略适用于对响应时间要求比较高,运行所需时间比较长的实时进程。采用该策略时,各实时进程按时间片轮流使用CPU。当一个运行进程的时间片用完后,进程调度程序停止其运行并将其置于可运行队列的末尾。

3进程调度依据
Linux只有一个可运行队列,处于TASK_RUNNING状态的实时进程和普通进程都加入到这个可运行队列中。Linux的进程调度采用了动态优先级和权值调控的方法,既可实现上述三种调度策略,又能保证实时进程总是比普通进程优先使用CPU。描述进程的数据结构task_struct中用以下几个数据作为调度依据:
Struct task_struct {
    ……
    volatile long need_resched;  /*是否需要重新调度*/
  long counter;  /*进程当前还拥有的时间片*/
long nice;  /*普通进程的动态优先级,来自UNIX系统*/
unsigned long policy; /*进程调度策略*/
unsigned long rt_priority;  /*实时进程的优先级*/
……
};
counter的值是动态变化的,进程运行时,每一个时钟滴答后,其值减1。当counter值为0时,表示该进程时间片已用完,该进程回到可运行队列中,等待再次调度。
为保证实时进程优于普通进程,Linux采取加权处理法。在进程调度过程中,每次选取下一个运行进程时,调度程序首先给可运行队列中的每个进程赋予一个权值weight。普通进程的权值就是其counter和优先级nice的综合,而实时进程的权值是它的rt_priority的值加1000,确保实时进程的权值总能大于普通进程。调度程序检查可运行队列中所有进程的权值,选取权值最大者作为下一个运行进程,保证了实时进程优先于普通进程获得CPU。Linux使用内核函数goodness()对进程进行加权处理:
Static inline goodness
(struct task_struct * pint this_cpu, struct mm_struct *this_mm)
{
  Int weight;
  Weight=-1;
/*判断如果任务的调度策略被置为SCHED_YIELD的话,则置权值为-1,返回。
系统调用SCHED_YIELD表示为“礼让”进程,其权值为最低*/
 If (p->policy & SCHED_YIELD)
        goto out;
/*先对普通进程进行处理(由于多数是普通进程,这样做有利于提高系统效率)*/
If (p->policy==SCHED_OTHER){
      weight=p->counter;         /*返回权值为进程的counter值*/
/*如果当前进程的counter为0,则表示当前进程的时间片已用完,直接返回*/
 If (! weight)
        Goto out;
#Ifdef CONFIG_SMP
 If (p->processor==this_cpu)
                 Weight+=PROC_CHANGE_PENALTY;
#Endif
/*对进程权值进行微调,如果进程的内存空间使用当前正在运行的进程的内存空间,
则权值额外加1*/
      If (p->mm==this_mm||! p->mm)
              Weight+=1;
/*将权值加上20与进程优先级nice的差。普通进程的权值主要由counter值和nice值组成*/
 Weight+=20-p->nice;
  Goto out;
}
 /*对实时进程进行处理,返回权值为rt_priority+1000,确保优先级高于普通进程*/
Weight=1000+p->rt_priority;
 Out:
return weight; 
}
从goodness()函数可以看出,对于普通进程,其权值主要取决于剩余的时间配额和nice两个因素。nice的规定取值范围为19~-20,只有特权用户才能把nice值设为负数,而表达式(20-p->nice)掉转方向成为1~40。所以,综合的权值在时间片尚未用完时基本上是两者之和。如果是内核进程,或者其用户空间与当前进程相同,则权值将额外加1作为奖励。对于实时进程,其权值为1000+p->rt_priority,当p->counter达到0时该进程将移到队列的尾部,但其优先级仍不少于1000。可见当有实时进程就绪时,普通进程是没机会运行的。
由此可以看出,通过goodness()函数,Linux从优先考虑实时进程出发,实现了多种调度策略的统一处理,其设计思想可谓非常巧妙。
<script type="text/javascript"> </script> <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"> </script>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值