线程状态(context)

程序计数器(Program Counter),它表示当前线程执行指令的位置。

保存变量的寄存器。

程序的Stack。通常来说每个线程都有属于自己的Stack,Stack记录了函数调用的记录,并反映了当前线程的执行点。

xv6的线程切换
  • 从一个用户进程切换到另一个用户进程,都需要从第一个用户进程接入到内核中,保存用户进程的状态并运行第一个用户进程的内核线程。

  • 再从第一个用户进程的内核线程切换到第二个用户进程的内核线程。

  • 之后,第二个用户进程的内核线程暂停自己,并恢复第二个用户进程的用户寄存器。

  • 最后返回到第二个用户进程继续执行

线程切换函数

xv6在这部分设计的非常巧妙,

其使用swtch(&p->context, &mycpu()->context)汇编函数,实现了两个函数sched scheduler的跳转,

达成了进程context以及调度器context的切换

swtch函数(汇编函数)

swtch函数(避开switch关键字)会保存用户进程P1对应内核线程的寄存器至context对象。所以有两类寄存器:用户寄存器存在trapframe中,内核线程的寄存器存在context中。

实际上,swtch函数并不是直接从一个内核线程切换到另一个内核线程。XV6中,一个CPU上运行的内核线程可以直接切换到的是这个CPU对应的调度器线程。所以如果我们运行在CPU0,swtch函数会恢复之前为CPU0的调度器线程保存的寄存器和stack pointer,之后就在调度器线程的context下执行schedulder函数中。

schedulder函数

schedulder函数由调度器线程执行。在schedulder函数中会做一些清理工作,例如将进程P1设置成RUNABLE状态。之后再通过进程表单找到下一个RUNABLE进程。假设找到的下一个进程是P2(虽然也有可能找到的还是P1),schedulder函数会再次调用swtch函数,完成下面步骤:

  1. 先保存自己的寄存器到调度器线程的context对象
  2. 找到进程P2之前保存的context,恢复其中的寄存器
  3. 因为进程P2在进入RUNABLE状态之前,如刚刚介绍的进程P1一样,必然也调用了swtch函数。所以之前的swtch函数会被恢复,并返回到进程P2所在的系统调用或者中断处理程序中(注,因为P2进程之前调用swtch函数必然在系统调用或者中断处理程序中)。
  4. 不论是系统调用也好中断处理程序也好,在从用户空间进入到内核空间时会保存用户寄存器到trapframe对象。所以当内核程序执行完成之后,trapframe中的用户寄存器会被恢复。
  5. 最后用户进程P2就恢复运行了。
sched函数

sched函数由用户线程调用,切换到调度器线程。其与schedulder函数互为协程的关系,也将调用swtch函数。根据线程切换机制,sched调用swtch函数后,CPU的下一条指令处于schedulder函数的swtch函数后,原因是swtch函数保存了ra(返回地址)寄存器,因此sched调用swtch函数后,当前ra变成了调度器线程的ra,即上一次调度器线程调用schedulder函数时存储的ra值,故接下来执行schedulder函数swtch调用后的代码。同理,schedulder函数调用swtch函数后,CPU将转移到sched函数。

void
sched(void)
{
int intena;
struct proc *p = myproc(); // 进行一系列判断
if(!holding(&p->lock))
panic("sched p->lock");
if(mycpu()->noff != 1)
panic("sched locks");
if(p->state == RUNNING)
panic("sched running");
if(intr_get())
panic("sched interruptible"); intena = mycpu()->intena;
// 交换上下文环境
swtch(&p->context, &mycpu()->context);
mycpu()->intena = intena;
}
// Per-CPU process scheduler.
// Each CPU calls scheduler() after setting itself up.
// Scheduler never returns. It loops, doing:
// - choose a process to run.
// - swtch to start running that process.
// - eventually that process transfers control
// via swtch back to the scheduler.
//
void
scheduler(void)
{
struct proc *p;
struct cpu *c = mycpu(); c->proc = 0;
for(;;){
// Avoid deadlock by ensuring that devices can interrupt.
intr_on(); for(p = proc; p < &proc[NPROC]; p++) {
acquire(&p->lock);
if(p->state == RUNNABLE) {
// Switch to chosen process. It is the process's job
// to release its lock and then reacquire it
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
// 将当前上下文环境交换到进程上下文环境,将控制权给进程
swtch(&c->context, &p->context); // Process is done running for now.
// It should have changed its p->state before coming back.
c->proc = 0;
}
release(&p->lock);
}
}
}
# Context switch
#
# void swtch(struct context *old, struct context *new);
#
# Save current registers in old. Load from new. .globl swtch
swtch:
sd ra, 0(a0)
sd sp, 8(a0)
sd s0, 16(a0)
sd s1, 24(a0)
sd s2, 32(a0)
sd s3, 40(a0)
sd s4, 48(a0)
sd s5, 56(a0)
sd s6, 64(a0)
sd s7, 72(a0)
sd s8, 80(a0)
sd s9, 88(a0)
sd s10, 96(a0)
sd s11, 104(a0) ld ra, 0(a1)
ld sp, 8(a1)
ld s0, 16(a1)
ld s1, 24(a1)
ld s2, 32(a1)
ld s3, 40(a1)
ld s4, 48(a1)
ld s5, 56(a1)
ld s6, 64(a1)
ld s7, 72(a1)
ld s8, 80(a1)
ld s9, 88(a1)
ld s10, 96(a1)
ld s11, 104(a1) ret

