相关文章推荐
行走的小摩托  ·  EFcore 解决 SQLite ...·  10 月前    · 
不敢表白的香菇  ·  Java OpenCSV How to ...·  1 年前    · 
安静的大熊猫  ·  【笔记】Python ...·  1 年前    · 

在进程进行切换时,需要进行上下文切换,也就是寄存器的值的保存。

对于上下文的切换,有两种情况:硬件自动(隐式)完成、操作系统显式保存。

下面分析两段xv6的源代码,都是上下文切换部分,版本有所不同:

注:每个进程都有一个用来维护过程调用的栈,有的书把这个栈叫做内核栈;在编译原理中,这个栈的术语叫活动记录,在下文中,提到的栈都是说的这个保存活动记录的栈。每个进程都有自己的活动记录(当然,如果是多CPU的情况下,就会引入线程的概念,则每个线程有一个活动记录)

较老的版本

#   void swtch(struct context *old, struct context *new);
# Save current register context in old
# and then load register context from new.
.globl swtch
swtch:
  # Save old registers
  movl 4(%esp), %eax
  popl 0(%eax)  # %eip
  movl %esp, 4(%eax)
  movl %ebx, 8(%eax)
  movl %ecx, 12(%eax)
  movl %edx, 16(%eax)
  movl %esi, 20(%eax)
  movl %edi, 24(%eax)
  movl %ebp, 28(%eax)
  # Load new registers
  movl 4(%esp), %eax  # not 8(%esp) - popped return address above
  movl 28(%eax), %ebp
  movl 24(%eax), %edi
  movl 20(%eax), %esi
  movl 16(%eax), %edx
  movl 12(%eax), %ecx
  movl 8(%eax), %ebx
  movl 4(%eax), %esp
  pushl 0(%eax)  # %eip   
 

这个版本的上下文结构struct context的内容如下:

