zoukankan      html  css  js  c++  java
  • 20135202闫佳歆--week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程--实验及总结

    week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程

    1.环境搭建:

    rm menu -rf
    git clone https://github.com/megnning/menu.git
    cd menu
    ls
    make rootfs
    qemu -kernel ../linux-3.18.6/arch/x86/boot/bzImage -initrd ../rootfs.img -s -S
    gdb
    file ../linux-3.18.6/vmlinux
    target remote:1234
    

    这一堆都是常用配置了,不赘述。

    2.使用gdb跟踪分析一个schedule()函数

    首先设置几个断点,分别是schedule, pick_next_task,context_switch,switch_to。最后一个switch_to设置失败了,我也无法定位到它,这个另外再说。
    设置断点如下:
    enter description here

    发现一个对schdule的调用:
    enter description here

    schdule函数定义如下:
    enter description here
    __visible保证任何地方都能够调用这个函数实现进程的切换。
    它首先要建立一个进程描述符,用来表示当前进程,而具体的调度过程交由__schedule()函数执行。

    由这张图可以看出来,两个重要的函数context_switch和pick_next_task函数都在__schedule函数中。
    enter description here

    首先看到的是pick_next_task函数:
    enter description here
    next指针指向的是由这个函数选择出来的进程,pick_next_task函数里面封装了许多进程调度的算法,通过这个函数就能寻找出适当的下一个执行进程。内部如下:
    enter description here

    然后看到了context_switch函数,这个函数的功能是进程上下文切换
    enter description here

    关于switch_to:
    switch_to是关键上下文切换的相关代码,切换堆栈和寄存器的状态,利用了prev和next两个参数:prev指向当前进程,next指向被调度的进程。
    这段是汇编相关无法跟踪,我只找到了一个__switch_to的函数,这个函数的返回值应该是eax的值。
    enter description here

    对于switch_to的代码分析如下:

    31  #define switch_to(prev, next, last)                    
    32  do {                                 
    33    /*                              
    34     * Context-switching clobbers all registers, so we clobber  
    35     * them explicitly, via unused output variables.     
    36     * (EAX and EBP is not listed because EBP is saved/restored  
    37     * explicitly for wchan access and EAX is the return value of   
    38     * __switch_to())                     
    39     */                                
    40    unsigned long ebx, ecx, edx, esi, edi;                
    41                                    
    42    asm volatile("pushfl
    	"      /* 保存当前进程的标志位 */   
    43             "pushl %%ebp
    	"        /* 保存当前进程的堆栈基址EBP   */ 
    44             "movl %%esp,%[prev_sp]
    	"  /* 保存当前栈顶ESP   */ 
    45             "movl %[next_sp],%%esp
    	"  /* 把下一个进程的栈顶放到esp寄存器中,完成了内核堆栈的切换,从此往下压栈都是在next进程的内核堆栈中。   */ 
    
    46             "movl $1f,%[prev_ip]
    	"    /* 保存当前进程的EIP   */ 
    47             "pushl %[next_ip]
    	"   /* 把下一个进程的起点EIP压入堆栈   */    
    48             __switch_canary                   
    49             "jmp __switch_to
    "  /* 因为是函数所以是jmp,通过寄存器传递参数,寄存器是prev-a,next-d,当函数执行结束ret时因为没有压栈当前eip,所以需要使用之前压栈的eip,就是pop出next_ip。  */ 
    
    以上四行代码实际是使用next进程的进程堆栈,但是还算成prev的进程执行,内核堆栈的切换和进程切换完成并不同时间。
    
    50             "1:	"               /* 认为next进程开始执行。 */         
    51             "popl %%ebp
    	"     /* restore EBP   */    
    52             "popfl
    "         /* restore flags */  
    53                                    
    54             /* output parameters 因为处于中断上下文,在内核中
                    prev_sp是内核堆栈栈顶
                    prev_ip是当前进程的eip */                
    55             : [prev_sp] "=m" (prev->thread.sp),     
    56               [prev_ip] "=m" (prev->thread.ip),  //[prev_ip]是标号        
    57               "=a" (last),                 
    58                                    
    59               /* clobbered output registers: */     
    60               "=b" (ebx), "=c" (ecx), "=d" (edx),      
    61               "=S" (esi), "=D" (edi)             
    62                                         
    63               __switch_canary_oparam                
    64                                    
    65               /* input parameters: 
                    next_sp下一个进程的内核堆栈的栈顶
                    next_ip下一个进程执行的起点,一般是$1f,对于新创建的子进程是ret_from_fork*/                
    66             : [next_sp]  "m" (next->thread.sp),        
    67               [next_ip]  "m" (next->thread.ip),       
    68                                         
    69               /* regparm parameters for __switch_to(): */  
    70               [prev]     "a" (prev),              
    71               [next]     "d" (next)               
    72                                    
    73               __switch_canary_iparam                
    74                                    
    75             : /* reloaded segment registers */           
    76            "memory");                  
    77  } while (0)
    

    简而言之,原理如下:

    schedule()函数选择一个新的进程来运行:

    使用pick_next_task内部封装的调度算法执行选择
    并调用context_switch进行上下文的切换
    context_switch又调用switch_to来进行关键上下文切换
    switch_to切换堆栈和寄存器的状态,利用了prev和next两个参数:
    prev指向当前进程,next指向被调度的进程。

    3.学习笔记

    请走链接 学习笔记

  • 相关阅读:
    DNS 域名系统服务
    tomcat
    mysql+redis
    centos7搭建lnmp
    redis安装
    redis 高级应用
    ubuntu,安装、配置和美化(1)
    解决大于5.7版本mysql的分组报错Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'userinfo.
    交换机常用命令
    SSH爆破应急响应
  • 原文地址:https://www.cnblogs.com/20135202yjx/p/5392618.html
Copyright © 2011-2022 走看看