Linux2.4内核进程调度的缺陷:

Linux2.4 内核的进程调度采用时间片轮转和优先级相结合的调度策略,但存在以下几个致命缺陷:

1>调度算法时间复杂度是 O(n)。2.4 内核每次调度都要进行一次循环,耗时与当前就绪

进程数有关,因此达不到实时性的要求;时间片重算时必须给 task_struct 结构和就绪进程队列上锁.

2>不提供抢占式调度,

会导致大量的竞争,使就绪队列成为一个明显的瓶颈;

3>在 SMP 系统中,只有一个就绪队列,这将导致大部分的 CPU 处于空闲状态,从而影响 SMP 的效率;

Linux2.6 内核进程调度分析

进程的调度时机与引起进程调度的原因和进程调度的方式有关。在 2.6 中,除核心应用

主动调用调度器之外, 核心还在应用不完全感知的情况下在以下三种时机中启动调度器工作:

1>从中断或系统调用返回到用户态;

2>某个进程允许被抢占 CPU;

3>主动进入休眠状态;

调度策略:

在 Linux2.6 中,仍有三种调度策略: SCHED_OTHER、SCHED_FIFO 和 SCHED_RR。

SCHED_ORHER:普通进程,基于优先级进行调度。

SCHED_FIFO:实时进程,实现一种简单的先进先出的调度算法。

SCHED_RR:实时进程,基于时间片的SCHED_FIFO,实时轮流调度算法。

前者是普通进程调度策略,后两者都是实时进程调度策略。

SCHED_FIFO 与 SCHED_RR 的区别是:

当进程的调度策略为前者时,当前实时进程将一直占用 CPU 直至自动退出,除非有更紧迫的、

优先级更高的实时进程需要运行时,它才会被抢占 CPU;当进程的调度策略

为后者时,它与其它实时进程以实时轮流算法去共同使用 CPU,用完时间片放到运行队列尾部。

注:实时进程的优先级高于普通进程,后面介绍。

O(1)调度器是以进程的动态优先级 prio为调度依据的,它总是选择目前就绪队列中优先

级最高的进程作为候选进程 next。由于实时进程的优先级总是比普通进程的优先级高,故能

保证实时进程总是比普通进程先被调度。

Linux2.6 中,优先级 prio 的计算不再集中在调度器选择 next 进程时,而是分散在进程

状态改变的任何时候,这些时机有:

1>进程被创建时;

2>休眠进程被唤醒时;

3>从TASK_INTERRUPTIBLE 状态中被唤醒的进程被调度时;

4>因时间片耗尽或时间片过长而分段被剥夺 CPU 时;

在这些情况下,内核都会调用 effective_prio()重新计算进程的动态优先级 prio并根据计算结果调整它在就绪队列中的位置。

调度算法:

O(1)调度器的重要数据结构

(1)就绪队列 struct runqueue

runqueue 的设计是 O(1)调度器的关键技术所在,它用于存放特定 CPU 上的就绪进程队

列信息,其中包含每个 CPU 的调度信息。该结构在 /kernel/sched.c 中的定义如下:

struct runqueue {

...

prio_array_t *active, *expired, array[2];

active 是指向活动进程队列的指针

expired 是指向过期进程队列的指针

array[2]是实际的优先级进程队列,其中一个是活跃的一个是过期的,过期数组存放时间片耗完的进程

...

}

在 2.6 中,每个 CPU 单独维护一个就绪队列,每个就绪队列都有一个自旋锁,从而解

决了 2.4 中因只有一个就绪队列而造成的瓶颈。

(2)task_struct 结构

Linux2.6 内核使用 task_struct 结构来表示进程。2.6 对 task_struct 也做了较大的改动,

该结构定义在/include/linux/sched.h 中:

struct task_struct{

...

int prio,static_prio;

prio 是动态优先级,static_prio 是静态优先级(与最初nice相关)

...

prio_array_t *array;

记录当前 CPU 的活跃就绪队列

unsigned long sleep_avg;

进程的平均等待时间,取值范围[0,MAX_SLEEP_AVG],初值为0。

sleep_avg 反映了该进程需要运行的紧迫性。进程休眠该值增加,如果进程当前正在运行该值减少。

是影响进程优先级最重要的元素。值越大,说明该进程越需要被调度。

...

};