struct context {
    int eip;    //程序计数器
    int esp;    //栈指针
    int ebp;
    int ecx;
    int edx;
    int esi;
    int edi;
    int ebp;
 

分析一下上下文切换的源代码:

源代码的上半段是将“老”的进程的上下文保存,下半段将“新”的进程(要切换到的)的上下文恢复。

执行swtch的指令时,栈的状态:

movl 4(%esp), %eax
 

将old(参数)的值放入寄存器%eax。

popl 0(%eax)
 

然后将栈顶的值保存到0(%eax)的位置。也就是将返回地址(%eip)的值保存到old指向的context空间中

之后,就是将其他的寄存器放入old指向的context空间中。

通过上面的源码,可以得到context在内存中分配空间的方式:

下面的代码就是从new指向的context空间中,将各个寄存器的值恢复,就不详细说明了。

较新的版本:就像一开始提到的,硬件会自动保存一部分

硬件会保存一部分的寄存器,还有剩余的需要进行手动保存。

# Context switch
#   void swtch(struct context **old, struct context *new);
# Save the current registers on the stack, creating
# a struct context, and save its address in *old.
# Switch stacks to new and pop previously-saved registers.
.globl swtch
swtch:
  movl 4(%esp), %eax
  movl 8(%esp), %edx
  # Save old callee-saved registers
  pushl %ebp
  pushl %ebx
  pushl %esi
  pushl %edi
  # Switch stacks
  movl %esp, (%eax)
  movl %edx, %esp
  # Load new callee-saved registers
  popl %edi
  popl %esi
  popl %ebx
  popl %ebp
 

开始的两条代码,将old与new放入%eax和%edx,方便后面通过指针的方式访问内存。

接下来的四条压栈指令,直接将要手动保存的寄存器压入栈中。

执行到这里,“老”进程和“新”进程对应的栈的示意图如下:

与之前旧版本的有些许不同,新版本的直接将上下文保存在进程自己的栈中;旧版本的保存在了其他的地方(暂时就理解到这个程度,由于没有分析过所有的源码,这里的分析可能比较片面,但这是我整个的分析过程,如有有大佬看出哪里不会,请指正)

进程切换过程之一:上下文切换注:下面给出的源码均出自https://github.com/mit-pdos/xv6-public在进程进行切换时,需要进行上下文切换,也就是寄存器的值的保存。对于上下文的切换,有两种情况:硬件自动(隐式)完成、操作系统显式保存。下面分析两段xv6的源代码,都是上下文切换部分,版本有所不同:注:每个进程都有一个用来维护过程调用的栈,有...
1. CPU上下文切换到底是个什么东西 文章目录1. CPU上下文切换到底是个什么东西1.1. CPU上下文1.2. CPU上下文切换1.2.1. 进程上下文切换1.2.2. 线程上下文切换1.2.3. 中断上下文切换 第一节,我们了解到了平均负载是个什么东西,并且通过三个案例展示了不同场景下(cpu密集型场景,io密集型场景,大量进程场景)的平均负载升高的分析方法其中,多个进程竞争cpu的问题经常会被我们忽略;多个进程竞争cpu的时候,会发生频繁的cpu上下文切换 1.1. CPU上下文 CPU寄存器,是cpu内置的容量小,但是速度极快的存储器件.而程序计数器,是用来存储CPU正在执行的执行
vmstat:是查看系统的整体上下文切换情况,想看具体的每一个进程的情况,需要pidstat工具。 如果不知道参数指令的意思,可以通过 man vmstat 查看具体的指令分析。 自愿上下文切换:是指由于系统资源不足,导致的上下文切花。这导致进程自动挂起,然后由系统进行管理进程的正常调度和运行。 非自愿上下文切换:是指由于时间片的时间到了,然后被系统强制调用,进而发生上下文切换。 pidstat -w -u 5 -w:进程上下文切换情况。 -u :进程cpu使用情况 5:是每五秒输出一组数据。 pidstat -wt 1 -wt :输出线程的上线文切换情况。 1:每1秒输出一组数据。 type.h:用于声明一些数据类型的简化名称,和声明页表指针的数据类型。 param.h:主要用于声明基本的一些常量,包括内核栈大小等。 memlayout.h:主要用于声明一些和内存与地址相关的常量与方法,包括虚拟内存转物理内存的方法以及物理内存转虚拟内存的方法等。 defs.h:声明在之后文件中要用到的函数。 x86.h:让c代码使用特殊的x86汇编的一些函数包括outb等,并声...
支持多任务处理是CPU设计史上最大的跨越之一。在计算机中,多任务处理是指同时运行两个或多个程序。从使用者的角度来看,这看起来并不复杂或者难以实现,但是它确实是计算机设计史上一次大的飞跃。在多任务处理系统中,CPU需要处理所有程序的操作,当用户来回切换它们时,需要记录这些程序执行到哪里。上下文切换就是这样一个过程,他允许CPU记录并恢复各种正在运行程序的状态,使它能够完成切换操作。 在上下文切
我都知道操作系统的一个重要功能就是进行进程管理,而进程管理就是在合适的时机选择合适的进程来执行,在单个cpu运行队列上各个进程宏观并行微观串行执行,多个cpu运行队列上的各个进程之间完全的并行执行。进程管理是个复杂的过程,例如进程的描述、创建和销毁、生命周期管理、进程切换进程抢占、调度策略、负载均衡等等。本文主要关注进程管理的一个切入点,那就是进程上下文切换,来理解linux内核是如何进程进程上下文切换的,从而揭开上下文切换的神秘面纱。 (注意:本文以linux-5.0内核源码讲解,采用arm64架构)
检查need_resched标志位,若有效则调用schedule()函数完成进程调度,schedule()会执行以下步骤: 调用pick_next_task()根据相关调度算法得到下一个待运行的进程。 调用context_switch()执行以下步骤: 调用switch_mm()将虚拟内存地址映射到待运行进程,恢复内
对于服务器的优化,很多人都有自己的经验和见解,但就我观察,有两点常常会被人忽视 – 上下文切换 和 Cache Line同步 问题,人们往往都会习惯性地把视线集中在尽力减少内存拷贝,减少IO次数这样的问题上,不可否认它们一样重要,但一个高性能服务器需要更细致地去考察这些问题,这个问题我将分成两篇文章来写: 1)从一些我们常用的用户空间函数,到linux内核代码的跟踪,来看一个上下文切换是如何产生...
上下文切换过程 上图展示了进程切换时的上下文切换过程,首先一个用户进程通过中断或者系统调用陷入内核,此时发生用户态和内核态的切换,进程切换到自己的内核栈,再接着切换到上下文切换到调度器专属的内核栈,最后从调度器的内核栈切换到另一个用户进程的内核栈,最后从该内核栈返回用户态,即完成切换到了另一个用户进程。 在如下的sched函数中,就发生了上述中的过程,在sched中调用了swtch函数,可以看到传入s
1.抢占和上下文切换 上下文切换(也就是切换进程),在schedul()函数中通过context_switch()函数处理进行两个基本工作。context_switch()函数就是执行下一个进程,并返回指向前一个进程进程结构的指针。context_switch()中的两个主要函数一个是切换虚拟内存映射,另外一个是切换进程/线程的结构。 第一个是由函数switch_mm()完成,该函数使用了许...