【潘恒 原创做品转载请注明出处 《Linux内核分析》MOOC课程 "http://mooc.study.163.com/course/USTC 1000029000 " 】linux
使用gdb跟踪分析一个schedule()函数,理解Linux系统中进程调度的时机。git
登录实验楼虚拟机http://www.shiyanlou.com/courses/195github
打开shell终端,执行如下命令:算法
cd LinuxKernel rm -rf menu git clone https://github.com/mengning/menu.git cd menu mv test_exec.c test.c make rootfs
能够看到编译出来的系统已经有了exec命令shell
能够经过增长-s -S启动参数打开调试模式函数
qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img -s -S
打开gdb进行远程调试spa
gdb file ../linux-3.18.6/vmlinux target remote:1234
设置断点线程
b schedule
b context_switch
b switch_to
b pick_next_task
经过实验可知schedule()函数用来选择一个新的进程来运行,并调用context_switch()进行上下文的切换,这个宏调用switch_to()来进行关键上下文切换,其中pick_next_task()函数封装了进程调度算法。调试
进程调度时机有三:rest
一、中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
二、内核线程能够直接调用schedule()进行进程切换,也能够在中断处理过程当中进行调度,也就是说内核线程做为一类的特殊的进程能够主动调度,也能够被动调度;
三、用户态进程没法实现主动调度,仅能经过陷入内核态后的某个时机点进行调度,即在中断处理过程当中进行调度。
进程切换:为了控制进程的执行,内核必须有能力挂起正在CPU上运行的进程,并恢复之前挂起的某个进程的执行。这种行为被称为进程切换(process switch)、任务切换(task switch)或上下文切换(context switch)。
挂起正在CPU上执行的进程,与中断时保存现场是不一样的,中断先后是在同一个进程上下文中,只是由用户态转向内核态执行。
进程上下文包含了进程执行须要的全部信息,包括:
一、用户地址空间:包括程序代码,数据,用户堆栈等
二、控制信息:进程描述符,内核堆栈等
三、硬件上下文(与中断保存硬件上下文的方法不一样)
Linux系统的通常执行过程
最通常的状况:
正在运行的用户态进程X切换到运行用户态进程Y的过程
一、正在运行的用户态进程X
二、发生中断——save cs:eip/esp/eflags(current) to kernel stack,then load cs:eip(entry of a specific ISR) and ss:esp(point to kernel stack).
三、SAVE_ALL //保存现场
四、中断处理过程当中或中断返回前调用了schedule(),其中的switch_to作了关键的进程上下文切换
五、标号1以后开始运行用户态进程Y(这里Y曾经经过以上步骤被切换出去过所以能够从标号1继续执行)
六、restore_all //恢复现场
七、iret - pop cs:eip/ss:esp/eflags from kernel stack
八、继续运行用户态进程Y
几种特殊状况:
一、经过中断处理过程当中的调度时机,用户态进程与内核线程之间互相切换和内核线程之间互相切换,与最通常的状况很是相似,只是内核线程运行过程当中发生中断没有进程用户态和内核态的转换;
二、内核线程主动调用schedule(),只有进程上下文的切换,没有发生中断上下文的切换,与最通常的状况略简略;
三、建立子进程的系统调用在子进程中的执行起点及返回用户态,如fork;
四、加载一个新的可执行程序后返回到用户态的状况,如execve;