xv6进程切换-swtch函数
https://blog.csdn.net/Swartz2015/article/details/61615603
xv6进程切换-swtch函数
进程切换中由于需要保存当前进程的寄存器状态信息,又要将新进程记录的寄存器状态信息加载到寄存器,因此涉及到许多栈的操作,堆栈间的来回切换,容易让人眼花缭乱,难以理解。本文试图分析以下xv6中的进程切换过程。
当前进程通过调用yield函数,进行进程切换。yield函数调用sched函数,sched函数启动swtch函数完成进程切换。整个流程是这样的:
yield->sched->swtch
- 1
在sched函数中,可以看到,当前进程总是先切换到当前cpu的scheduler切换器:
//void sched(void)
swtch(&proc->context, cpu->scheduler);
- 1
- 2
切换器是一个死循环,该循环不断在进程表中扫描,选择一个RUNNABLE的进程调度,即从scheduler切换器转换到新选择的进程:
//scheduler切换器
void
scheduler(void)
{
struct proc *p;
for(;;){
// Enable interrupts on this processor.
sti();
// Loop over process table looking for process to run.
acquire(&ptable.lock);
for(p = ptable.proc; p < &ptable.proc[NPROC]; p++){
if(p->state != RUNNABLE)
continue;
// Switch to chosen process. It is the process's job
// to release ptable.lock and then reacquire it
// before jumping back to us.
proc = p;
switchuvm(p);
p->state = RUNNING;
swtch(&cpu->scheduler, proc->context);
switchkvm();
// Process is done running for now.
// It should have changed its p->state before coming back.
proc = 0;
}
release(&ptable.lock);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
显然,swtch函数是重点。swtch函数的原型是:
void swtch(struct context **old, struct context *new);
- 1
它的工作主要包括:1. 保存当前(old)进程的上下文。 2. 加载新进程(new)的上下文到机器寄存器中。
contex中其实就是几个寄存器变量,用来保存这些寄存器的值。
swtch的函数代码如下:
# Context switch
#
# void swtch(struct context **old, struct context *new);
#
# Save current register context in old
# and then load register context from new.
.globl swtch
swtch:
movl 4(%esp), %eax
movl 8(%esp), %edx
# Save old callee-save registers
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
# Switch stacks
movl %esp, (%eax)
movl %edx, %esp
# Load new callee-save registers
popl %edi
popl %esi
popl %ebx
popl %ebp
ret
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
下面分析以下swtch函数的执行过程。
swtch函数的执行过程
当任意进程调用swtch函数时,该进程的堆栈是下面这个样子的:
其中栈中每个存储区域都是4个字节。这里需要提一下,在执行指令call时,当前指令的下一条指令的地址将会push到栈中,也就是上面看到的eip,这样就可以保证在函数返回时,可以回到函数调用的下一条指令的地方。然后swtch开始执行,
movl 4(%esp), %eax
movl 8(%esp), %edx
- 1
- 2
所以寄存器eax和edx都会指向如下图所示的位置:
接下来会依次将有关上下文信息的寄存器push到栈中
pushl %ebp
pushl %ebx
pushl %esi
pushl %edi
- 1
- 2
- 3
- 4
push完之后,就可以堆栈布局就变成下图所示:
对比contex和当前的栈布局,可以直观看到,此时在栈上存储的刚好是一个contex。当前的栈顶esp刚好指向当前进程的contex中的第一个元素的地址,所以这里可以说contex中包括了栈信息和寄存器信息,不仅可以用它来进行栈切换,还可以通过它恢复寄存器。因此可以想到,下次重新调度这个进程时,只需要直接将栈上的信息弹到相应寄存器中就可以了。而这个栈顶指针,我们可以直接用proc->contex指针记录就可以了。即
proc->contex = esp;
- 1
我们继续看swtch函数:
# Switch stacks
movl %esp, (%eax)
movl %edx, %esp
- 1
- 2
- 3
根据注释也可以看到,这两行主要是用于栈的切换。前面我们知道,eax中存储的是指向old_proc->contex的指针,old_proc->contex是指向当前进程的contex的指针,所以movl %esp, (%eax),相当于:
old_proc->contex = esp;
- 1
也就是让当前进程的contex指针指向栈顶。根据前面的示意图我们可以理解原理。
到此为止,旧进程的contex保存工作已经完成了。
下面的工作自然是怎么把新进程的contex弹出到对应的寄存器中。其实保存过程和弹出恢复过程是对称的。
从保存过程中,我们知道新进程的contex信息总是可以通过new_proc->contex获得,因为new_proc->contex指向了new_proc内核栈的栈顶,而栈顶依次保存着上下文寄存器信息,所以将new_proc->contex赋值到esp就完成了堆栈切换,再依次pop就可以恢复上下文寄存器信息。但是我们怎么获得new_proc的contex呢?回到前面,我们看到,就进程在调用swtch时,就把new_proc的contex放在了堆栈中,并且根据前面edx=new_proc->contex。所以堆栈切换就是把
esp= edx;
- 1
然后
popl %edi
popl %esi
popl %ebx
popl %ebp
- 1
- 2
- 3
- 4
就可以依次将new_contex中保存的寄存器值依次弹回到对应寄存器中了。
最后的
ret
- 1
指令将eip弹回。所以新进程会从它上次调用swtch函数的下一条指令开始执行。
xv6进程切换-swtch函数的更多相关文章
- linux0.11改进之四 基于内核栈的进程切换
这是学习哈工大李治军在mooc课操作系统时做的实验记录.原实验报告在实验楼上.现转移到这里.备以后整理之用. 完整的实验代码见:实验楼代码 一.tss方式的进程切换 Linux0.11中默认使用的是硬 ...
- Linux内核分析——理解进程调度时机跟踪分析进程调度与进程切换的过程
20135125陈智威 +原创作品转载请注明出处 +<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验 ...
- Linux内核分析之理解进程调度时机跟踪分析进程调度与进程切换的过程
一.原理分析 1.调度时机 背景不同类型的进程有不同的调度需求第一种分类I/O-bond:频繁的进行I/O:通常会花费很多时间等待I/O操作的完成CPU-bound:计算密集型:需要大量的CPU时间进 ...
- Linux进程切换代码分析
朱宇轲 + 原创作品转载请注明出处 + <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 这次我们来分析L ...
- 20135202闫佳歆--week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程--实验及总结
week 8 实验:理解进程调度时机跟踪分析进程调度与进程切换的过程 1.环境搭建: rm menu -rf git clone https://github.com/megnning/menu.gi ...
- 通过gdb跟踪进程调度分析进程切换的过程
作者:吴乐 山东师范大学 <Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 本实验目的:通过gdb在lin ...
- 在Linux-0.11中实现基于内核栈切换的进程切换
原有的基于TSS的任务切换的不足 进程切换的六段论 1 中断进入内核 2 找到当前进程的PCB和新进程的PCB 3 完成PCB的切换 4 根据PCB完成内核栈的切换 5 切换运行资源LDT 6 利用I ...
- linux内核学习之四:进程切换简述
在讲述专业知识前,先讲讲我学习linux内核使用的入门书籍:<深入理解linux内核>第三版(英文原版叫<Understanding the Linux Kernel>),不过 ...
- linux进程解析--进程切换
为了控制进程的执行,linux内核必须有能力挂起正在cpu上运行的进程,换入想要切换的进程,也就是恢复以前某个挂起的进程,这就是linux的进程切换. 1进程切换的时机 一般来说,进程切换都是发生在 ...
随机推荐
- Flume的可靠性
Flume的可靠性 当节点出现故障时,日志能够被传送到其他节点上而不会丢失. Flume提供了三种级别的可靠性保障,从强到弱依次分别为:end-to- end(收到数据agent首先将event写到磁 ...
- Codefroces 831B Keyboard Layouts
B. Keyboard Layouts time limit per test 1 second memory limit per test 256 megabytes input standard ...
- CentOS经常使用文件操作命令[百度博客搬家]
路径操作的CentOS经常使用命令 如今整理例如以下(百度博客搬家) cd pwd NO1. 显示当前路径 [root@rehat root]# pwd NO2. 返回用户主文件夹 [ro ...
- ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第三篇:ASP.NET MVC全局观
摘要 本文对ASP.NET MVC的全局运行机理进行一个简要的介绍,以使得朋友们更好的理解后续文章. 前言 在上一篇文章中,我们实现了第一个ASP.NET MVC页面.对于没有接触 ...
- nodeJS+socket.io传递消息
服务器端 安装express,socket.io npm install express --save-dev npm install socket.io --save app.js const ex ...
- VC++的函数指针和回调函数 及友元函数
什么是函数指针 函数指针是指向函数的指针变量.也就是说,它是一个指针变量,而且该指针指向一个函数. 对于指针变量来说,它的值是它指向的变量的地址.举个例子:指针变量pi是指向一个整型变量i的指针,则变 ...
- 00092_字符输出流Writer
1.字符输出流Writer (1)既然有专门用于读取字符的流对象,那么肯定也有写的字符流对象: (2)查阅API,发现有一个Writer类,Writer是写入字符流的抽象类.其中描述了相应的写的动作. ...
- 机器学习算法中怎样选取超參数:学习速率、正则项系数、minibatch size
本文是<Neural networks and deep learning>概览 中第三章的一部分,讲机器学习算法中,怎样选取初始的超參数的值.(本文会不断补充) 学习速率(learnin ...
- Android通过startService播放背景音乐简单演示样例
关于startService的基本使用概述及其生命周期可參见博客<Android中startService的使用及Service生命周期>. 本文通过播放背景音乐的简单演示样例,演示sta ...
- 从软件project的角度写机器学习3——主要监督学习算法的project性分析
主要机器学习算法的project适用性分析 前段时间AlphaGo跟李世石的大战及相关的深度学习的新闻刷了一遍又一遍的朋友圈.只是这件事情,也仅仅是在机器学习的深度上进一步拓展,而机器学习的广度(也即 ...