zoukankan      html  css  js  c++  java
  • 2020-2021-1 20209315《Linux内核原理与分析》第九周作业

    作业信息

    这个作业属于哪个课程 <2020-2021-1Linux内核原理与分析)>
    这个作业要求在哪里 <2020-2021-1Linux内核原理与分析第一周作业>
    这个作业的目标 <了解进程的切换和linux系统的一般执行过程>
    作业正文 https://www.cnblogs.com/lmmn/p/14078634.html

    理解进程调度时机

    进程调度时机

    • 中断处理过程(包括时钟中断、I/O中断、系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule();
    • 内核线程可以直接调用schedule()进行进程切换,也可以在中断处理过程中进行调度,也就是说内核线程作为一类的特殊的进程可以主动调度,也可以被动调度;
    • 用户态进程无法实现主动调度,仅能通过陷入内核态后的某个时机点进行调度,即在中断处理过程中进行调度。

    进程切换的一般过程

    • 一个用户态进程A,发生中断,save cs:eip/esp/eflags等到内核堆栈;
    • SAVE_ALL保存现场。中断处理过程中直接调用或者中断返回前调用schedule,其中switch_to中的汇编代码做了关键的部分。切换了内核堆栈,从当前进程进入到下一个进程。从标号1开始就开始运行下一个进程(这个进程必须是曾经通过这个过程被切换出去的,比如新进程就不包含在内);
    • 恢复下一个进程的现场,pop出eip,esp。继续运行进程切换前用户态正在跑的程序。

    特殊情况:

    • 通过中断处理过程中的调度,用户态进程与内核进程之间互相切换,与一般情形类似;
    • 内核进程程主动调用 schedule 函数,只有进程上下文的切换,没有中断上下文切换;
    • 创建子进程的系统调用在子进程中的执行起点及返回用户态,如:fork;
      加载一个新的可执行程序后返回到用户态的情况,如:execve。

    调度策略与算法

    linux支持以下基本的调度策略,以满足不同进程的调度需求。这相当于按照进程的调度方式对进程进行分类,具体的策略如下。

    • SCHED_NORMAL
    • SCHED_FIFO
    • SCHED_RR
      SCHED_NORMAL是用于普通进程的调度类,而SCHED_FIFO和SCHED_RR是用于实时进程的调度类。

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

    配置运行MenuOS

    用如下代码更新menu

    cd LinuxKernel
    rm menu -rf
    git clone https://github.com/mengning/menu.git
    make rootfs
    

    用gdb设置断点,跟踪调试

    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
    b schedule
    b context_switch
    b switch_to
    b pick_next_task
    

    跟踪分析schedule()函数

    schedule()函数原型位于linux-3.18.6/kernel/sched/core.c中,其中主要的关键函数有pick_next_task(),这个函数调用之后,会根据某种进程调度策略选择出下一个运行的进程。紧接着是context_switch(),这是进程上下文切换函数,它的函数实现如下:

    context_switch(struct rq *rq, struct task_struct *prev,struct task_struct *next)
    {
        struct mm_struct *mm, *oldmm;
     
        prepare_task_switch(rq, prev, next);
     
        mm = next->mm;
        oldmm = prev->active_mm;
        arch_start_context_switch(prev);
        if (!mm) {
            next->active_mm = oldmm;
            atomic_inc(&oldmm->mm_count);
            enter_lazy_tlb(oldmm, next);
        } else
            switch_mm(oldmm, mm, next);
     
        if (!prev->mm) {
            prev->active_mm = NULL;
            rq->prev_mm = oldmm;
        }
        spin_release(&rq->lock.dep_map, 1, _THIS_IP_);
            context_tracking_task_switch(prev, next);
            
            switch_to(prev, next, prev);        //关键函数
     
            barrier();
        /*
         * this_rq must be evaluated again because prev may have moved
         * CPUs since it called schedule(), thus the 'rq' on its stack
         * frame will be invalid.
         */
        finish_task_switch(this_rq(), prev);
    }
    

    在context_switch()函数中,最为重要的是switch_to()函数,它主要是由内联汇编实现,功能是完成进程切换。

    #define switch_to(prev, next, last)                    
    do {                                 
             unsigned long ebx, ecx, edx, esi, edi;              
             asm volatile("pushfl
    	"            /* 保存当前进程的flags */   
                       "pushl %%ebp
    	"          /* 把当前进程的当前的ebp压入当前进程的栈   */ 
                       "movl %%esp,%[prev_sp]
    	"  /*保存当前的esp到prev->thread.sp指向的内存中   */ 
                       "movl %[next_sp],%%esp
    	"  /* 重置esp,把下个进程的next->thread.sp赋予esp */ 
                       "movl $1f,%[prev_ip]
    	"   /*把1:的代码在内存中存储的地址保存到prev->thread.ip中*/ 
                       "pushl %[next_ip]
    	"        /*重置eip   */    
                        __switch_canary                   
                       "jmp __switch_to
    "      /*跳转到switch_to函数*/
                       "1:	"                        
                       "popl %%ebp
    	"       /* 重置ebp  */    
                       "popfl
    "     /* 重置flags*/  
                       : [prev_sp] "=m" (prev->thread.sp),     
                         [prev_ip] "=m" (prev->thread.ip),        
                         "=a" (last),                 
                         "=b" (ebx), "=c" (ecx), "=d" (edx),      
                         "=S" (esi), "=D" (edi)             
                         __switch_canary_oparam                
                           : [next_sp]  "m" (next->thread.sp),        
                         [next_ip]  "m" (next->thread.ip),       
                         [prev]     "a" (prev),              
                         [next]     "d" (next)               
                         __switch_canary_iparam                
                         "memory");                  
    } while (0)
    
  • 相关阅读:
    关于线程池,那些你还不知道的事
    Java发送邮件
    原来实现项目多环境打包部署是如此的简单
    史上最全的maven的pom.xml文件详解
    Linux系统基础知识整理(一)
    计算机启动过程的简单介绍 计算机启动流程 计算机BIOS作用 POST 开机自检 计算机启动顺序 分区表 操作系统启动
    交换机工作原理、MAC地址表、路由器工作原理详解
    $(function(){})和$(document).ready(function(){}) 的区别
    关于RAM与ROM的区别与理解
    CDN的作用与基本过程
  • 原文地址:https://www.cnblogs.com/lmmn/p/14078634.html
Copyright © 2011-2022 走看看