慢慢欣赏linux 之 last = switch_to(prev, next)分析

发布于:2025-06-09 ⋅ 阅读:(20) ⋅ 点赞:(0)

last = switch_to(prev, next); 为什么需要定义last作为调用switch_to之前的prev的引用
原因如下:

struct task_struct * switch_to(struct task_struct *prev,
		struct task_struct *next)
{
	... ...
	return cpu_switch_to(prev, next);
	=> .global cpu_switch_to
	cpu_switch_to:
		add     x8, x0, #THREAD_CPU_CONTEXT
		mov     x9, sp
		stp     x19, x20, [x8], #16
		stp     x21, x22, [x8], #16
		stp     x23, x24, [x8], #16
		stp     x25, x26, [x8], #16
		stp     x27, x28, [x8], #16
		stp     x29, x9, [x8], #16
		str     lr, [x8]

		add     x8, x1, #THREAD_CPU_CONTEXT
		ldp     x19, x20, [x8], #16
		ldp     x21, x22, [x8], #16
		ldp     x23, x24, [x8], #16
		ldp     x25, x26, [x8], #16
		ldp     x27, x28, [x8], #16
		ldp     x29, x9, [x8], #16
		ldr     lr, [x8]
		mov     sp, x9	// 切换了 sp, 导致 prev 的堆栈空间切换到了 next 空间的堆栈
		ret
}

我们再来看 switch_to 的调用者

static void __schedule(void)
{
	struct task_struct *prev, *next, *last;
	struct run_queue *rq = &g_rq;

	prev = current;
	=> #define current get_current()
		=> static inline struct task_struct *get_current(void)
		{
			return (struct task_struct *) (current_stack_pointer & ~(THREAD_SIZE - 1));
				=> register unsigned long current_stack_pointer asm ("sp");	// 这时 prev 指向的 sp 已经是 next 进程空间的堆栈
		}

	/* 检查是否在中断上下文中发生了调度 */
	schedule_debug(prev);

	/* 关闭中断包含调度器,以免中断发生影响调度器 */
	raw_local_irq_disable();

	if (prev->state)
		dequeue_task(rq, prev);

	next = pick_next_task(rq, prev);
	clear_task_resched(prev);
	if (next != prev) {
		last = switch_to(prev, next);
		/*
		 * switch_to函数是用来切换prev进程到next进程。
		 * switch_to函数执行完成之后,已经切换到next
		 * 进程,整个栈和时空都发生变化了,不能使用这
		 * 里的prev变量来表示prev进程,只能通过aarch64
		 * 的x0寄存器来获取prev进程的task_struct。
		 *
		 * 在switch_to函数使用x0寄存器来传递prev进程
		 * task_struct,返回值也是通过x0寄存器,因此
		 * 这里last变量表示prev进程的task_struct
		 */
		rq->nr_switches++;
		rq->curr = current;
	}

	/* 由next进程来收拾prev进程的现场 */
	schedule_tail(last);
}

参考:

ARM64体系结构编程与实践


网站公告

今日签到

点亮在社区的每一天
去签到