从系统调用或者异常中断返回用户空间时,thread_flags 被设置成TIF_NEED_RESCHED 会发生调度,当然还有其他几个时机也会发生调度,这里主要介绍中断返回用户空间时的情况。
linux-4.10/arch/arm64/kernel/entry.S
744 ret_fast_syscall:
745 disable_irq // disable interrupts
746 str x0, [sp, #S_X0] // returned x0
747 ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for syscall tracing
748 and x2, x1, #_TIF_SYSCALL_WORK
749 cbnz x2, ret_fast_syscall_trace
750 and x2, x1, #_TIF_WORK_MASK
751 cbnz x2, work_pending //跳到work_pending
752 enable_step_tsk x1, x2
753 kernel_exit 0
761 work_pending:
762 mov x0, sp // 'regs'
763 bl do_notify_resume //跳到do_notify_resume
764 #ifdef CONFIG_TRACE_IRQFLAGS
765 bl trace_hardirqs_on // enabled while in userspace
766 #endif
767 ldr x1, [tsk, #TSK_TI_FLAGS] // re-check for single-step
768 b finish_ret_to_user
这个流程在前面的博客中有贴出来过,在应用发生crash 异常的时候,会走到这里。
linux-4.10/arch/arm64/kernel/signal.c
402 asmlinkage void do_notify_resume(struct pt_regs *regs,
403 unsigned int thread_flags)
404 {
405 /*
406 * The assembly code enters us with IRQs off, but it hasn't
407 * informed the tracing code of that for efficiency reasons.
408 * Update the trace code with the current status.
409 */
410 trace_hardirqs_off();
411 do {
412 if (thread_flags & _TIF_NEED_RESCHED) {
413 schedule(); //如果调度的标识位被设置,则进行调度
414 } else {
...
430 }
431
432 local_irq_disable();
433 thread_flags = READ_ONCE(current_thread_info()->flags);
434 } while (thread_flags & _TIF_WORK_MASK);
435 }
thread_flags 被设置成(或者添加)_TIF_NEED_RESCHED是,这时候会调用schedule()进行调度
linux-4.10/kernel/sched/core.c
3451 asmlinkage __visible void __sched schedule(void)
3452 {
3453 struct task_struct *tsk = current;
3454
3455 sched_submit_work(tsk);
3456 do {
3457 preempt_disable(); //禁止抢占
3458 __schedule(false);
3459 sched_preempt_enable_no_resched(); //开启抢占
3460 } while (need_resched());
3461 }
3462 EXPORT_SYMBOL(schedule);
接着跑到__schedule() 参数preempt 表示是否运行抢占,这里参数的参数是false,也就是不允许抢占。
3334 static void __sched notrace __schedule(bool preempt)
3335 {
3336 struct task_struct *prev, *next;
3337 unsigned long *switch_count;
3338 struct pin_cookie cookie;
3339 struct rq *rq;
3340 int cpu;
3341
/*
* linux-4.10/include/linux/smp.h
* # define smp_processor_id() raw_smp_processor_id()
*
* linux-4.10/arch/arm64/include/asm/smp.h
* #define raw_smp_processor_id() (*raw_cpu_ptr(&cpu_number)) //虽然写法比较奇怪,认为就是当前运行的cpu id就可以了,如果是下面的写就很直观
* linux-4.10/arch/arm/include/asm/smp.h
* #define raw_smp_processor_id() (current_thread_info()->cpu)
*/
3342 cpu = smp_processor_id();
3343 rq = cpu_rq(cpu); //获取运行队列
3344 prev = rq->curr; //当前运行的task_struct
3345
3346 schedule_debug(prev);
3347
3348 if (sched_feat(HRTICK)) //是否开启了HRTICK
3349 hrtick_clear(rq); //
3350
3351 local_irq_disable(); //关闭中断
3352 rcu_note_context_switch(); //RCU(Read-Copy Update),对于被RCU保护的共享数据结构,读者不需要获得任何锁就可以访问它
3353
3354 /*
3355 * Make sure that si