LINUX 操作系统 第4章 进程调度
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
27
4.2.5 计算优先级和时间片
进程有一个初始优先级(也称静态优先级), 叫做nice值,范围从-20~19,默认为0,它 由用户指定。 nice值越小,优先级越高。 nice值通过转换后存放在task_struct结构的 static_prio域中,转换方法见下页:
28
如何将nice转换成static_prio
活动数组:其中的可执行队列上的进程都还 有时间片剩余 过期数组:其中的可执行队列上的进程都耗 尽了时间片
当一个进程的时间片耗尽时,它会被移至 过期数组,但在此之前,时间片已给它重 新计算好了。
24
活动数组和过期数组的切换
现在,重新计算时间片只需要在活动数组 和过期数组之间来回切换就行了。这个动 作由schedule( )完成,部分代码如下:
10
4.2 Linux调度算法
Linux的调度程序在kernel/sched.c中定义。 在2.5开发版的内核中重写了调度程序的大 部分代码,主要为了实现以下目标:
充分实现O(1)调度 全面实现SMP的可扩展性 强化SMP的亲和力 加强交互性能 保证公平
11
4.2.1 可执行队列
第4章 进程调度
进程调度
调度程序是内核的组成部分,它负责选择 下一个要运行的进程。
调度程序的基本工作:在一组处于可运行 状态的进程中选择一个来执行。 Linux提供抢占式的多任务模式。
2
本章主要内容
调度策略
Linux调度算法
抢占和上下文切换
实时
与调度相关的系统调用
3
4.1 调度策略
可执行队列定义于kernel/sched.c中,由结 构runqueue表示。 可执行队列是给定处理器上的可执行进程 的链表,每个处理器一个。
12
可执行队列runqueue
struct runqueue { spinlock_t unsigned long unsigned long long unsigned long unsigned long unsigned long long task_t struct mm_struct prio_array_t atomic_t task_t struct list_head }; lock; nr_running; nr_switches; nr_uninterruptible; expired_timestamp; timestamp_last_tick; *curr, *idle; *prev_mm; *active, *expired, arrays[2]; nr_iowait; *migration_thread; migration_queue;
在schedule( )函数中首先要判断谁是优先级 最高的进程,代码见下页。
26
判断谁是优先级最高的进程
struct task_struct struct runqueue struct prio_array struct list_head int *prev, *next; *rq; *array; *queue; idx;
#define CURRENT_BONUS(p) \ (NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / MAX_SLEEP_AVG)
33
\
bonus的计算
通过计算得:MAX_BONUS=10; MAX_SLEEP_AVG=100*10=1000。 假设进程的休眠时间为x, 即x= (p)-> sleep_avg,则 CURRENT_BONUS(p) = x/ (1000000000 / 1000)×10/1000= x/108。 因此bonus = CURRENT_BONUS(p) MAX_BONUS / 2=x/108 -5。
8
时间片
当一个进程的时间片耗尽时,就认为进程 到期了。 没有时间片的进程不会再投入运行,除非 等到其它所有的进程都耗尽了它们的时间 片,这时,会重新计算所有进程的时间片。
9
4.1.4 进程抢占
两种情况下会发生进程抢占:
有一个进程进入TASK_RUNNING状态,而它 的优先级高于当前正在运行的进程 当正在运行的进程的时间片变为0时
20
prio_array结构体分析
每个优先级数组都包含一个这样的位图成员, 至少为每个优先级准备一位。初始时,所有 的位都臵0;当拥有某个优先级的进程准备 执行时,位图中的相应位就被臵1。 这样,查找系统中最高的优先级就变成了查 找位图中被设臵的第一个位。因为优先级的 个数是定值(140个),所以查找时间恒定, 并不受系统到底有多少可执行进程的影响。
29
动态优先级prio
调度程序要用到的动态优先级存放在 task_struct结构的prio域中。
动态优先级prio通过一个关于静态优先级 和进程交互性的函数关系计算而来。
30
计算进程的动态优先级
effective_prio( )函数可以返回一个进程的 动态优先级。 该函数以nice值为基数,再加上-5到+5之 间的进程交互性的奖励或罚分。 该函数在kernel/sched.c中实现,代码见下 页。
13
与可执行队列相关的几个宏
cpu_rq(processor):返回给定处理器的可 执行队列的指针 this_rq( ):返回当前处理器的可执行队列 task_rq(task):返回给定任务所在的队列 指针
14
锁住可执行队列
在对可执行队列进行操作之前,应该先锁 住它,防止队列被其它代码改动。 需要用到task_rq_lock( )和task_rq_unlock( ) 函数。
struct runqueue *rq; rq=this_rq_lock( ); /*操作当前运行队列rq*/ rq_unlock(rq);
16
如何避免死锁
为了避免死锁,要锁住多个运行队列的代 码必须总是按照同样的顺序获取这些锁: 按照可执行队列地址从低到高的顺序。 P34页代码 自旋锁用于防止多个任务同时对可执行队 列操作。
32
bonus的计算中需要用到的宏和函数
# define HZ 1000 #define NS_TO_JIFFIES(TIME) ((TIME) / (1000000000 / HZ))
#define DEF_TIMESLICE
(100 * HZ / 1000)
#define MAX_SLEEP_AVG \ (DEF_TIMESLICE * MAX_BONUS)
17
4.2.2 优先级数组
每个运行队列有两个优先级数组,一个活 跃的,一个过期的。 优先级数组在kernel/sched.c中定义,是 prio_array类型的结构体。 优先级数组是一种能够提供O(1)算法复杂 度的数据结构。
18
prio_array结构体
struct prio_array { unsigned int unsigned long struct list_head };
prev = current; array = rq->active; idx = sched_find_first_bit(array->bitmap); queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list);
19
prio_array结构体分析
BITMAP_SIZE是优先级位图数组的大小, 定义如下:
#define BITMAP_SIZE \ ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long))
由以上定义,计算出BITMAP_SIZE=5。 数组bitmap[BITMAP_SIZE]为unsigned long型, 长32位,如果每位代表一个优先级的话,共有 32*5=160位,足够表示前面提到的140个优先 级。
if (!array->nr_active) { rq->active = rq->expired; rq->expired = array; array = rq->active; }
25
4.2.4 schedule( )
schedule( )函数完成的工作:选定下一个投 入运行的进程,并切换到这个进程。
nr_active; bitmap[BITMAP_SIZE]; queue[MAX_PRIO];
MAX_PRIO定义了系统拥有的优先级个数,默 认值是140,在sched.h中定义如下:
#define MAX_USER_RT_PRIO #define MAX_RT_PRIO #define MAX_PRIO 100 MAX_USER_RT_PRIO (MAX_RT_PRIO + 40)
22
4.2.3 重新计算时间片
重新计算时间片的老方法:
for (系统中的每个任务) { 重新计算优先级 重新计算时间片 }
存在的弊端:
耗费的时间长 必须靠锁来保护任务队列,加剧对锁的争用 重新计算时间片的时机不确定 实现粗糙
23
新的Linux调度程序如何计算时间片
Linux调度程序为每个处理器维护两个优先 级数组:
nice值:范围从-20~19,默认值是0。值越大, 优先级越低。 实时优先级:任何实时进程的优先级都高于 普通进程。
7
4.1.3 时间片
时间片大小的确定
太短 —— 问题? 太长 —— 问题?
Linux的调度程序提供较长的默认时间片Baidu Nhomakorabea 交互式程序。 Linux还能根据进程的优先级动态调整分 配给它的时间片,从而保证优先级高的进 程,执行的频率高,执行时间长。
#define MAX_RT_PRIO 100 #define NICE_TO_PRIO(nice) \ (MAX_RT_PRIO + (nice) + 20) struct task_struct *p; p->static_prio = NICE_TO_PRIO(nice);
经计算得:static_prio的范围为100~139, 值越小,优先级越高。
5
调度策略
调度策略通常要在两个矛盾中寻找平衡:
进程响应时间短 系统吞吐量高
Linux为了保证交互式应用,更倾向于优先 调度I/O消耗型进程。
6
4.1.2 进程优先级
基于优先级的调度:调度程序总是选择时 间片未用尽而且优先级最高的进程运行。 Linux实现了基于动态优先级的调度方法。 Linux内核提供了两组独立的优先级:
调度策略决定调度程序在何时让什么进程 运行。
4
4.1.1 I/O消耗型和处理器消耗型进程
I/O消耗型进程:进程的大部分时间用来提 交I/O请求或是等待I/O请求。 这样的进程经常处于可运行状态,但通常 只运行短短的一会儿。 处理器消耗型进程:它把时间大多用在执 行代码上。 对于处理器消耗型进程,调度策略是尽量 降低它们的运行频率。
21
prio_array结构体分析
每个prio_array包含一个叫作queue的数组, 该数组的每个元素都是一个struct list_head 类型的队列。每个队列与一个给定的优先 级相对应。 prio_array中还包含了一个计数器 nr_active,记录该优先级数组内可执行进 程的数目。
struct runqueue *rq; unsigned long flags; rq= task_rq_lock(task, &flags); /*对任务的队列rq进行操作*/ task_rq_unlock(rq, &flags);
15
锁住当前的可执行队列
可以用this_rq_lock( )来锁住当前的可执行 队列,用rq_unlock(struct runqueue * rq) 释放给定队列上的锁。
31
effective_prio( )函数
static int effective_prio(task_t *p) { int bonus, prio; if (rt_task(p)) return p->prio; bonus = CURRENT_BONUS(p) - MAX_BONUS / 2; prio = p->static_prio - bonus; if (prio < MAX_RT_PRIO) prio = MAX_RT_PRIO; if (prio > MAX_PRIO-1) prio = MAX_PRIO-1; return prio; }
4.2.5 计算优先级和时间片
进程有一个初始优先级(也称静态优先级), 叫做nice值,范围从-20~19,默认为0,它 由用户指定。 nice值越小,优先级越高。 nice值通过转换后存放在task_struct结构的 static_prio域中,转换方法见下页:
28
如何将nice转换成static_prio
活动数组:其中的可执行队列上的进程都还 有时间片剩余 过期数组:其中的可执行队列上的进程都耗 尽了时间片
当一个进程的时间片耗尽时,它会被移至 过期数组,但在此之前,时间片已给它重 新计算好了。
24
活动数组和过期数组的切换
现在,重新计算时间片只需要在活动数组 和过期数组之间来回切换就行了。这个动 作由schedule( )完成,部分代码如下:
10
4.2 Linux调度算法
Linux的调度程序在kernel/sched.c中定义。 在2.5开发版的内核中重写了调度程序的大 部分代码,主要为了实现以下目标:
充分实现O(1)调度 全面实现SMP的可扩展性 强化SMP的亲和力 加强交互性能 保证公平
11
4.2.1 可执行队列
第4章 进程调度
进程调度
调度程序是内核的组成部分,它负责选择 下一个要运行的进程。
调度程序的基本工作:在一组处于可运行 状态的进程中选择一个来执行。 Linux提供抢占式的多任务模式。
2
本章主要内容
调度策略
Linux调度算法
抢占和上下文切换
实时
与调度相关的系统调用
3
4.1 调度策略
可执行队列定义于kernel/sched.c中,由结 构runqueue表示。 可执行队列是给定处理器上的可执行进程 的链表,每个处理器一个。
12
可执行队列runqueue
struct runqueue { spinlock_t unsigned long unsigned long long unsigned long unsigned long unsigned long long task_t struct mm_struct prio_array_t atomic_t task_t struct list_head }; lock; nr_running; nr_switches; nr_uninterruptible; expired_timestamp; timestamp_last_tick; *curr, *idle; *prev_mm; *active, *expired, arrays[2]; nr_iowait; *migration_thread; migration_queue;
在schedule( )函数中首先要判断谁是优先级 最高的进程,代码见下页。
26
判断谁是优先级最高的进程
struct task_struct struct runqueue struct prio_array struct list_head int *prev, *next; *rq; *array; *queue; idx;
#define CURRENT_BONUS(p) \ (NS_TO_JIFFIES((p)->sleep_avg) * MAX_BONUS / MAX_SLEEP_AVG)
33
\
bonus的计算
通过计算得:MAX_BONUS=10; MAX_SLEEP_AVG=100*10=1000。 假设进程的休眠时间为x, 即x= (p)-> sleep_avg,则 CURRENT_BONUS(p) = x/ (1000000000 / 1000)×10/1000= x/108。 因此bonus = CURRENT_BONUS(p) MAX_BONUS / 2=x/108 -5。
8
时间片
当一个进程的时间片耗尽时,就认为进程 到期了。 没有时间片的进程不会再投入运行,除非 等到其它所有的进程都耗尽了它们的时间 片,这时,会重新计算所有进程的时间片。
9
4.1.4 进程抢占
两种情况下会发生进程抢占:
有一个进程进入TASK_RUNNING状态,而它 的优先级高于当前正在运行的进程 当正在运行的进程的时间片变为0时
20
prio_array结构体分析
每个优先级数组都包含一个这样的位图成员, 至少为每个优先级准备一位。初始时,所有 的位都臵0;当拥有某个优先级的进程准备 执行时,位图中的相应位就被臵1。 这样,查找系统中最高的优先级就变成了查 找位图中被设臵的第一个位。因为优先级的 个数是定值(140个),所以查找时间恒定, 并不受系统到底有多少可执行进程的影响。
29
动态优先级prio
调度程序要用到的动态优先级存放在 task_struct结构的prio域中。
动态优先级prio通过一个关于静态优先级 和进程交互性的函数关系计算而来。
30
计算进程的动态优先级
effective_prio( )函数可以返回一个进程的 动态优先级。 该函数以nice值为基数,再加上-5到+5之 间的进程交互性的奖励或罚分。 该函数在kernel/sched.c中实现,代码见下 页。
13
与可执行队列相关的几个宏
cpu_rq(processor):返回给定处理器的可 执行队列的指针 this_rq( ):返回当前处理器的可执行队列 task_rq(task):返回给定任务所在的队列 指针
14
锁住可执行队列
在对可执行队列进行操作之前,应该先锁 住它,防止队列被其它代码改动。 需要用到task_rq_lock( )和task_rq_unlock( ) 函数。
struct runqueue *rq; rq=this_rq_lock( ); /*操作当前运行队列rq*/ rq_unlock(rq);
16
如何避免死锁
为了避免死锁,要锁住多个运行队列的代 码必须总是按照同样的顺序获取这些锁: 按照可执行队列地址从低到高的顺序。 P34页代码 自旋锁用于防止多个任务同时对可执行队 列操作。
32
bonus的计算中需要用到的宏和函数
# define HZ 1000 #define NS_TO_JIFFIES(TIME) ((TIME) / (1000000000 / HZ))
#define DEF_TIMESLICE
(100 * HZ / 1000)
#define MAX_SLEEP_AVG \ (DEF_TIMESLICE * MAX_BONUS)
17
4.2.2 优先级数组
每个运行队列有两个优先级数组,一个活 跃的,一个过期的。 优先级数组在kernel/sched.c中定义,是 prio_array类型的结构体。 优先级数组是一种能够提供O(1)算法复杂 度的数据结构。
18
prio_array结构体
struct prio_array { unsigned int unsigned long struct list_head };
prev = current; array = rq->active; idx = sched_find_first_bit(array->bitmap); queue = array->queue + idx; next = list_entry(queue->next, task_t, run_list);
19
prio_array结构体分析
BITMAP_SIZE是优先级位图数组的大小, 定义如下:
#define BITMAP_SIZE \ ((((MAX_PRIO+1+7)/8)+sizeof(long)-1)/sizeof(long))
由以上定义,计算出BITMAP_SIZE=5。 数组bitmap[BITMAP_SIZE]为unsigned long型, 长32位,如果每位代表一个优先级的话,共有 32*5=160位,足够表示前面提到的140个优先 级。
if (!array->nr_active) { rq->active = rq->expired; rq->expired = array; array = rq->active; }
25
4.2.4 schedule( )
schedule( )函数完成的工作:选定下一个投 入运行的进程,并切换到这个进程。
nr_active; bitmap[BITMAP_SIZE]; queue[MAX_PRIO];
MAX_PRIO定义了系统拥有的优先级个数,默 认值是140,在sched.h中定义如下:
#define MAX_USER_RT_PRIO #define MAX_RT_PRIO #define MAX_PRIO 100 MAX_USER_RT_PRIO (MAX_RT_PRIO + 40)
22
4.2.3 重新计算时间片
重新计算时间片的老方法:
for (系统中的每个任务) { 重新计算优先级 重新计算时间片 }
存在的弊端:
耗费的时间长 必须靠锁来保护任务队列,加剧对锁的争用 重新计算时间片的时机不确定 实现粗糙
23
新的Linux调度程序如何计算时间片
Linux调度程序为每个处理器维护两个优先 级数组:
nice值:范围从-20~19,默认值是0。值越大, 优先级越低。 实时优先级:任何实时进程的优先级都高于 普通进程。
7
4.1.3 时间片
时间片大小的确定
太短 —— 问题? 太长 —— 问题?
Linux的调度程序提供较长的默认时间片Baidu Nhomakorabea 交互式程序。 Linux还能根据进程的优先级动态调整分 配给它的时间片,从而保证优先级高的进 程,执行的频率高,执行时间长。
#define MAX_RT_PRIO 100 #define NICE_TO_PRIO(nice) \ (MAX_RT_PRIO + (nice) + 20) struct task_struct *p; p->static_prio = NICE_TO_PRIO(nice);
经计算得:static_prio的范围为100~139, 值越小,优先级越高。
5
调度策略
调度策略通常要在两个矛盾中寻找平衡:
进程响应时间短 系统吞吐量高
Linux为了保证交互式应用,更倾向于优先 调度I/O消耗型进程。
6
4.1.2 进程优先级
基于优先级的调度:调度程序总是选择时 间片未用尽而且优先级最高的进程运行。 Linux实现了基于动态优先级的调度方法。 Linux内核提供了两组独立的优先级:
调度策略决定调度程序在何时让什么进程 运行。
4
4.1.1 I/O消耗型和处理器消耗型进程
I/O消耗型进程:进程的大部分时间用来提 交I/O请求或是等待I/O请求。 这样的进程经常处于可运行状态,但通常 只运行短短的一会儿。 处理器消耗型进程:它把时间大多用在执 行代码上。 对于处理器消耗型进程,调度策略是尽量 降低它们的运行频率。
21
prio_array结构体分析
每个prio_array包含一个叫作queue的数组, 该数组的每个元素都是一个struct list_head 类型的队列。每个队列与一个给定的优先 级相对应。 prio_array中还包含了一个计数器 nr_active,记录该优先级数组内可执行进 程的数目。
struct runqueue *rq; unsigned long flags; rq= task_rq_lock(task, &flags); /*对任务的队列rq进行操作*/ task_rq_unlock(rq, &flags);
15
锁住当前的可执行队列
可以用this_rq_lock( )来锁住当前的可执行 队列,用rq_unlock(struct runqueue * rq) 释放给定队列上的锁。
31
effective_prio( )函数
static int effective_prio(task_t *p) { int bonus, prio; if (rt_task(p)) return p->prio; bonus = CURRENT_BONUS(p) - MAX_BONUS / 2; prio = p->static_prio - bonus; if (prio < MAX_RT_PRIO) prio = MAX_RT_PRIO; if (prio > MAX_PRIO-1) prio = MAX_PRIO-1; return prio; }