LKD4

来源:互联网 发布:剑三纯阳正太捏脸数据 编辑:程序博客网 时间:2024/06/09 17:21

第四章 进程调度

1.调度程序可看作是在可运行态进程之间分配有限的处理器资源的内核子系统。
2.多任务操作系统就是能同时并发的交互执行多个进程的操作系统
3.非抢占式多任务和抢占十多任务,Linux属于后者
4.进程的时间片:分配给每个可运行进程的处理器时间段,避免某个进程独占系统
5.Linux的O(1)调度程序
------------------------------------------------------------------
1. IO消耗型进程:进程的大部分时间来提交IO请求和等待IO请求。常处于可运行态
2. 处理器消耗型进程:时间大多用在执行代码上,除非被强占,通常一直运行
3. 同时具有上述特征:X-windows服务器,字处理软件
4. 调度策略在两个矛盾的目标中寻找平衡:响应时间和吞吐率。
5. 调度算法最基本的是:基于优先级的调度,在Linux系统中,优先级高的进程使用的时间片也较长。
6. Linux的动态优先级的调度方法: IO型进程的优先级会被提高,处理器型的优先级会被降低。
7. nice(-20~19) 与实时优先级
8. 时间片是指进程在抢占之前所能持续运行的时间。时间片过长,响应表现欠佳,时间片短,进程切换所耗比重增加。
9. 进程并不要一次用完所有的时间片。没有时间片的进程不会再投入运行。除非等到所有的进程都耗尽了时间片,然后重新计算。
10.当一个进程进入TASK_RUNNING状态,内核会检查她的优先级与当前进程的优先级比较。
11.当一个进程的时间片为0时,抢占也会发生。


--------------------------------------------------------------------------------
1.Linux的调度程序在kernel/sched.c中,实现以下目标
-充分实现O(1)功能
-扩展SMP的特性,每个处理器有自己的锁和自己的可执行队列,强化SMP的亲和力
-强化交互性能,保证公平,

2. 可执行队列:runqueue,定义于kernel/sched.c.
struct runqueue{
spinlock_t lock;
unsigned long nr_running;
unsigned long nr_switches;
unsigned long expired_timestamp;
unsigned long nr_uninterruptible;
unsigned long long timestamp_last_tick;
struct task_struct *curr;
struct task_struct *idle;
struct mm_struct *prev_mm;
struct prio_array *active;
struct prio_array *expired;
struct prio_array arrays[2];
struct task_struct *migration_thread;
struct list_head *migration_queue;
atomic_t nr_iowait;
}
3.一组宏操作
cpu_rq(process):
this_rq()
task_rq(task)

4.在对可执行队列操作时,应该先锁住,每个可执行队列对应一个处理器。
task_rq_lock()和task_rq_unlock(),this_rq_lock和rq_unlock()
5.按一定的次序锁住多个队列:double_rq_lock(rq1,rq2) double_rq_unlock(rq1,rq2)
6.自旋:spinlock
7.两个优先级数组:活跃的和过期的。在kernel/sched.c中定义
8.提供O(1)算法的数据结构
struct prio_array{
int nr_active;
unsigned long bitmap[BITMAP_SIZE];为每个优先级准备一bit
struct list_head queue[MAX_PRIO].
}
9. sched_find_first_bit(),或者find_first_set指令
10.Linux上重新计算时间片很简单,只是把两个数组换过来即可
if(!array->nr_active){
 rq->active = rq->expired;
 rq->expired = array;
}
这种交换是O(1)的核心,
11.schedule的过程:找到优先级数组中被设置的位-〉选择这个优先级链表中的第一个进程->
如果prev != next,表明切换了进程,context_switch()函数被调用
12.static_prio存放着nice值,之所以叫静态,是因为从一开始由用户指定,并且不能改变。
13.动态优先级有静态优先级和进程交互性的函数关系计算出来:effective_prio()
14.通过进程休眠的长短来判断属于IO型进程还是处理器型进程。
15.sleep_avg就是用来表示这个的:休眠是增加,运行时减少
16.重新计算时间片仅仅依赖于nice值。子进程创建的时候,父进程和子进程平分剩下的时间片。
17.timeslice()计算时间片,这是把nice值按比例的放大缩小。范围(5ms~800ms,默认是100ms)
18.支持交互进程的机制:如果交互性非常强,当他的时间片用完后,会被再次放入活动数组,而不是过期数组中。但不是立即执行,需要等待轮训。
19.schedule_tick来实现
task = current;
if(!--task->time_slice){
 if(!TASK_INTERACTIVE(task) || EXPIRED_STARVING(rq))
  enqueue_task(task,rq->expired);
 else
  enqueue(task,rq->active);
}

