博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
慢慢欣赏linux switch_to
阅读量:4070 次
发布时间:2019-05-25

本文共 3775 字,大约阅读时间需要 12 分钟。

以linux 4.16.3为例

A->B->C

A进程切换到B进程,B进程切换到C进程。
在A进程的栈看来, prev为A, next为B
在B进程的栈看来, prev为B, next为C

static __always_inline struct rq *context_switch(struct rq *rq, struct task_struct *prev, struct task_struct *next, struct rq_flags *rf){	struct mm_struct *mm, *oldmm;	mm = next->mm;	oldmm = prev->active_mm;		if (!mm) {		next->active_mm = oldmm;		mmgrab(oldmm);		enter_lazy_tlb(oldmm, next);	} else		switch_mm_irqs_off(oldmm, mm, next);		=>void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next, struct task_struct *tsk)		{			this_cpu_write(cpu_tlbstate.is_lazy, false);						load_new_mm_cr3(next->pgd, new_asid, true); // 重新加载页表						if (next != &init_mm)				this_cpu_write(cpu_tlbstate.last_ctx_id, next->context.ctx_id);			this_cpu_write(cpu_tlbstate.loaded_mm, next);			this_cpu_write(cpu_tlbstate.loaded_mm_asid, new_asid);						load_mm_cr4(next);			switch_ldt(real_prev, next);		}	switch_to(prev, next, prev);	=>#define switch_to(prev, next, last)					\	do {									\		prepare_switch_to(next);					\										\		((last) = __switch_to_asm((prev), (next)));			\	// 这是个函数调用, 会把下一条指令的地址也就是rip压到A的堆栈中, 也就是 barrier();对应的第一条指令的地址	} while (0)		=>ENTRY(__switch_to_asm)			UNWIND_HINT_FUNC			/*			 * Save callee-saved registers			 * This must match the order in inactive_task_frame			 */			pushq	%rbp			pushq	%rbx			pushq	%r12			pushq	%r13			pushq	%r14			pushq	%r15			/* switch stack */			movq	%rsp, TASK_threadsp(%rdi)			movq	TASK_threadsp(%rsi), %rsp	// 切换rsp指针, 以后push和pop就在B进程的内核栈进行		#ifdef CONFIG_CC_STACKPROTECTOR			movq	TASK_stack_canary(%rsi), %rbx			movq	%rbx, PER_CPU_VAR(irq_stack_union)+stack_canary_offset		#endif		#ifdef CONFIG_RETPOLINE			/*			 * When switching from a shallower to a deeper call stack			 * the RSB may either underflow or use entries populated			 * with userspace addresses. On CPUs where those concerns			 * exist, overwrite the RSB with entries which capture			 * speculative execution to prevent attack.			 */			FILL_RETURN_BUFFER %r12, RSB_CLEAR_LOOPS, X86_FEATURE_RSB_CTXSW		#endif			/* restore callee-saved registers */			popq	%r15			popq	%r14			popq	%r13			popq	%r12			popq	%rbx			popq	%rbp	// 切换rbp指针, 后续的局部变量都是B的			jmp	__switch_to	// jmp不压栈, 所以_switch_to 不会返回到jmp的下一条指令			=>__visible __notrace_funcgraph struct task_struct *			__switch_to(struct task_struct *prev_p, struct task_struct *next_p) // 两个入参分别是rdi rsi, 通过反汇编可以看出			{				struct thread_struct *prev = &prev_p->thread;	// 由于是寄存器传递参数, 所以prev_p还是指向A, 但是prev和next已经是在B的内核栈空间创建				struct thread_struct *next = &next_p->thread;	// next_p还是指向B								struct tss_struct *tss = &per_cpu(cpu_tss_rw, cpu);								save_fsgs(prev_p);								load_TLS(next, cpu);								savesegment(es, prev->es);				savesegment(ds, prev->ds);								load_seg_legacy(prev->fsindex, prev->fsbase,				next->fsindex, next->fsbase, FS);				load_seg_legacy(prev->gsindex, prev->gsbase,						next->gsindex, next->gsbase, GS);				switch_fpu_finish(next_fpu, cpu);				/*				 * Switch the PDA and FPU contexts.				 */				this_cpu_write(current_task, next_p);				this_cpu_write(cpu_current_top_of_stack, task_top_of_stack(next_p));				/* Reload sp0. */				update_sp0(next_p);								return prev_p;	// A只有rdi指向, 所以很容易丢失, 通过一个返回值返回用于别的用途, 返回到了 switch_to(prev, next, prev);的下一条语句, 即barrier();							}		END(__switch_to_asm)		barrier();	return finish_task_switch(prev);}

总之, 关键记住三点: 

第一: rsp决定push和pop; rbp决定局部变量. 当rsp和rbp切换之后, A进程就变成了B进程;
第二: call会压下一条指令的地址到本进程的内核栈中, 而jmp不会, 所以用 jmp    __switch_to 而不用 call __switch_to;
第三: 当进程上下文切换, 上一个进程的信息只能通过寄存器传递

关于Linux进程切换switch_to宏的一个细节(认识内联汇编)

https://blog.csdn.net/qq_42763389/article/details/89028749

switch_to()宏的理解

http://blog.chinaunix.net/uid-130624-id-2907709.html

【内核】进程切换 switch_to 与 __switch_to    (好文)

https://cnblogs.com/visayafan/archive/2011/12/10/2283660.html

你可能感兴趣的文章
JSP中文乱码总结
查看>>
Java-IO-File类
查看>>
Java-IO-java的IO流
查看>>
Java-IO-输入/输出流体系
查看>>
Java实现DES加密解密
查看>>
HTML基础
查看>>
Java IO
查看>>
Java NIO
查看>>
Java大数据:Hbase分布式存储入门
查看>>
Java大数据:全文搜索引擎Elasticsearch入门
查看>>
大数据学习:Hadoop入门学习书单
查看>>
大数据学习:Spark SQL入门简介
查看>>
大数据学习:Spark RDD操作入门
查看>>
大数据框架:Spark 生态实时流计算
查看>>
大数据入门:Hive和Hbase区别对比
查看>>
大数据入门:ZooKeeper工作原理
查看>>
大数据入门:Zookeeper结构体系
查看>>
大数据入门:Spark RDD基础概念
查看>>
大数据入门:SparkCore开发调优原则
查看>>
大数据入门:Java和Scala编程对比
查看>>