linux中O(1)调度算法与完全公平(CFS)调度算法

一、O(1)调度算法

1.1:优先级数组

O(1)算法的:一个核心数据结构即为prio_array结构体。该结构体中有一个用来表示进程动态优先级的数组queue,它包含了每一种优先级进程所形成的链表。

1 #define
MAX_USER_RT_PRIO        100
2 #define
MAX_RT_PRIO             MAX_USER_RT_PRIO
3 #define
MAX_PRIO                (MAX_RT_PRIO + 40)
4 typedef struct prio_array
prio_array_t;
5 struct prio_array
{
6         unsigned int nr_active;
7         unsigned long bitmap[BITMAP_SIZE];
8         struct list_head
queue[MAX_PRIO];
9 };

由于进程优先级的最大值为139,因此MAX_PRIO的最大值取140(具体的是,普通进程使用100到139的优先级,实时进程使用0到99的优先级)。因此,queue数组中包含140个可运行状态的进程链表,每一条优先级链表上的进程都具有相同的优先级,而不同进程链表上的进程都拥有不同的优先级。

除此之外,prio_array结构中还包括一个优先级位图bitmap。该位图使用一个位(bit)来代表一个优先级,而140个优先级最少需要5个32位来表示,因此BITMAP_SIZE的值取5。起初,该位图中的所有位都被置0,当某个优先级的进程处于可运行状态时,该优先级所对应的位就被置1。

因此,O(1)算法中查找系统最高的优先级就转化成查找优先级位图中第一个被置1的位。与2.4内核中依次比较每个进程的优先级不同,由于进程优先级个数是定值,因此查找最佳优先级的时间恒定,它不会像以前的方法那样受可执行进程数量的影响。

如果确定了优先级,那么选取下一个进程就简单了,只需在queue数组中对应的链表上选取一个进程即可。

1.2:活动进程和过期进程

在操作系统原理课上我们知道,当处于运行态的进程用完时间片后就会处于就绪态,此时调度程序再从就绪态的进程中选取一个作为即将要运行的进程。

而在具体Linux内核中,就绪态和运行态统一称为可运行态(TASK_RUNNING)。对于系统内处于可运行状态的进程,我们可以分为三类,首先是正处于执行状态的那个进程;其次,有一部分处于可运行状态的进程则还没有用完他们的时间片,他们等待被运行;剩下的进程已经用完了自己的时间片,在其他进程没有用完它们的时间片之前,他们不能再被运行。

据此,我们将进程分为两类,活动进程,那些还没有用完时间片的进程;过期进程,那些已经用完时间片的进程。因此,调度程序的工作就是在活动进程集合中选取一个最佳优先级的进程,如果该进程时间片恰好用完,就将该进程放入过期进程集合中。

在可运行队列结构中,arrays数组的两个元素分别用来表示刚才所述的活动进程集合和过期进程集合,active和expired两个指针分别直接指向这两个集合。

关于可运行队列和两个优先级数组的关系可参考下面的图:

正如上面分析的那样,可运行队列结构和优先级数组结构使得Q(1)调度算法在有限的时间内就可以完成,它不依赖系统内可运行进程的数量。

1.3:时间片的计算

Linux2.4版本的内核调度算法理解起来简单:在每次进程切换时,内核依次扫描就绪队列上的每一个进程,计算每个进程的优先级,再选择出优先级最高的进程来运行;尽管这个算法理解简单,但是它花费在选择优先级最高进程上的时间却不容忽视。系统中可运行的进程越多,花费的时间就越大,时间复杂度为O(n)。伪代码如下:

1 for (系统中的每个进程)
{
2     重新计算时间片;
3     重新计算优先级;
4 }

而2.6内核所采用的O(1)算法则很好的解决了这个问题,该算法可以在恒定的时间内为每个进程重新分配好时间片,而且在恒定的时间内可以选取一个最高优先级的进程,重要的是这两个过程都与系统中可运行的进程数无关,这也正是该算法取名为O(1)的缘故。

O(1)算法采用过期进程数组和活跃进程数组解决以往调度算法所带来的O(n)复杂度问题。过期数组中的进程都已经用完了时间片,而活跃数组的进程还拥有时间片。当一个进程用完自己的时间片后,它就被移动到过期进程数组中,同时这个过期进程在被移动之前就已经计算好了新的时间片。可以看到O(1)调度算法是采用分散计算时间片的方法,并不像以往算法中集中为所有可运行进程重新计算时间片。

