linux汇编resw,慢慢欣赏linux switch_to

以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)));\

} 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)

movqTASK_threadsp(%rsi), %rsp// 切换rsp指针, 以后push和pop就在B进程的内核栈进行

#ifdef CONFIG_CC_STACKPROTECTOR

movqTASK_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进程。

关于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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值