理解进程调度时机并跟踪分析进程调度与进程切换的过程

来源:互联网 发布:迦太基 汉尼拔 知乎 编辑:程序博客网 时间:2024/04/30 11:08

董涛 

原创作品转载请注明出处

《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

 

基于实验楼网站提供的《Linux内核分析》实验平台,使用gdb跟踪分析一个schedule()函数 ,验证对Linux系统进程调度与进程切换过程的理解。


在Linux系统中,系统对进程的调度与切换是通过schedule()函数实现的。schedule函数定义如下:

asmlinkage __visible void __sched schedule(void)
     {
         struct task_struct *tsk = current;
         sched_submit_work(tsk);
         __schedule();
      }


在Linux内核代码中搜索schedule()函数,可以看出,schedule()函数在系统调用等函数中被大量引用,原因是根据Linux机制,进程调度与进程切换是在Linux内核态中实现的,内核态中的函数可以主动调用schedule()函数。


schedule()函数选择一个新的进程来运行,并调用context_switch进行上下文的切换,context_switch是一个宏,这个宏调用switch_to()函数来进行关键上下文切换,具体的结构关系如下所示:
    next = pick_next_task(rq, prev);    //进程调度算法都封装这个函数内部
    context_switch(rq, prev, next);      //进程上下文切换
    switch_to利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程


下一步,选择包含schedule()函数的thaw_processes()函数作为实例,在实验楼网站的实验平台上跟踪thaw_processes()函数中的schedule()函数,具体的操作截图如下:





在switch_to函数中,通过嵌入如下的汇编代码实现进程上下文的切换以及与中断上下文的切换:

asm volatile("pushfl\n\t"        /* 保存标志位 */   
             "pushl %%ebp\n\t"        /* 保存    EBP   */   
            "movl %%esp,%[prev_sp]\n\t"    /* 保存    ESP   */
             "movl %[next_sp],%%esp\n\t"    /* 恢复 ESP   */
             "movl $1f,%[prev_ip]\n\t"    /* 保存    EIP   */   
             "pushl %[next_ip]\n\t"    /* 恢复 EIP   */   
             __switch_canary                   

             "jmp __switch_to\n"    /* 跳转l  */   
             "1:\t"                       
             "popl %%ebp\n\t"        /* 恢复 EBP   */   
             "popfl\n"            /* 恢复标志位*/   
                                   
             /* output parameters */               
             : [prev_sp] "=m" (prev->thread.sp),       
               [prev_ip] "=m" (prev->thread.ip),       
               "=a" (last),                   
                                  
               /* clobbered output registers: */       
               "=b" (ebx), "=c" (ecx), "=d" (edx),       
               "=S" (esi), "=D" (edi)               
                                          
               __switch_canary_oparam               
                                   
               /* input parameters: */               
             : [next_sp]  "m" (next->thread.sp),       
               [next_ip]  "m" (next->thread.ip),       
                                          
               /* regparm parameters for __switch_to(): */   
               [prev]     "a" (prev),               
               [next]     "d" (next)               
                                    \
               __switch_canary_iparam               
                                    \
             : /* reloaded segment registers */           
            "memory");                   


根据如上简介,可以总结Linux系统一般的执行过程是:程序发出请求——>系统中断——>进入终端控制台设备驱动程序或系统调用等内核态函数——>创建或者切换进程——>调用fork函数创建进程的子进程——>调用exec系统调用,将发出请求的程序的可执行文件装入内存——>从exec系统调用返回——完成程序的执行。

0 0