mirror of
https://github.com/gatieme/LDD-LinuxDeviceDrivers.git
synced 2026-08-19 01:22:36 +08:00
进程调度之优先级详解...
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@@ -581,8 +581,41 @@ rev->sched_class == class && rq->nr_running == rq->cfs.h_nr_running
|
||||
|
||||
##context_switch进程上下文切换
|
||||
-------
|
||||
|
||||
**进程上下文切换**
|
||||
|
||||
|
||||
**上下文切换**(有时也称做**进程切换**或**任务切换**)是指CPU从一个进程或线程切换到另一个进程或线程
|
||||
|
||||
稍微详细描述一下,上下文切换可以认为是内核(操作系统的核心)在 CPU 上对于进程(包括线程)进行以下的活动:
|
||||
|
||||
1. 挂起一个进程,将这个进程在 CPU 中的状态(上下文)存储于内存中的某处,
|
||||
|
||||
2. 在内存中检索下一个进程的上下文并将其在 CPU 的寄存器中恢复
|
||||
|
||||
3. 跳转到程序计数器所指向的位置(即跳转到进程被中断时的代码行),以恢复该进程
|
||||
|
||||
|
||||
上下文是指某一时间点CPU寄存器和程序计数器的内容, 广义上还包括内存中进程的弟子映射信息
|
||||
|
||||
上下文切换只能发生在内核态中, 上下文切换通常是计算密集型的。也就是说,它需要相当可观的处理器时间,在每秒几十上百次的切换中,每次切换都需要纳秒量级的时间。所以,上下文切换对系统来说意味着消耗大量的 CPU 时间,事实上,可能是操作系统中时间消耗最大的操作。
|
||||
Linux相比与其他操作系统(包括其他类 Unix 系统)有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少.
|
||||
|
||||
context_switch函数完成了进程上下文的切换, 其定义在[kernel/sched/core.c#L2715](http://lxr.free-electrons.com/source/kernel/sched/core.c#L2715),
|
||||
http://abcdxyzk.github.io/blog/2014/05/22/kernel-sched-tick/
|
||||
|
||||
执行如下操作
|
||||
|
||||
* 调用switch_mm(), 把虚拟内存从一个进程映射切换到新进程中
|
||||
|
||||
* 调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息
|
||||
|
||||
context_switch( )函数建立next进程的地址空间。进程描述符的active_mm字段指向进程所使用的内存描述符,而mm字段指向进程所拥有的内存描述符。对于一般的进程,这两个字段有相同的地址,但是,内核线程没有它自己的地址空间而且它的 mm字段总是被设置为 NULL
|
||||
|
||||
context_switch( )函数保证:如果next是一个内核线程, 它使用prev所使用的地址空间
|
||||
|
||||
进程切换并不是我们今天的重点, 我们将在后面的章节中着重讲解
|
||||
|
||||
|
||||
##need_resched与TIF_NEED_RESCHED标识
|
||||
-------
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* context_switch - switch to the new MM and the new thread's register state.
|
||||
*/
|
||||
static __always_inline struct rq *
|
||||
context_switch(struct rq *rq, struct task_struct *prev,
|
||||
struct task_struct *next)
|
||||
{
|
||||
struct mm_struct *mm, *oldmm;
|
||||
|
||||
prepare_task_switch(rq, prev, next);
|
||||
|
||||
mm = next->mm;
|
||||
oldmm = prev->active_mm;
|
||||
/*
|
||||
* For paravirt, this is coupled with an exit in switch_to to
|
||||
* combine the page table reload and the switch backend into
|
||||
* one hypercall.
|
||||
*/
|
||||
arch_start_context_switch(prev);
|
||||
|
||||
if (!mm) {
|
||||
next->active_mm = oldmm;
|
||||
atomic_inc(&oldmm->mm_count);
|
||||
enter_lazy_tlb(oldmm, next);
|
||||
} else
|
||||
switch_mm(oldmm, mm, next);
|
||||
|
||||
if (!prev->mm) {
|
||||
prev->active_mm = NULL;
|
||||
rq->prev_mm = oldmm;
|
||||
}
|
||||
/*
|
||||
* Since the runqueue lock will be released by the next
|
||||
* task (which is an invalid locking op but in the case
|
||||
* of the scheduler it's an obvious special-case), so we
|
||||
* do an early lockdep release here:
|
||||
*/
|
||||
lockdep_unpin_lock(&rq->lock);
|
||||
spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
|
||||
|
||||
/* Here we just switch the register state and the stack. */
|
||||
switch_to(prev, next, prev);
|
||||
barrier();
|
||||
|
||||
return finish_task_switch(prev);
|
||||
}
|
||||
|
||||
/*
|
||||
* nr_running and nr_context_switches:
|
||||
*
|
||||
* externally visible scheduler statistics: current number of runnable
|
||||
* threads, total number of context switches performed since bootup.
|
||||
*/
|
||||
unsigned long nr_running(void)
|
||||
{
|
||||
unsigned long i, sum = 0;
|
||||
|
||||
for_each_online_cpu(i)
|
||||
sum += cpu_rq(i)->nr_running;
|
||||
|
||||
return sum;
|
||||
}
|
||||
@@ -25,8 +25,8 @@ pick_next_task(struct rq *rq, struct task_struct *prev)
|
||||
{
|
||||
/* 调用cfs的选择函数pick_next_task找到最优的那个进程p*/
|
||||
p = fair_sched_class.pick_next_task(rq, prev);
|
||||
/* #define RETRY_TASK ((void *)-1UL)没有找到合适的进程 */
|
||||
if (unlikely(p == RETRY_TASK))
|
||||
/* #define RETRY_TASK ((void *)-1UL)有被其他调度气找到合适的进程 */
|
||||
if (unlikely(p == RETRY_TASK))
|
||||
goto again; /* 则遍历所有的调度器类找到最优的进程 */
|
||||
|
||||
/* assumes fair_sched_class->next == idle_sched_class */
|
||||
|
||||
Reference in New Issue
Block a user