• linux进程解析--进程切换


    为了控制进程的执行,linux内核必须有能力挂起正在cpu上运行的进程,换入想要切换的进程,也就是恢复以前某个挂起的进程,这就是linux的进程切换。
     1进程切换的时机
    一般来说,进程切换都是发生在从中断或者系统调用返回用户态的时候,最常见的是时钟中断。在允许内核抢占的情况下,系统调用被中断打断也有可能会引发进程切换。中断处理和系统调用处理都发生在内核态,所以进程之间的切换实际上也是发生在了内核态。
    2进程切换做的工作
    2.1切换页全局目录以安装一个新的地址空间。
    2.2切换内核态堆栈和硬件上下文,硬件上下文提供了新进程运行所需要的所有的寄存器的所有信息。
    3进程切换的过程
    进程切换统一发生在schedule()函数中,在这个里面我们仅仅先来关注切换的过程,在内核代码中切换进程的过程在switch_to()中,switch_to()是一个宏,其定义如下:
    #define switch_to(prev,next,last) do {
    unsigned long esi,edi;
    asm volatile("pushfl "
        "pushl %%ebp "
        "movl %%esp,%0 " /* save ESP */
        "movl %5,%%esp " /* restore ESP */
        "movl $1f,%1 " /* save EIP */
        "pushl %6 " /* restore EIP */
        "jmp __switch_to "
        "1: "
        "popl %%ebp "
        "popfl"
        :"=m" (prev->thread.esp),"=m" (prev->thread.eip),
         "=a" (last),"=S" (esi),"=D" (edi)
        :"m" (next->thread.esp),"m" (next->thread.eip),
         "2" (prev), "d" (next));
    } while (0)
    3.1参数说明
    prev存放的是当前的current进程描述符指针。
    next存放的是需要被替换进来的进程的描述符指针。
    last比较麻烦,在一次进程被切换出去又切换进来的过程中,一般会涉及到3个进程,进程A,B,C,当进程A运行时,切换到进程B,然后再系统运行一段时间后,系统中运行的进程变为了进程C,然后由进程调度程序调度进程A运行,当调度进程A运行时,进程A使用的是自己的内核栈,里面的prev和next就还是从A切换到B时的prev, next,即prev = A, next=B.然而本次从C切换到A应该将prev改变为C才对,而不是A了。
    为解决这个问题switch_to()宏在进入后,会把prev先保存到寄存器eax中,当完成进程切换后,把eax的值存入变量last中。
     
    3.2过程说明
    采用标准的汇编语言说明如下:
    1在eax,edx寄存器中分别保存prev和next的值。
    movl prev, %eax
       movl next, %edx
    2把eflags和ebp寄存器存入prev进程的内核栈中,ebp寄存器中存放的是当前函数栈帧的栈底。
    pushfl
    push  %ebp
    3把esp寄存器的值存入prev->thread.esp中以使该值记录prev内核栈的栈顶,应该注意由于内核态代码使用统一的ess寄存器,所以该寄存器不需要被保存。
    mov %esp, 484(%eax)
    4把next->thread.esp的值加载到寄存器esp中,这样就会切换到要被切换进来进程的内核栈上,实际上完成了进程的切换。
    mov next->thread.esp %esp
    5把标记为1的地址记入prev->thread.eip,这样当该进程被切换回来时,会去执行该地址的指令。
    mov $1f, 480(%eax)
    6把next->thread.eip值压入内核栈,大多数情况下就是标记1指向的地址。
    pushl 480(%edx)
        7跳到__switch_to()函数,__switch_to()函数中我们关注的操作主要有2步:
    7.1init_tss[cpu].esp0 = next_p->thread.esp,放入对应tss寄存器,在进行中断处理或 系统调用时,会通过tss的esp0字段设定当前进程内核栈。
    7.2ret, 该指令会把栈顶保存的返回地址装入eip寄存器,从而执行返回地址的代 码,在第6步将该值压入了栈中,因此一般地址为标记1的地址,如果该进程是新建进 程,返回地址则是ret_from_fork()函数。

    8在这里被进程B替换的进程A再次获得CPU,它会执行恢复eflags和ebp的指令。
    1:
    popl %ebp
    popfl 

    9copy eax寄存器的内容到last指向的内存区域,由于last和prev指向同样的内存区域,这样last就记录了当前被替换的进程的进程描述符。
           mov %eax, last

  • 相关阅读:
    linux服务器挂掉自动重启脚本(转)
    shell中打印带有时间的日志的命令(转)
    libreoffice python 操作word及excel文档
    Python操作redis系列之 列表(list) (五)(转)
    docker占满linux磁盘根目录的解决办法
    docker 端口映射 及外部无法访问问题
    转:查看远端的端口是否通畅3个简单实用案例
    转: MAC认证码的说明
    取消Eclipse SVN的自动链接方式
    C++语法查询在线手册
  • 原文地址:https://www.cnblogs.com/pangblog/p/3323017.html
Copyright © 2020-2023  润新知