您的位置:首页 > 运维架构 > Linux

linux内核代码研究——进程调度总结

2011-10-10 11:34 260 查看
进程分为I/O消耗型和处理器消耗型,I/O消耗型一般指和用户交互性大的进程,不会持续占用CPU但是一段时间内不能阻塞,保持畅通。处理器消耗型进程在获得CPU时,利用率会高达100%,可以阻塞,不用保持可运行态。

linux进程的状态可分为五种状态,状态记录于进程描述符中的state域的标志,

TASK_RUNNING 进程可执行,或者在执行或者在活动队列中等待。

TASK_INTERRUPTIBLE 进程被阻塞,等待某些条件的达成,一旦条件达成,内核就会把进程状态设置为TASK_RUNNING,处于此状态的进程也会因为接收到信号而被提前唤醒并投入使用

TASK_UNINTERRUPTIBLE 此状态的进程不会接收信号,其他特征与TASK_INTERRUPTIBLE相同

TASK_ZOMBLE 僵死,进程已经结束,但是父进程调用释放该进程的系统调用,为了父进程能感知到该进程的存在,进程描述符保留,此时的状态就是僵死状态

TASK_STOPPED 停止,进程停止执行,进程没有投入运行也不能投入运行

进程的状态由描述符中的state域保存,另一个重要的信息就是进程的优先级,普通优先级和实时优先级,顾名思义,实时优先级要比普通优先级高,而linux中是优先级数值越小,优先级越高,实时优先级的数值范围为0-99,而普通优先级范围是100-140,我们主要关注普通优先级。

普通优先级由nice值来表示范围-20到19,共40个数字,与刚才提到的100-140一一对应。nice值默认是0,nice数值越小优先级越高,同时nice值还影响时间片长短,nice值越低时间片越长。其实优先级和时间片长短是有关系的,像一些交互性很强需要及时响应用户操作的进程优先级必定很高,同时也必须获得较长的时间片来保证处于可运行状态。nice值越大也就代表着这个进程可运行并且可抢占。

时间片被用完的进程由活动队列放入过期队列,直到活动队列所有的进程时间片都用完,此时再重新计算时间片,这时候需要考量的因素有该进程是I/O消耗型还是处理器消耗型等因素,linux采取的方法是活动放入过期队列之前为每个进程计算好下一次的时间片,然后当活动队列为空时,过期队列变为活动队列,原来的活动队列再变为过期队列,正是这样一个朴素的思想保证重新计算时间片的复杂度为O(1).

时间片的概念是保证进程在此时间片内在活动队列,而并不是在此期间一直享有CPU,某进程可能会因为条件不满足而让出CPU或者被其他优先级较高的进程抢占。

说到优先级,linux系统是如何快速感知到活动队列中的进程优先级呢?

位图,linux每个活动队列中包含两个优先级数组,一个活跃的一个过期的,优先级数组包含优先级位图,刚才提到约有140个优先级数,位图包含160位,为每一个优先级分配一位,开始所有的位置零,当某个优先级进程准备执行时,相应的位就会被置一。每个优先级还对应一个级别链表,此链表里包含所有该级别的进程。在活动优先级找到被置一的位,然后找到级别链表中的进程,此进程被调度执行。这样做的好处是不用循环所有进程,因此系统进程数对该方法无影响,它所用的时间是恒定的。

上面说到关于进程调度的功能都是由schedule()方法触发的。

下面说到的睡眠由schedule()来完成的,有些进程是因为条件不满足需要进入睡眠状态,如果一旦设置为休眠状态,有可能无休止的休眠下去,所以现在的代码使用的递归,就是在判断条件不满足并且未接收到唤醒信号后再次调用schedule()函数,保证进程的状态根据实际情况及时更新。唤醒操作由wake_up()进行,如果被唤醒的进程比当前正在执行的进程优先级高,还要设置need_resched标志。通常哪段代码促使条件达成,就要负责随后调用wake_up()函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: