lab5:深入理解进程切换

linux 中进程切换涉及到一个调用链:

schedule() –> context_switch() –> switch_to –> __switch_to()

先是 context_switch 函数,content_switch 函数位于 Linux 内核源码目录的 kernel/sched/core.c 中,是 schedule 函数中实现进程切换的函数.
content_switch 函数有三个参数:rq、prev、next,其中 rq 指向本次进程切换发生的 running queue;prev 和 next 分别指向切换前后进程的进程描述符。

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

执行过程

1. prepare_task_switch

在进程切换之前,首先执行调用每个体系结构都必须定义的prepare_task_switch挂钩,,这使得内核执行特定于体系结构的代码,,为切换做事先准备。

static inline void
prepare_task_switch(struct rq *rq, struct task_struct *prev,
            struct task_struct *next)
{
    sched_info_switch(rq, prev, next);
    perf_event_task_sched_out(prev, next);
    fire_sched_out_preempt_notifiers(prev, next);
    prepare_lock_switch(rq, next);
    prepare_arch_switch(next);
}

2. arch_start_context_switch

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

3. prepare_lock_switch

与死锁检测 lockdep 有关。

4. switch_to

最后用switch_to完成了进程的切换,该函数切换了寄存器状态和栈,新进程在该调用后开始执行,而switch_to之后的代码只有在当前进程下一次被选择运行时才会执行。
执行环境的切换是在switch_to()中完成的, switch_to完成最终的进程切换,它保存原进程的所有寄存器信息,恢复新进程的所有寄存器信息,并执行新的进程。
内核在switch_to中执行如下操作:

进程切换, 即esp的切换, 由于从esp可以找到进程的描述符
硬件上下文切换, 设置ip寄存器的值, 并jmp到__switch_to函数
堆栈的切换, 即ebp的切换, ebp是栈底指针, 它确定了当前用户空间属于哪个进程

5. finish_task_switch

这个函数和前面的prepare_task_switch相对应。finish_task_switch完成一些清理工作, 使得能够正确的释放锁。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值