当活跃进程数组中没有任何进程时,说明此时所有可运行的进程都用完了自己的时间片。那么此时只需要交换一下两个数组即可将过期进程切换为活跃进程,进而继续被调度程序所调度。两个数组之间的切换其实就是指针之间的交换,因此花费的时间是恒定的。下面的代码说明了两个数组之间的交换:

1 struct prop_array
*array = rq->active;
2 if (array->nr_active
!= 0) {
3     rq->active
= rq->expired;
4     rq->expired
= array;
5 }

通过分散计算时间片、交换过期和活跃两个进程集合的方法可以使得O(1)算法在恒定的时间内为每个进程重新计算好时间片。

进程调度的本质就是在当前可运行的进程集合中选择一个最佳的进程,这个最佳则是以进程的动态优先级为选取标准的。不管是过期进程集合还是活跃进程集合,都将每个优先级的进程组成一个链表,因此每个集合就有140个不同优先级的进程链表。同时,两个集合中还采用优先级位图来标记每个优先级链表中是否存在进程。

调度程序在选取最高优先级的进程时,首先利用优先级位图从高到低找到第一个被设置的位,该位对应着一条进程链表,这个链表中的进程是当前系统所有可运行进程中优先级最高的。在该优先级链表中选取头一个进程,它拥有最高的优先级,即为调度程序马上要执行的进程。上述进程的选取过程可用下述代码描述:

01 struct task_struct
*prev, *next;
02 struct list_head
*queue;
03 struct prio_array
*array;
04 int idx;
05  
06 prev
= current;
07 array
= rq->active;
08 idx
= sehed_find_first_bit(array->bitmap);
09 queue
= array->queue + idx;
10 next
= list_entry(queue->next, 
struct task_struct,
run_list);
11 if (prev
!= next)
12     context_switch();

sehed_find_first_bit()用于在位图中快速查找第一个被设置的位。如果prev和next不是一个进程,那么此时进程切换就开始执行。

通过上述的内容可以发现,在恒定的时间重新分配时间片和选择一个最佳进程是Q(1)算法的核心。

二、完全公平(CFS)调度算法

一个调度算法中最重要的两点就是调度哪个进程以及该被调度进程的运行时间是多少,下面我们分别来讨论

2.1:调度哪个进程

O(1)算法是根据进程的优先级来选择调度进程的,而CFS是根据进程的虚拟运行时间来进行调度的,当然,该虚拟运行时间也会受到优先级的影响,但不全是,下面看看什么是虚拟运行时间,以及如何根据它来选择下一个调度进程。

CFS算法的初衷就是让所有进程同时运行在一个CPU上,例如两个进程都需要运行10ms的时间,则CFS算法下,连个进程同时运行在CPU上,且时间为20ms,而不是每个进程分别运行10ms。但是这只是一种理想的运行方式,CFS为了近似这种运行算法,就提出了虚拟运行时间(vruntime)的概念。vruntime记录了一个可执行进程到当前时刻为止执行的总时间(需要以进程总数n进行归一化,并且根据进程的优先级进行加权)。根据vruntime的定义可以知道,vruntime越大,说明该进程运行的越久,所以被调度的可能性就越小。所以我们的调度算法就是每次选择vruntime值最小的进程进行调度,内核中使用红黑树可以方便的得到vruntime值最小的进程。至于每个进程如何更新自己的vruntime?内核中是按照如下方式来更新的:vruntime
+=  delta* NICE_0_LOAD/ se.weight;其中:

NICE_0_LOAD是个定值,及系统默认的进程的权值;se,weight是当前进程的权重(优先级越高,权重越大);

delta是当前进程运行的时间;我们可以得出这么个关系:vruntime与delta成正比,即当前运行时间越长vruntime增长越快

vruntime与se.weight成反比,即权重越大vunruntime增长越慢。简单来说,一个进程的优先级越高,而且该进程运行的时间越少,则该进程的vruntime就越小,该进程被调度的可能性就越高。

2.2:调度进程的运行时间

现在知道了如何调度进程了,但是当该进程运行时,它的运行时间是多少呢?

CFS的运行时间是有当前系统中所有可调度进程的优先级的比重来确定的,假如现在进程中有三个可调度进程A、B、C,它们的优先级分别为5,10,15,则它们的时间片分别为5/30,10/30,15/30。而不是由自己的时间片计算得来的,这样的话,优先级为1,2的两个进程与优先级为50,100的两个进程分的时间片是相同的。简单来说,CFS采用的所有进程优先级的比重来计算每个进程的时间片的,是相对的而不是绝对的。

这样就从以上两个方面来分析了CFS进程调度算法

