《linux内核设计与实现》阅读笔记 第三章 调度

多任务系统主要有两种:非抢占式多任务, 抢占式多任务(linux)

linux提供两套独立的优先级范围:NICE值(-20 高 ----------- 19低), 默认为0;(task_struct.static_prio保存)
                                              实时优先级(0 低 -------------- 99高),一般实时进程的实时优先级要普通的高

linux调度算法(kernel/sched.c).

调度算法中最基本的数据结构是运行队列(runqueue,kernel/sched.c),每个处理器分别有一个独立的运行队列,
该结构内为可执行的进程,并且一个进程同一时间只能出现在一个运行队列中,该结构并没有定义在内核头文件中为的是对内核的其
他部分隐藏实现细节,同时提供统一的抽象接口访问。

cpu_rq(processor):返回给定处理器的运行队列
this_rq:当前处理器的运行队列
task_rq(task):当前给定任务所在的运行队列

对运行队列操作必须先锁住它
struct runqueue * rq;

rq = task_rq_lock(task, &flags);/*返回task对应的rq并锁定*/
操作运行队列
task_rq_unlock(rq, &flags)
或是
this_rq_lock锁定当前运行队列,rq_unlock(struct runqueue *rq)解锁

锁定多个运行队列时必须按照同一顺序锁定,也可以使用double_rq_lock(rq1, rq2) double_rq_unlock(rq1, rq2);

每个运行队列都有两个优先级数组(struct prio_array kernel/sched.c),一个活跃的,一个过期的
当一个进程使用完它的时间片后就会被移入过期数组(也有例外,为了支持交互性而重新放回活跃数组的,见下)

struct prio_array{
int  nr_active; /*可执行进程数目?*/
unsigned long  bitmap[BITMAP_SIZE];  /*优先级位图 32位*5 160位 BITMAP_SIZE为5*/
struct list_head queue[MAX_PRIO]; /*MAX_PRIO为优先级数默认为140,
}


当初始时bitmap全为0,当某个优先级的进程运行时(状态为TASK_RUNNING),则bitmap相应的位置1,查找当前运行队列中的最高优
先级就是查找其对应的bitmap中第一个为1的位。

schedule()通过bitmap找到当前优先级最高的进程,判断是否是当前进程,不是则调用context_switch切换

该过程算法复杂度为O(1)级,不需要遍历整个数据结构。

linux实际使用动态优先级(task_struct.prio),动态优先级使用effective_prio函数计算,该函数使用nice值+进程交互性的奖励或
罚分(-5----+5之间)来计算动态优先级,交互性强的奖励高。

进程交互性的判断:linux记录进程执行和休眠的时间,该值(task_struct.sleep_avg,范围:0----MAX_SLEEP_AVG),但进程休眠则
休眠时间保存在该值中,执行则减去执行时间。

时间片:对于交互性强的程序,优先级高,默认时间片也长,时间片可以分多次使用完,而当进程创建时父子进程平分父进程的剩余
时间片

时间片的计算:时间片使用动态优先级来计算,task_timeslice为指定进程返回一个新的时间片。

对交互性的支持:某些交互性强的进程在使用完时间片后也不会被放入过期数组,而是重新加入活跃数组,该逻辑在scheduler_tick
中实现,该函数会被定时器中断调用,函数中使用TASK_INTERACTIVE(task)宏(主要通过nice值判断,由此可见交互性强的进程应该

将nice值设低)检测该进程是否是交互性的,同时还要使用EXPIRED_STARVING(rq)宏检测过期数组中的饥饿进程(如果发生这种情况
则进程哪怕是交互性强的也不会放入活跃数组)

休眠(被阻塞):
休眠时进程会将自己放入一个等待某个事件发生的队列中,具体过程见P35。
唤醒过程:通常达成条件的进程需要调用wake_up函数,该函数调用try_to_wake_up设置对应等待队列中的进程状态为TASK_RUNNING,调用activate_task将进程放入可执行队列,如果该进程比当前运行进程优先级高则设置need_resched标志。

有的时候对于唤醒并不表示条件达成,所以要用循环来处理

负载平衡:
因为每个处理器有自己的运行队列,在多处理器时会出现负载不均衡状态,这时就需要负载平衡程序load_balance(kernel/sched.c)
它有两种调度方法:schedule()时,运行队列为空时调用,定时器在系统空间时调用,在单处理器时load_balance不会被调用,甚至

不会编译进内核,因为只有一条运行队列。
load_balance具体操作P37。

上下文切换:
context_switch:见P38
need_resched标志:设置:当一个进程的时间片用完时在scheduler_tick中会设置,还有上面的try_to_wake_up中会设置。
need_resched标志保存在thread_info中,因此每个进程都有此标志,以前版本的内核使用全局变量,但放在task_struct中比访问全

局变量要快。

用户抢占发生时间:系统调用返回用户空间时

                           中断处理程序返回用户空间时

linux支持内核抢占,在内核中运行时也能被抢占,当一个进程持有锁的时候thread_info中的preempt_count字段为锁的数量,这个

时候不回发生抢占,只有preempt_count为0时表明该进程是能被安全抢占的。

内核抢占发生的时间: 当中断处理程序返回内核空间时
                              当内核代码再一次具有可抢占性时
                              内核中的任务显式的调用schedule
                              内核中的任务阻塞时

linux的实时调度策略: SCHED_FIFO:一直运行直到阻塞或显式地释放处理器
                               SCHED_RR  :带时间片的实时,在时间片内是实时的。
                               SCHED_OTHER:普通,非实时的。


linux的实时调度算法提供了软实时的方式?

与调度相关的系统调用:P40

处理器绑定已经放弃处理器时间:P41

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值