本文主要是以context_switch为起点,分析了整个进程切换过程中的基本操作和基本的代码框架,很多细节,例如tlb的操作,cache的操作,锁的操作等等会在其他专门的文档中描述。进程切换包括体系结构相关的代码和系统结构无关的代码。第二、三、四分别描述了context_switch的代码脉络,后面的章节是以ARM64为例子,讲述了具体进程地址空间的切换过程和硬件上下文的切换过程。
二、context_switch代码分析
在kernel/sched/core.c中有一个context_switch函数,该函数用来完成具体的进程切换,代码如下(本文主要描述进程切换的基本逻辑,因此部分代码会有删节):
static inline struct rq * context_switch(struct rq *rq, struct task_struct *prev,
struct task_struct *next)------------------(1)
{
struct mm_struct *mm, *oldmm;
mm = next->mm;
oldmm = prev->active_mm;-------------------(2)
if (!mm) {---------------------------(3)
next->active_mm = oldmm;
atomic_inc(&oldmm->mm_count);
enter_lazy_tlb(oldmm, next);-----------------(4)
} else
switch_mm(oldmm, mm, next); ---------------(5)
if (!prev->mm) {------------------------(6)
prev->active_mm = NULL;
rq->prev_mm = oldmm;
}
switch_to(prev, next, prev);------------------(7)
barrier();
return finish_task_switch(prev);
}
(1)一旦调度器算法确定了pre task和next task,那么就可以调用context_switch函数实际执行进行切换的工作了,这里我们先看看参数传递情况:
rq:在多核系统中,进程切换总是发生在各个cpu core上,参数rq指向本次切换发生的那个cpu对应的run queue
prev:将要被剥夺执行权利的那个进程
next:被选择在该cpu上执行的那个进程
(2)next是马上就要被切入的进程(后面简称B进程),prev是马上就要被剥夺执行权利的进程(后面简称A进程)。mm变量指向B进程的地址空间描述符,oldmm变量指向A进程的当前正在使用的地址空间描述符(active_mm)。对于normal进程,其任务描述符(task_struct)的mm和active_mm相同,都是指向其进程地址空间。对于内核线程而言,其task_struct的mm成员为NULL(内核线程没有进程地址空间),但是,内核线程被调度执行的时候,总是需要一个进程地址空间,而active_mm就是指向它借用的那个进程地址空间。
(3)mm为空的话,说明B进程是内核线程,这时候,只能借用A进程当前正在使用的那个地址空间(prev->active_mm)。注意:这里不能借用A进程的地址空间(prev->mm),因为A进程也可能是一个内核线程,不拥有自己的地址空间描述符。
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-76979-1.html