linux进程切换问题

#define switch_to(prev,next,last) do {         unsigned long esi,edi;         asm volatile("pushfl\n\t"                      "pushl %%ebp\n\t"                      "movl %%esp,%0\n\t" /* save ESP */                      "movl %5,%%esp\n\t" /* restore ESP */                      "movl $1f,%1\n\t" /* save EIP */                      "pushl %6\n\t" /* restore EIP */                      "jmp __switch_to\n"                      "1:\t"                      "popl %%ebp\n\t"                      "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)

注意:switch_to是宏,所以prev,next,last参数是通过寄存器传递的,直接就是栈中的局部变量,switch_to时请注意:这是一个宏,不是函数,它的参数prev, next, last不是值拷贝,而是它的调用者context_switch()的局部变量。局部变量是通过%ebp寄存器来索引的,也就是通过n(%ebp),n是编译时决定的,在不同的进程的同一段代码中,同一局部变量的n是相同的。

在A进程中的prev和next,是其局部变量,可以成为prev_A,next_B,指的是进程描述符的内存地址

在A进程中,switch_to(prev,next,prev)

prev,next,last这三个都是schedue函数的局部变量,寻址都是以EBP为基地址的

step1:复制两个局部变量到寄存器:
    
    eax <== prev_A 或eax <==%p(%ebp_A)
    edx <== next_A 或edx <==%n(%ebp_A)
    这里prev和next都是A进程的局部变量。

step2:保存进程A的ebp和eflags
    pushfl
    pushl %ebp
    注意,因为现在esp还在A的堆栈中,所以这两个东西被保存到A进程的内核堆栈中。

step3:保存当前esp到A进程内核描述符中://thread_struct的thread字段:存储了esp和eip等大部分寄存器
    "movl %%esp,%[prev_sp]/n/t"    /*save    ESP   */
    它可以表示成:prev_A->thread.sp <== esp_A
    在调用switch_to时,prev是指向A进程自己的进程描述符的。

step4:从next(进程B)的描述符中取出之前从B切换出去时保存的esp_B:其实是将next->thread.esp装入esp

"movl%[next_sp],%%esp/n/t" /* restore ESP */
    它可以表示成:esp_B<== next_A->thread.sp
    注意,在A进程中的next是指向B的进程描述符的。
    从这个时候开始,CPU当前执行的进程已经是B进程了,因为esp已经指向B的内核堆栈。但是,现在的ebp仍然指向A进程的内核堆栈,所以所有局部变量仍然是A中的局部变量,比如next实质上是%n(%ebp_A),也就是next_A,即指向B的进程描述符。

此时,已经完成了进程的堆栈的切换了,但是ebp指向的却是A进程的内核堆栈。

step5,6:把标号为1的指令地址保存到A进程描述符的ip域:

step6:将返回地址保存到堆栈,然后调用__switch_to()函数,__switch_to()函数完成硬件上下文切换。
    "pushl %[next_ip]/n/t"    /*restore EIP   */由于上步导致的esp和ebp不是指向同一个堆栈,所以这部next的变量属于A进程的局部变量,代表的是B的进程描述符指针,next->thread.ip压入next的内核栈(也就是B的内核栈)
    "jmp __switch_to/n"    /* regparmcall  */

step7:从__switch_to()返回后继续从1:标号后面开始执行,修改ebp到B的内核堆栈,恢复B的eflags:
    "popl %%ebp/n/t"       /* restore EBP   */    
    "popfl/n"           /* restore flags */
    如果从__switch_to()返回后从这里继续运行,那么说明在此之前B肯定被switch_to调出过,因此此前肯定备份了ebp_B和flags_B,这里执行恢复操作。

注意,这时候ebp已经指向了B的内核堆栈,所以上面的prev,next等局部变量已经不是A进程堆栈中的了,而是B进程堆栈中的(B上次被切换出去之前也有这两个变量,所以代表B堆栈中prev、next的值了),因为prev == %p(%ebp_B),而在B上次被切换出去之前,该位置保存的是B进程的描述符地址。如果这个时候就结束switch_to的话,在后面的代码中(即 context_switch()函数中switch_to之后的代码)的prev变量是指向B进程的,因此,进程B就不知道是从哪个进程切换回来。而从context_switch()中switch_to之后的代码中,我们看到finish_task_switch(this_rq(), prev)中需要知道之前是从哪个进程切换过来的,因此,我们必须想办法保存A进程的描述符到B的堆栈中,这就是last的作用。

step8:将eax写入last,以在B的堆栈中保存正确的prev信息。
    "=a" (last)  即 last_B <== %eax
    而从context_switch()中看到的调用switch_to的方法是:
    switch_to(prev, next, prev);
    所以,这里面的last实质上就是prev,因此在switch_to宏执行完之后,prev_B就是正确的A的进程描述符了。   
    这里,last的作用相当于把进程A堆栈中的A进程描述符地址复制到了进程B的堆栈中。

eax总是保持被切换进程的值

时间: 2024-08-29 15:09:42

linux进程切换问题的相关文章

Linux进程切换(1) 基本框架