参考文献:http://edsionte.com/techblog/archives/2851

时间: 2024-10-26 17:54:19

linux中O(1)调度算法与完全公平(CFS)调度算法的相关文章

linux中O(1)调度算法与全然公平(CFS)调度算法

一.O(1)调度算法 1.1:优先级数组 O(1)算法的:一个核心数据结构即为prio_array结构体. 该结构体中有一个用来表示进程动态优先级的数组queue,它包括了每一种优先级进程所形成的链表. 1 #define MAX_USER_RT_PRIO        100 2 #define MAX_RT_PRIO             MAX_USER_RT_PRIO 3 #define MAX_PRIO                (MAX_RT_PRIO + 40) 4 type

linux中常用时间和字符串之间相互转化

在Linux中经常会遇到时间和字符串相互转化的情形,有两个函数专门对应相应的转化. 1.时间转字符串函数strftime 函数原型:size_t strftime(char *s,size_t maxsize,char *format,conststruct tm *timeptr) strftime函数对timeptr指向的tm结构所代表的时间和日期进行格式编排,其结果放在字符串s中.该字符串的长度被设置为(最少)maxsize个字符.格式字符串format用来对写入字符串的字符进行控制,它包

详解 linux中的grub

grub是什么: grub是引导操作系统的程序,它会根据自己的配置文件,去引导内核,当内核被加载到内存以后, 内核会根据grub配置文件中的配置,找到根分区所使用的文件系统对应的驱动,通过根分区文件系统 对应的驱动,挂载根分区,从而达到启动操作系统的目的. 在了解grub以前,请先大体上了解一下centos5/6的启动过程,然后再理解grub就更容易了, 还记的我们以前总结过的centos5系统启动流程吗,如下图,此处我们重点讨论下图红框中的步骤. centos5/6中使用grub作为bootl

Linux中的crontab命令用法

Crontab 在linux中,crontab的用来设置定期执行指定的命令,我们可以用它来指定一些需要重复的事情,Linux系统的用户只需将想要定期要执行的命令序列加到crontab文件中,操作系统即会按用户配置的时间执行这些命令序列.向crontab文件里添加指令之前,需要检查下crontab服务是否已启动和是否开机自动启动: [查看状态] Linux 系统上面原本就有非常多的计划性工作,因此这个系统服务是默认启动的 可以使用service crond status进行查看状态,下图是我在ce

centos linux中怎么查看和修改计算机名/etc/sysconfig/network

centos linux中怎么查看和修改计算机名 查看计算机名:在终端输入hostname 修改的话 hostname +计算机名(重启后失效)要永久修改的话要修改配置文件/etc/sysconfig/network修改hostname=你要改的名字

Linux中的select,poll,epoll模型

Linux中的 select,poll,epoll 都是IO多路复用的机制. select select最早于1983年出现在4.2BSD中,它通过一个select()系统调用来监视多个文件描述符的数组,当select()返回后,该数组中就绪的文件描述符便会被内核修改标志位,使得进程可以获得这些文件描述符从而进行后续的读写操作.select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,事实上从现在看来,这也是它所剩不多的优点之一.select的一个缺点在于单个进程能够监视的文件描

linux中vim编辑器各种常用命令及用法

linux中vim编辑器的常用命令以及用法(注意严格区分大小写以及中英文): vim编辑器有三种模式,分别是:编辑模式,输入模式以及末行模式. 模式转换: 编辑模式>>>输入模式: i:在光标所在字符前面,转为输入模式(即转完后在光标所在字符前输入):                      I:在光标所在行的行首,转为输入模式(即转完后在行首输入,不包括行首空                         白) a:在光标所在字符后,转为输入模式(即转完后在光标所在字符后面输入):

Linux中添加用户、删除用户时新手可能遇到的问题

Linux中添加用户.删除用户时新手可能遇到的问题  1.创建新用户后切换到新用户:No directory, logging in with HOME=/     添加用户     #sudo useradd -m -s /bin/bash -g group loginname     -m 创建home目录 (不加这个要手动添加目录,不然会出现No directory,Logging in with HOME=/ )     -s /bin/bash 使用bash( 默认为 /bin/sh

理解Linux中的load Averges

一.什么是load average? linux系统中的Load对当前CPU工作量的度量 (WikiPedia: the system load is a measure of the amount of work that a computer system is doing).也有简单的说是进程队列的长度. Load Average 就是一段时间 (1 分钟.5分钟.15分钟) 内平均 Load . 我们可以通过系统命令"w"查看当前load average情况 [[email p