(3)优先级数组

每个处理器的就绪队列都有两个优先级数组,它们是 prio_array 类型的结构体。Linux2.6

内核正是因为使用了优先级数组,才实现了 O(1)调度算法。该结构定义在 kernel/sched.c 中:

struct prio_array{

...

unsigned int nr_active;

/**相应 runqueue 中的进程数

unsigned long bitmap[BITMAP_SIZE];

/**索引位图,BITMAP_SIZE 默认值为 5,5个long(32位)类型,每位代表一个优先级,可以代表160个优先级,但实际中只有140。

与下面的queue[]对应。

分布0-99对应为实时进程,100-140对应为普通的进程

struct list_head queue[MAX_PRIO];

/**每个优先级的进程队列,MAX_PRIO 是系统允许的最大优先级数,默认值为 140,数值越小优先级越高

bitmap每一位都与 queue[i]相对应,当 queue[i]的进程队列不为空时,bitmap 相应位为 1,否则就为 0。

}

O(1)调度算法实现的简单介绍

(1)选择并运行候选进程 next它确定下一个应该占有 CPU 并运行的进程,

schedule()函数是完成进程调度的主要函数,

并完成进程切换的工作。schedule()用于确定最高优先级进程的代码非常快捷高效,其

性能的好坏对系统性能有着直接影响,它在/kernel/sched.c 中的定义如下:

...

int idx;

...

preempt_disable();

...

idx = sched_find_first_bit( array -> bitmap);

queue = array -> queue + idx;

next = list_entry( queue -> next, task_t, run_list);

...

prev = context_switch( rq, prev, next);

...

}

其中,sched_find_first_bit()能快速定位优先级最高的非空就绪进程链表,运行时间和就

绪队列中的进程数无关,是实现 O(1)调度算法的一个关键所在。

schedule()的执行流程:首先,调用 pre_empt_disable(),关闭内核抢占,因为此时要对

内核的一些重要数据结构进行操作,所以必须将内核抢占关闭;其次,调用

sched_find_first_bit()找到位图中的第1个置1的位,该位正好对应于就绪队列中的最高优先级进程链表;

再者,调用 context_switch()执行进程切换,选择在最高优先级链表中的第 1个进程投入运行;

详细过程如图 1 所示:图 1

图中的网格为 140 位优先级数组,queue[7]为优先级为 7 的就绪进程链表。

此种算法保证了调度器运行的时间上限,加速了候选进程的定位过程。

(2)时间片的计算方法与时机

Linux2.4 调度系统在所有就绪进程的时间片都耗完以后在调度器中一次性重新计算,其中重算是用for循环相当耗时。

Linux2.6 为每个 CPU 保留 active 和 expired 两个优先级数

组, active 数组中包含了有剩余时间片的任务,expired 数组中包含了所有用完时间片的任务。

当一个任务的时间片用完了就会重新计算其时间片,并插入到 expired 队列中,当 active 队

列中所有进程用完时间片时,只需交换指向 active 和 expired 队列的指针即可。此交换是实

现 O(1)算法的核心,由 schedule()中以下程序来实现:

array = rq ->active;

if (unlikely(!array->nr_active)) {

rq -> active = rq -> expired;

rq -> expired = array;

array = rq ->active;

...

}

参考资料:《linux内核设计与实现》 《深入理解linux内核》

其中例子几乎书上都有,如有错误欢迎指正。

linux 2.6内核进程调度,linux2.6内核进程调度相关推荐

  1. linux 2.6内核进程调度,Linux2.6内核进程调度系列--scheduler_tick()函数2.更新实时进程的时间片,...

    Linux2.6内核进程调度系列--scheduler_tick()函数2.更新实时进程的时间片, RT /** * 递减当前进程的时间片计数器,并检查是否已经用完时间片. * 由于进程的调度类型不同 ...

  2. linux中initrd的含义,Linux2.6 内核的 Initrd 机制解析

    1.什么是 Initrdinitrd 的英文含义是 boot loaderinitialized RAM disk,就是由 boot loader 初始化的内存盘.在 linux内核启动前, boot ...

  3. linux 2.6内核镜像,Linux2.6内核镜像的构建过程

    make menuconfig:修改.添加内核配置选项,最后生成.config配置文件. make all:首先编译.config得到没有压缩的内核核心vmlinux,然后将其压缩生成引导过程使用的内 ...

  4. linux系统调用劫持隐藏进程,Linux2.6内核中劫持系统调用隐藏进程

    //#include #define CALLOFF 100 //使用模块参数来定义需要隐藏的进程名 int orig_cr0; char psname[10]="looptest" ...

  5. linux2.6内核分析,linux2.6内核分析——LRU链表

    LRU链表 本文转自http://liurugongzi.blog.sohu.com/153648100.html lru链表是统称,细分为:活动链表.非活动链表.链表中存放的是属于进程用户态地址空间 ...

  6. Linux2.6 内核进程调度分析

    Linux2.6 内核进程调度分析    进程的调度时机与引起进程调度的原因和进程调度的方式有关.在 2.6 中,除核心应用     主动调用调度器之外, 核心还在应用不完全感知的情况下在以下三种时机 ...

  7. 学习 Linux内核的意义及内核 head_list分析

    转自:http://blog.sina.com.cn/s/blog_6dd71c3c0101mgpf.html 1.分析linux内核的重要性 操作系统作为最核心的软件,关系到国家的战略安全,在现代的 ...

  8. linux内核链表以及list_entry--linux内核数据结构(一)

    传统的链表实现 之前我们前面提到的链表都是在我们原数据结构的基础上增加指针域next(或者prev),从而使各个节点能否链接在一起, 比如如下的结构信息 typedef struct fox { un ...

  9. Linux内核移植之一:内核源码结构与Makefile分析

    内容来自 韦东山<嵌入式Linux应用开发完全手册> 一.内核介绍 1.版本及其特点 Linux内核的版本号可以从源代码的顶层目录下的Makefile中看到,比如下面几行它们构成了Linu ...

最新文章

  1. Kotlin implements 的实现
  2. 在ie8下ext显示的问题
  3. mount:在/dev/sr0上找不到媒体
  4. linux touch
  5. reset.css(样式重置)
  6. 拓端tecdat|R语言逻辑回归(Logistic Regression)、回归决策树、随机森林信用卡违约分析信贷数据集
  7. SHELL下获得指定进程的进程号,并截取为整数
  8. mysql max as_mysql使用max函数+将类似123的字符型数据转换成数据类型
  9. 2015 2020 r4烧录卡 区别_2020版药典,药用辅料被重视了
  10. 昂达平板不能开机刷机_常用的昂达平板电脑怎么刷机 常用的昂达平板电脑刷机教程...
  11. 「上海院子」打造不可复制的国宅风华
  12. 怎么在WPS计算机,电脑怎么把wps热点删了?电脑永久删除wps热点的方法
  13. 领导周末喊程序员修bug,程序员霸气回应:在下卖艺不是卖身!
  14. 华硕T100 安装linux,华硕T100重装win10系统教程
  15. Model和ModelMap的关系
  16. 关于 watched variable changed 异常的详解
  17. kubernetes Affinity亲和性
  18. 如何卸载Oracle 10g
  19. JavaScript中的计时器与定时器
  20. Hive SQL操作与函数自定义(二)

热门文章

  1. Leetcode-Pascal's Triangle
  2. 关于建筑企业 业财一体化的一点思考
  3. bootstrap带图标的按钮与图标做连接
  4. 2017年4月5号课堂笔记
  5. BZOJ 1599: [Usaco2008 Oct]笨重的石子( 枚举 )
  6. 交叉编译和使用HTOP
  7. 图像处理VintaSoftImaging.NET SDK控件发布v7.0版本
  8. 合理设置域名TTL值给网站加速
  9. [导入]C#中WebService里的回车符\r丢失问题
  10. 5.22青海云南同震