linux进程管理 - 核心调度器
来源:互联网 发布:阿里妈妈淘宝客采集器 编辑:程序博客网 时间:2024/05/21 06:15
1966 /* Here we just switch the register state and the stack. */1967 switch_to(prev, next, prev);1968 1969 barrier();
linux内核进程调度器基于两个函数:周期性调度器函数和主调度器函数.
周期性调度器
所谓周期性调度器就是scheduler_tick中实现。如果系统正在活动中,内核会按照HZ自动调用这个函数。实际上在每个滴答的handler中会调用这个函数。如果在没有进程等待调度,那么在计算机电力供应不足的情况下,也可以关闭该调度器以减少电能消耗。该函数会激活负责当前进程的调度类的周期性调度方法。
if (curr != rq->idle) /* FIXME: needed? */ curr->sched_class->task_tick(rq, curr);
由于调度器的模块化结构,调度器本身实现比较简单,因为主要的工作完全可以委托给特定调度器类的方法。
task_tick的实现完全依赖于底层的调度器类。例如,CFS调度器类会在方法中检测进程是否已经运行太长时间,以避免过长的延迟。如果需要调度,那么会调用set_tsk_need_resched函数来设置TIF_NEED_RESCHED标志,以表示该请求。
主调度器
在内核的许多地方,如果需要将CPU分配给与当前活动进程不同的另一个进程,都会直接调用主调度器函数schedule。在从系统调用返回后,内核会检查当前进程是否设置了重新调度标志TIF_NEED_RESCHED标志(例如上面提到的 scheduler_tick就可能会设置这个标志),如果设置了,则调用schedule函数。
我们现在看一下schedule的实现
3616 /*3617 * schedule() is the main scheduler function.3618 */3619 asmlinkage void __sched schedule(void)3620 {3621 struct task_struct *prev, *next;3622 long *switch_count;3623 struct rq *rq;3624 int cpu;3625 3626 need_resched:3627 preempt_disable();3628 cpu = smp_processor_id();3629 rq = cpu_rq(cpu);3630 rcu_qsctr_inc(cpu);3631 prev = rq->curr;3632 switch_count = &prev->nivcsw;
3630 先获得当前正在运行的进程,保存在prev中。
3639 /*3640 * Do the rq-clock update outside the rq lock:3641 */3642 local_irq_disable();3643 __update_rq_clock(rq);3644 spin_lock(&rq->lock);3645 clear_tsk_need_resched(prev);
3643 更新rq->prev_clock_raw和rq->clock
3645 清除TIF_NEED_SCHED标志
3647 if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {3648 if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&3649 unlikely(signal_pending(prev)))) {3650 prev->state = TASK_RUNNING;3651 } else {3652 deactivate_task(rq, prev, 1);3653 }3654 switch_count = &prev->nvcsw;3655 }
如果当前进程处在可中断睡眠状态,那么必须再次提升为运行进程。否则调用deactivate_task使得进程停止活动
3660 prev->sched_class->put_prev_task(rq, prev);3661 next = pick_next_task(rq, prev);
调用调度器类的put_prev_task通知调度器类当前的进程要被另外一个进程取代。这个操作并不意味着prev从就绪队列移除,而是提供了一个时机,执行一些记账工作。
pick_next_task选择下一个应该执行的进程。注意新选择的进程有可能就是原来的进程,比如就绪队列中仅有一个进程的情况。
3665 if (likely(prev != next)) {3666 rq->nr_switches++;3667 rq->curr = next;3668 ++*switch_count;3669 3670 context_switch(rq, prev, next); /* unlocks the rq */3671 } else3672 spin_unlock_irq(&rq->lock);
如果新进程不等于旧的进程,那么我们调用context_switch进行进程上下文的切换。
上下文切换
1929 static inline void1930 context_switch(struct rq *rq, struct task_struct *prev,1931 struct task_struct *next)1932 {............... 1945 if (unlikely(!mm)) {1946 next->active_mm = oldmm;1947 atomic_inc(&oldmm->mm_count);1948 enter_lazy_tlb(oldmm, next);1949 } else1950 switch_mm(oldmm, mm, next);
switch_mm是一个体系结构特定的函数,前换页全局目录以安装一个新的地址空间。对于arm平台来说,就是设置CP15协处理器TTB寄存器为新的pgd;对于X86来说,则是设置CR3寄存器为新的pgd。有人会问,这里切换了新的pgd,那么代码执行会不会不连续了? 没关系,因为现在是内核空间,内核地址空间的页面映射不会随着pgd而改变。
1966 /* Here we just switch the register state and the stack. */1967 switch_to(prev, next, prev);1968 1969 barrier();1970 /*1971 * this_rq must be evaluated again because prev may have moved1972 * CPUs since it called schedule(), thus the 'rq' on its stack1973 * frame will be invalid.1974 */1975 finish_task_switch(this_rq(), prev);1976 }
进程切换的硬件上下文是共享的CPU 寄存器,进程在切换时,硬件上下文保存在task_struct->thread字段中,注意thread是体系结构特定的,所以不是每个体系结构都需要保存寄存器到这个结构中的。swtich之后的代码,只有在当前进程下一次被选择执行时才会执行。
barrier确保switch_to和后面的finish_task_switch不会乱序执行。
- [进程管理] linux核心调度器
- linux进程管理 - 核心调度器
- Linux核心调度器之周期性调度器scheduler_tick--Linux进程的管理与调度(十八)
- Linux进程核心调度器之主调度器schedule--Linux进程的管理与调度(十九)
- Linux 进程调度管理
- linux进程管理与调度
- linux进程管理与调度
- Linux进程管理-调度-通信-
- Linux进程管理与调度
- linux进程管理与调度
- linux进程管理与调度
- linux进程管理与调度
- linux进程管理与调度
- Linux进程调度器概述--Linux进程的管理与调度(十五)
- Linux进程调度器的设计--Linux进程的管理与调度(十七)
- Linux 进程管理(一) 进程调度
- Linux进程调度之CFS调度器概述--Linux进程的管理与调度(二十四)
- Linux进程管理之CFS调度器分析
- system2之:4-文件系统管理(上)
- tomcat+memcache 集群 memcached-session-manager配置
- Annotation
- EL表达式截取字符串和格式化
- 集线器,路由器,交换机,网关设备之间的区别
- linux进程管理 - 核心调度器
- Git常用命令和操作(二)
- 如何调试php
- 由KIWI数据格式到MapInfo数据格式的转换研究
- 使用sharedpreference存储复杂对象
- 【快乐Python之一】List Comprehensions 和 Generator Expressions
- Java枚举类型
- 一个回调函数不能操作Qt主线程设置Gui的问题
- 解决error:could not open ...jvm.cfg