lab5:深入理解进程切换

发布时间 2023-04-30 11:06:19作者: monoSirius

linux操作系统分析Lab5:深入理解进程切换

context_switch 函数

content_switch 函数位于 Linux 内核源码目录的 kernel/sched/core.c 中,是 schedule 函数中实现进程切换的函数

/*
 * 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 rq_flags *rf)
{
	prepare_task_switch(rq, prev, next);

	/*
	 * 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);

	/*
	 * kernel -> kernel   lazy + transfer active
	 *   user -> kernel   lazy + mmgrab() active
	 *
	 * kernel ->   user   switch + mmdrop() active
	 *   user ->   user   switch
	 */
	if (!next->mm) {                                // to kernel
		enter_lazy_tlb(prev->active_mm, next);

		next->active_mm = prev->active_mm;
		if (prev->mm)                           // from user
			mmgrab(prev->active_mm);
		else
			prev->active_mm = NULL;
	} else {                                        // to user
		membarrier_switch_mm(rq, prev->active_mm, next->mm);
		/*
		 * sys_membarrier() requires an smp_mb() between setting
		 * rq->curr / membarrier_switch_mm() and returning to userspace.
		 *
		 * The below provides this either through switch_mm(), or in
		 * case 'prev->active_mm == next->mm' through
		 * finish_task_switch()'s mmdrop().
		 */
		switch_mm_irqs_off(prev->active_mm, next->mm, next);

		if (!prev->mm) {                        // from kernel
			/* will mmdrop() in finish_task_switch(). */
			rq->prev_mm = prev->active_mm;
			prev->active_mm = NULL;
		}
	}

	rq->clock_update_flags &= ~(RQCF_ACT_SKIP|RQCF_REQ_SKIP);

	prepare_lock_switch(rq, next, rf);

	/* Here we just switch the register state and the stack. */
	switch_to(prev, next, prev);
	barrier();

	return finish_task_switch(prev);
}

content_switch 函数有三个参数:rq、prev、next,其中 rq 指向本次进程切换发生的 running queue;prev 和 next 分别指向切换前后进程的进程描述符。

执行过程

1. prepare_task_switch()

该函数在进程切换之前调用,内核会执行与体系结构相关的一些调测指令。上下文切换完成后,必须调用 finish_task_switch,即这两个函数一定是要成对出现的。

2. arch_start_context_switch()

该函数给各个体系结构专有的开始上下文切换的工作提供了入口,不同体系结构的实现不同。

3. switch_mm_irqs_off() 进程地址切换

switch_mm更换通过task_struct->mm描述的内存管理上下文,该工作的细节取决于处理器,主要包括加载页表,刷出地址转换后备缓冲器(部分或者全部),向内存管理单元(MMU)提供新的信息。

3. switch_to()

调用switch_to(),从上一个进程的处理器状态切换到新进程的处理器状态。这包括保存、恢复栈信息和寄存器信息。
switch_to切换处理器寄存器的呢内容和内核栈(虚拟地址空间的用户部分已经通过switch_mm变更,其中也包括了用户状态下的栈,因此switch_to不需要变更用户栈,只需变更内核栈),此段代码严重依赖于体系结构,且代码通常都是用汇编语言编写。

实验总结

通过上述分析,我们已经得到了进程切换过程中发生的主要操作:

__schedule   // kernel/sched/core.c
->context_switch
  ->switch_mm_irqs_off   //进程地址空间切换
  ->switch_to //处理器状态切换

实际上在不同的体系结构下,只有switch_to函数是有较大差异的,因为不同的体系结构下的寄存器和堆栈有所不同,涉及具体的操作自然也是不同的,其他的函数基本上是通用的。