Linux 进程调度的更多相关文章

  1. linux进程调度之 FIFO 和 RR 调度策略

    转载 http://blog.chinaunix.net/uid-24774106-id-3379478.html    linux进程调度之 FIFO 和 RR 调度策略 2012-10-19 18 ...

  2. Linux进程调度原理

    Linux进程调度原理 Linux进程调度机制 Linux进程调度的目标 1.高效性:高效意味着在相同的时间下要完成更多的任务.调度程序会被频繁的执行,所以调度程序要尽可能的高效: 2.加强交互性能: ...

  3. Linux进程调度原理【转】

    转自:http://www.cnblogs.com/zhaoyl/archive/2012/09/04/2671156.html Linux进程调度的目标 1.高效性:高效意味着在相同的时间下要完成更 ...

  4. Linux进程调度与源码分析(二)——进程生命周期与task_struct进程结构体

    1.进程生命周期 Linux操作系统属于多任务操作系统,系统中的每个进程能够分时复用CPU时间片,通过有效的进程调度策略实现多任务并行执行.而进程在被CPU调度运行,等待CPU资源分配以及等待外部事件 ...

  5. Linux进程调度与源码分析(一)——简介

    本系列文章主要是近期针对Linux进程调度源码进行阅读与分析后的经验总结,分析过程中可能结合部分Linux网络编程的相关知识以便于理解,加深对Linux进程调度的理解和知识分享. 本系列文章主要结合L ...

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

     3.调度函数schedule()分析 当kernel/sched.c:sched_tick()执行完,并且时钟中断返回时,就会调用kernel/sched.c:schedule()完成进程切换.我们 ...

  7. 深度讲解Linux内存管理和Linux进程调度-打通任督二脉

    我在多年的工程生涯中发现很多工程师碰到一个共性的问题:Linux工程师很多,甚至有很多有多年工作经验,但是对一些关键概念的理解非常模糊,比如不理解CPU.内存资源等的真正分布,具体的工作机制,这使得他 ...

  8. [转载]Linux进程调度原理

    [转载]Linux进程调度原理 Linux进程调度原理 Linux进程调度的目标 1.高效性:高效意味着在相同的时间下要完成更多的任务.调度程序会被频繁的执行,所以调度程序要尽可能的高效: 2.加强交 ...

  9. 【原创】(五)Linux进程调度-CFS调度器

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  10. 【原创】(六)Linux进程调度-实时调度器

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

随机推荐

  1. 1.Vuejs-第一个实例

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  2. C++内存管理:new / delete 和 cookie

    new 和 delete C++的内存申请和释放是通过 new 和 delete 实现的, 而new 和 delete 其实就是通过 malloc 和 free 实现的. new 申请内存分为三个步骤 ...

  3. 【划重点】Python matplotlib绘图建立画布和坐标系

    一.建立画布 import matplotlib.pyplot as plt import numpy as np x=np.arange(8) y=np.arange(8) print(x,y) # ...

  4. hooks中,useState异步问题解决方案

    问题描述: 在hooks中,修改状态的是通过useState返回的修改函数实现的.它的功能类似于class组件中的this.setState().而且,这两种方式都是异步的.可是this.setSta ...

  5. *CTF pwn write up

    第一次做出XCTF的题目来,感谢wjh师傅的指点,虽然只做出一道最简单的pwn题,但是还是挺开心的.此贴用来记录一下,赛后试着看看其他大师傅的wp,看看能不能再做出一道题来. babyheap 程序有 ...

  6. CF749B Parallelogram is Back 题解

    Content 给出平行四边形的三个顶点 \((x_1,y_1),(x_2,y_2),(x_3,y_3)\),求出所有可能的第四个顶点. 数据范围:\(\forall i\in[1,3],-1000\ ...

  7. Spring学习(四)在Web项目中实例化IOC容器

    1.前言 前面我们讲到Spring在普通JAVA项目中的一些使用.本文将介绍在普通的Web项目中如何实例化Spring IOC容器.按照一般的思路.如果在Web中实例化Ioc容器.这不得获取Conte ...

  8. JAVA将Object数组转换为String数组

    java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String; java将Object[ ...

  9. summernote富文本图片上传,增加视频上传功能、批量上传方法

    Summernote 是一个简单灵活的所见即所得的 HTML 在线编辑器,基于 jQuery 和 Bootstrap 构建,支持快捷键操作,提供大量可定制的选项. 但是却只有图片上传功能,没有视频上传 ...

  10. VS2015 up3激活key

    密钥 专业版:HMGNV-WCYXV-X7G9W-YCX63-B98R2 企业版:HM6NR-QXX7C-DFW2Y-8B82K-WTYJV