中断处理过程(时钟中断、I/O中断、系统调用和异常)中或返回用户态根据need_resched标记调用。
内核线程直接调用进行进程切换,也可以在中断处理过程中进行调度,内核线程可以主动调度,也可以被动调度。
用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,在中断处理过程中调度。
schedule()函数开始,禁用内核抢占并初始化一些局部变量,此时,schedule()检查运行队列中剩余的可运行进程数,如果有可调用进程,开始调用。
schedule()函数中在switch_to宏之后紧接着的指令并不由next进程立即执行,而是稍后当调度程序选择prev又执行时由prev执行。 prev局部变量并不指向开始描述schedule()时所替换出去的原来的那个进程,而是指向prev被调度时由prev替换出的原来那个进程。
紧接着context_switch()函数调用之后,宏barrier()产生一个代码优化屏障。执行finish_task_switch()函数。
schedule()函数最后执行的是:重新获得大内核锁,重新启动内核抢占,并检查是否其他进程设置了当前进程的TIF_RESCHED标志,如果是,整个schedule()函数重新开始执行。
"movl $1f,%[prev_ip]\n\t"保存当前进程的eip,恢复的时候从prev_ip来恢复eip "pushl %[next_ip]\n\t"把下个进程的起点ip位置压到next进程堆栈栈顶(起点) "jmp __switch_to\n" jmp通过prev和next寄存器的方式传递参数 "popl %%ebp\n\t"恢复上下文,next曾经push过ebp context_switch(struct rq *rq,struct task_struct *prev context_switch()上下文切换;建立next的地址空间。 struct task_struct *next) { struct mm_struct *mm, *oldmm; prepare_task_switch(rq, prev, next); mm= next -> mm; mm字段指向进程所拥有的内存描述符。 oldmm = prev -> active_mm; active_mm字段指向进程所使用的内存描述符。 arch_start_context_switch(prev); if (!mm) { next->active_mm = oldmm; 如果next是内核线程,则线程使用prev所使用的地址空间; atomic_inc(&oldmm->mm_count); schedule()函数把该线程设置为懒惰TLB模式。 enter_lazy_tlb(oldmm,next); }else switch_mm(oldmm,mm,next); 如果next是普通进程,schedule()函数用next替换prev的地址空间 if (!prev->mm) { prev->active_mm = NULL; 如果prev是内核线程或正退出的进程,context_switch()函数就把 rq->prev_mm = oldmm; 指向prev内存描述的支撑保存到运行队列的prev_mm字段中; } 并且重新设置prev->active_mm。 spin_release(&rq->lock,dep_map,1,_THIS_IP_); context_tracking_task_switch(prev,next); context_switch()可以调用switch_to执行prev和next之间的进程切换 finish_task_switch(this_rq(),prev); }
cd LinuxKernel rm menu -rf git clone https://github.com/mengning/menu.git cd menu mv test_exec.c test.c make rootfs
shell1 qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -inird rootfs.img -s -S shell2 gdb file linux-3.18.6/vmlinux target remote:1234 b schedule c
通过本次课程的学习,我对进程调度时机及进程调度与进程切换的过程有了一定的认识。进程调度的时机:中断处理过程中,直接调用schedule,或者返回用户态时根据need_resched标记调用schedule;内核线程可以直接调用schedule进行进程切换,也可以在中断处理过程中进行调度,即内核线程作为一类特殊的进程可以主动调度,也可别动调度;用户态进程无法实现主动调度,只能在中断处理过程中进行调度。进程切换:schedule函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,这个宏调用switch_to来进行关键上下文的切换。