一.前言 本文主要是以context_switch为起点,分析了整个进程切换过程中的基本操作和基本的代码框架,很多细节,例如tlb的操作,cache的操作,锁的操作等等会在其他专门的文档中描述.进程切换包括体系结构相关的代码和系统结构无关的代码.第二.三.四分别描述了context_switch的代码脉络,后面的章节是以ARM64为例子,讲述了具体进程地址空间的切换过程和硬件上下文的切换过程. 二.context_switch代码分析 在kernel/sched/core.c中有一个contex

Linux进程切换(2) TLB处理

一.前言 进程切换是一个复杂的过程,本文不准备详细描述整个进程切换的方方面面,而是关注进程切换中一个小小的知识点:TLB的处理.为了能够讲清楚这个问题,我们在第二章描述在单CPU场景下一些和TLB相关的细节,第三章推进到多核场景,至此,理论部分结束.在第二章和第三章,我们从基本的逻辑角度出发,并不拘泥于特定的CPU和特定的OS,这里需要大家对基本的TLB的组织原理有所了解,具体可以参考本站的<TLB操作>一文.再好的逻辑也需要体现在HW block和SW block的设计中,在第四章,我们给出

Linux进程切换(2) 同步处理

一.前言 本文主要描述了主调度器(schedule函数)中的同步处理. 二.进程调度简介 进程切换有两种,一种是当进程由于需要等待某种资源而无法继续执行下去,这时候只能是主动将自己挂起(调用schedule函数),引发一次任务调度过程.另外一种是进程被抢占.所谓抢占(preempt)就是在当前进程欢快执行的时候,终止其对CPU资源的占用,切换到另外一个更高优先级的进程执行.进程被抢占往往是由于各种调度事件的发生: 1. 时间片用完 2. 在中断上下文中唤醒其他优先级更高的进程 3. 在其他进程上

[linux]进程(三)——idle进程

9,linux进程切换 进程切换:基本概念:进程上下文:当一个进程在执行时,CPU的所有寄存器中的值.进程的状态以及堆栈中的内容被称为该进程的上下文.当内核需要切换到另一个进程时,它需要保存当前进程的所有状态,即保存当前进程的上下文,运行于进程上下文的进程是可以被抢占的.硬件上下文:进程恢复执行前必须载入寄存器的一组数据称为硬件上下文linux内核在进程切换的时候是并不区分进程和线程的~因为切换是针对task,进程切换的时机:一般是在系统调用或者中断的时候,发生在内核态.进程切换过程:进程切换统

Linux内核设计第八周学习总结 理解进程调度时机跟踪分析进程调度与进程切换的过程

陈巧然 原创作品转载请注明出处 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 一.视频内容 Linux系统的一般执行过程 最一般的情况:正在运行的用户态进程X切换到运行用户态进程Y的过程 1. 正在运行的用户态进程X 2. 发生中断——save cs:eip/esp/eflags(current) to kernel stack, then load cs:eip(entry of a specific IS

Linux内核源码学习之进程切换细节整理

linux中的进程是个最基本的概念,进程从运行队列到开始运行有两个开始的地方, 一个就是switch_to宏中的标号1:"1:/t",//只要不是新创建的进程,几乎都是从上面的那个标号1开始的,而switch_to宏则是除了内核本身,所有的进程要 想运行都要经过的地方 另 一个就是ret_form_fork 这样看来,虽然linux的进程体系以及进程调度非常复杂,但是总体看来就是一个沙漏状, 对于系统中的每个新进程它首次被执行的过程必然是: sys_fork---->do_for

Linux进程调度(3):进程切换分析

3.调度函数schedule()分析 当kernel/sched.c:sched_tick()执行完,并且时钟中断返回时,就会调用kernel/sched.c:schedule()完成进程切换.我们也可以显示调用schedule(),例如在前面"Linux进程管理"的介绍中,进程销毁的do_exit()最后就直接调用schedule(),以切换到下一个进程. schedule()是内核和其他部分用于调用进程调度器的入口,选择哪个进程可以运行,何时将其投入运行.schedule通常都和一

Linux内核进程调度的时机和进程切换

陈铁+ 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 对于现代操作系统,多任务是必备的,在linux系统下,进程会不断的被内核调度,从X进程切换为Y进程,以实现用户所见到的多任务状态,下面我们就看一看这样的过程,分析一下内核如何对进程调度,以及进程间如何切换. 内核使用schedule()函数实现进程的调度,而通常的用户进程要无法主动调度这个函数,只能通过中断处理过程(包括时钟中断

Linux 前后台进程切换(转)

Linux 前后台进程切换 当你用shell启动一个程序时,往往他是在前台工作的. 例如经常用PUTTY连接到远程服务器执行脚本的时候,如果本地网络中断后,这个时候前台进程就结束了,比较的懊恼,必须重新执行.因此有必要进行前后台进程的切换. 例如直接在终端里输入firefox,那么会打开firefox,但当你关闭此终端或者ctrl+c强制终止时,firefox也随机关闭了.你可以在执行时后面加一个&,这样就在后台工作了. Shell支持作用控制,有以下命令: 1. command  & 让