目录
一、调度场景分析
1、产生时钟中断。处理器采用定时器来周期性地执行。调度器利用时钟中断来定时检测当前运行的线程是否需要调度。当需要调度时,设置need_resched标志位
2、当时钟中断返回,根据linux内核是否支持内核抢占来确定是否需要调度:
不支持内核抢占的内核
支持内核抢占
二、如何让新进程执行
1.进程的硬件上下文。保存在进程的cpu_context数据结构。
2.pt_regs
int copy_thread_tls(unsigned long clone_flags, unsigned long stack_start,
unsigned long stk_sz, struct task_struct *p, unsigned long tls)
{
} else {
memset(childregs, 0, sizeof(struct pt_regs));
childregs->pstate = PSR_MODE_EL1h;//5 处理器状态 第0位 栈指针选择符,1:选择栈之战寄存器SP_EL1 2:3 异常级别,值1表示异常级别1
if (IS_ENABLED(CONFIG_ARM64_UAO) &&
cpus_have_const_cap(ARM64_HAS_UAO))
childregs->pstate |= PSR_UAO_BIT;
if (arm64_get_ssbd_state() == ARM64_SSBD_FORCE_DISABLE)
set_ssbs_bit(childregs);
if (system_uses_irq_prio_masking())
childregs->pmr_save = GIC_PRIO_IRQON;
p->thread.cpu_context.x19 = stack_start;//函数地址,用来创建内核线程的函数kernel_thread的第一参数
p->thread.cpu_context.x20 = stk_sz;//参数 ,用来创建内核线程的函数kernel_thread的第二参数
}
p->thread.cpu_context.pc = (unsigned long)ret_from_fork;//子进程的程序计数器,调度入口
p->thread.cpu_context.sp = (unsigned long)childregs;//sp指向内核栈底部pt_regs起始位置
}
三、调度的本质
-
进程A在用户空间运行;
-
发生中断
-
CPU打断正在运行的用户进程A,处于异常模式。CPU会跳转到异常向量表的el0_irq里。在汇编函数el0_irq中,首先把中断现场保存到进程A的pt_regs栈
-
处理中断
-
调度滴答处理函数,返回el0_irq汇编函数里。即将返回现场前,ret_to_user汇编函数会检查当前进程是否需要调度。
-
若当前进程需要调度,调用schedule()函数选择下一个进程并切换 ;(switch_to函数)
-
切换函数返回,CPU开始运行内核线程B;进程需要为前一个进程做收尾工作,比如调用raw_spin_unlock_irq来释放锁并打开本地中断。见finish_task_switch函数。
-
CPU沿着内核线程B保存的栈帧回溯,一直返回。返回路径finish_task_switch->el1_preempt->el1_irq
-
在el1_irq汇编函数里把上一次发生中断时保存在栈里的中断现场进行恢复,最后从上一次中断的地方开始执行内核线程B的代码。
评论(0)
您还未登录,请登录后发表或查看评论