20. 休眠的两种状态:TASK_INTERRUPTIBLE,TASK_UNINTERRUPTIBLE.
21. 等待队列:wait_queue_head_t,由DECLARE_WAITQUEUE,init_waitqueue_head()来做初始化。
进程把自己放入等待队列中,并设置成不可运行状态。防止产生竞争条件,休眠和唤醒必须....
DECLARE_WAITQUEUE(wait,current)
add_wait_queue(q,wait)
while(!cond){ 这个循环的目的是防止虚假的唤醒。
 set_current_state(TASK_INTERRUPTIBLE);
 if(signal_pending(current))
  /**/
 schedule();
}
22 当条件满足后,设置为task_running状态,然后remove_wait_queue()把自己移出队列
23. 唤醒操作通过wake_up,唤醒制定等待队列上所有的进程。->try_wake_up()->activate_task()将此进程放入可执行队列。如果优先级较高,设置need_resched标志。
-------------------------------------------------------------------------------------1. 在多处理器系统中,每个处理器拥有自己的进程链表。整个调度系统对每个处理器来说也是独立的
2.负载平衡程序,保证可执行队列之间的负载处于均衡状态。在kernel/sched.c中的load_balance()来实现。在schedule执行的时候,如果当前的可执行队列为空,或者是在定时器(空闲时1ms,其他200ms)调用一次。
3.要锁住当前处理器的可执行队列,并屏蔽中断,防止可执行队列并发的访问。
4.load_balance调用find_busiest_queue,(比当前队列多25%或以上),
从繁忙的队列中选一个优先级数组一边抽去进程。最好是过期数组。
寻找含有进程并且由县级最高的链表,因为把优先级高的进程平均分散开来才是最有效的
选择一个进程,(不在执行,没有亲和性,不再告诉缓存中),调用pull_task从繁忙队列放入当前队列。
只要仍然不平衡就继续执行。

------------------------------------------------------------------------------------
1. 在进程切换时,如果是两个进程,就会调用context_switch():调用switch_mm().切换虚拟内存,调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。
2.need_resched的标志在:schedule_tick()或者是高优先级进入可执行状态(try_to_wake_up()的时候设置。
3.在返回用户空间或是从中断返回时,内核会检查这个标志。每个进程有个need_resched的标志。在thread_info中
4.用户抢占发生在:从系统调用返回,从中断处理程序返回用户空间
5.一般Unix系统,内核中的各任务是协作方式调度的,不具抢占性,Linux2.6引入了抢占能力,只要重新调度是安全的,内核就可以在任何时间抢占执行的任务。
6.支持内核抢占,所以thread_info中添加了preempt_count,使用所得时候加一,释放所得时候减一,只有当为0时,表示可以在内核被抢占。从中断返回内核空间的时候,内核会检查need_resched和preempt_count的值。

7.如果内核进程阻塞了,或显示的调用schedule(),内核抢占也会发生。
8.内核抢占发生在:
从中断程序返回内核空间
内核代码在一次的具有可抢占性
显时的调用schedule()
内核中的任务阻塞
-------------------------------------------------------------------------------------
1.两种实时调度策略:SCHED_FIFO,不使用时间片,优先调度,但可抢占。
SCHED_RR:采用时间片的sched_FIFO
2.软实时:内核调度进程尽量使进程在他的限定时间前运行,但不保证总能满足要求。
3.实时优先级是(0,99)而NORMAL的优先级是100~139
4.包含在C库中,没有太多的封装。
nice()
sched_setscheduler()
sched_getscheduler()
sched_setparam()
sched_getparam()
sched_get_priority_max()
sched_get_priority_min()
sched_rr_get_interval()
sched_setaffinity()
sched_getaffinity()
sched_yiled():主动让出控制权,放入过期队列中,实时进程仅仅放在活动队列的后面

 

原创粉丝点击