系列文章目录和关于我

一丶CPU的虚拟化

一个桃子,我们称之为物理(physical)桃子。但有很多想吃这个桃子的 人,我们希望向每个想吃的人提供一个属于他的桃子,这样才能皆大欢喜。我们把给每个 人的桃子称为虚拟(virtual)桃子。我们通过某种方式,从这个物理桃子创造出许多虚拟桃子。重要的是,在这种假象中,每个人看起来都有一个物理桃子,但实际上不是。

以最基本的计算机资源 CPU 为例, 假设一个计算机只有一个 CPU(尽管现代计算机一般拥有 2 个、4 个或者更多 CPU),虚拟化要做的就是将这个 CPU 虚拟成多个虚拟 CPU 并分给每一个进程使用,因此,每个应用都以为自己在独占 CPU,但实际上只有一个 CPU。这便是CPU的虚拟化

二丶进程

1.概念

进程就是运行中的程序。程序本身是没有生命周期的,它只是存在磁盘上面的一些指令(也可能是一些静态数据)。是操作系统让这些字节运行起来,让程序发挥作用。

2.进程的机器状态

程序在运行时可以读取或更新的内容。在任何时刻,机器的哪些部分对执行该程序很重要(进程执行过程中会使用到机器的哪些部分)

  • 内存

    指令存在内存中。正在运行的程序读取和写入的数据也在内存中。因此进程可以访问的内存(称为地址空间,address space) 是该进程的一部分

  • 寄存器

    许多指令明确地读取或更新寄存器

  • 程序计数器

    代表程序当前正在执行哪个指令;类似地,栈指针(stack pointer)和相关的帧指针(frame pointer)用于 管理函数参数栈、局部变量和返回地址。

  • 持久存储设备

    此类 I/O 信息可能包含当前打开的文件列表

3.时分共享

通过让一个进程只运行一个时间片,然后切换到其他进程,操作系统提供了存在多个虚拟 CPU 的假象。这就是时分共享(time sharing)CPU 技术。

磁盘空间是一个空分共享资源,因为一旦将块分配给文件,在用户删除文件之前,不可能将它分配给其他文件。

4.程序如何转化为进程

操作系统如何启动并运行一个程序?进程创建实际如何进行?

  • 程序最初以某种可执行格式驻留在磁盘上

    操作系统需要将代码和所有静态数据(例如初始化变量)加载(load)到内存中,加载到进程的地址空间中

  • 为进程分配空间

    为程序的运行时栈分配一些内存。程序使用栈存放局部变量、函数参数和返回地址。操作系统分配这些内存,并提供给进程。

  • 为程序的堆分配一些内存

    C语言程序通过调用 malloc()来请求这样的空间

  • 其他初始化任务

    如在UNIX系统中,默认情况下每个进程都有 3 个打开的文件描述符(file descriptor),用于标 准输入、输出和错误。这些描述符让程序轻松读取来自终端的输入以及打印输出到屏幕。

    操作系统需要初始化这三个文件描述符

5.进程的状态

  • 运行

在运行状态下,进程正在处理器上运行。这意味着它正在执行 指令。

  • 就绪

在就绪状态下,进程已准备好运行,但没有被CPU进行调度

  • 阻塞

在阻塞状态下,一个进程执行了某种操作,直到发生其他事件时才会准备运行。一个常见的例子是,当进程向磁盘发起 I/O 请求时,它会被阻塞, 因此其他进程可以使用处理器。

为了跟踪每个进程的状态,操作系统使用进程列表,跟踪当前正在运行的进程的一些附加信息。操作系统还必须以某种方式跟踪被阻塞的进程(当 I/O 事件完成时,操作系统应确保唤醒正确的进程,让它准备好再次运行)

对于停止的进程,寄存器上下文将保存其寄存器的内容。当一个进程停止时,它的寄存器将被保存到这个内存位置。 通过恢复这些寄存器(将它们的值放回实际的物理寄存器中),操作系统实现恢复运行该进程。

僵尸状态:一个进程处于已退出但尚未清理的最终状态。其他进程(通常是创建进程的父进程)可以检查僵尸进程的返回代码,并查看刚刚完成的进程是否成功执行。

6.进程API

6.1.fork

调用fork函数,父进程fork函数返回的是子进程的进程id,子进程将返回0,如果返回小于0的数表示fork失败。

6.2.exec

使用exec系统调用,需要给定可执行程序的名称以及需要的参数,随后exec将从可执行程序中加载代码和静态数据,并用它复写自己的代码段,静态数据,堆,栈以及其他内存空间也会被重新初始化,然后操作系统就执行该程序。

因此exec()并没有创建新进程,而是直接将运行的程序替换成另一个程序。

6.4.wait

父进程调用 wait(),延迟自己的执行,直到子进程执行完毕。当子进程结束时,父进程才从wait返回。

三丶进程的调度

为了虚拟化 CPU,操作系统需要以某种方式让许多进程共享物理 CPU,让它们看起来像是同时运行。操作系统使用时分共享:运行一个进程一段时间,然后运行另一个进程实现cpu的虚拟化。

实现时分共享面临的挑战:

  • 性能

    如何在不增加系统开销的情况下,实现虚拟化cpu

  • 控制权

    如何有效地运行进程,同时保留对 CPU 的控制?控制权对于操作系统尤为重要,因为操作系统负责资源管理。如果没有控制权,一个进程可以简单地无限制运行并接管机器,或访问没有权限的信息。因此,在保持控制权的 同时获得高性能,这是构建操作系统的主要挑战之一。

1.用户态/内核态

  • 用户模式

    此模式下运行的代码会受到限制。例如,在用户模式下运行时,进程不能发出 I/O 请求。这 样做会导致处理器引发异常,操作系统可能会终止进程。

    应用程序在用户态下无法完全的访问硬件资源。

  • 内核模式

    与用户模式不同的内核模式,操作系统(或内核)就以这种模式运行。在此模式下操作系统可以访问机器的全部资源。

如果用户态的进程希望执行一些特权操作(比如读取磁盘),那么需要执行操作系统向外提供的系统调用

要执行系统调用,程序必须执行特殊的陷阱指令。执行陷阱指令可以切换到内核态,完成指令后,操作系统将调用一个特殊的从陷阱返回指令,该指令会返回到发起调用的用户程序中,同时将 特权级别降低,回到用户模式。

在 x86 上执行陷阱指令时,处理器会将程序计数器、标志和其他一些寄存器中的信息保存到每个进程的内核栈上。从陷阱中返回时将从栈弹出这些值,并恢复执行用户模式程序。

2.进程切换

2.1 协作和抢占

  • 协作模式

    操作系统相信系统的进程会合理运行。运行时间过长的进程被假定会定期放弃 CPU,以便操作系统可以决定运行其他任务。

    进程通过调用系统调用的方式将cpu控制权转移给操作系统,例如使用yield系统调用。

  • 抢占模式

    操心系统通过时钟中断,时钟设备可以编程为每隔几毫秒产生一次中断。产生中断时,当前正在运行的进程停止,操作系统中预先配置的中断处理程序会运行。此时,操作系统重新获得 CPU 的控制权,可以进行进程切换。

2.2 上下文切换

当操作系统进行进程切换的时候,需要

  • 为当前执行的线程

    来保存通用寄存器程序计数器,以及当前正在运行的进程的内核栈指针

  • 为即将执行的进程

    恢复寄存器程序计数器,并切换内核栈,供即将运行的进程使用

3.进程调度算法

3.1 衡量算法的指标

  • 周转时长

    完成时间 - 到达时间

  • 响应时间

    首次运行 - 到达时间

  • CPU利用率

    cpu执行任务时间/总cpu时间

  • 吞吐量

    完成任务数量/执行任务花费的时间

3.2 算法

3.2.1 先进先出/先到先服务
  • 优点:简单,而且易于实现。
  • 缺点:一些耗时较少的潜在资源消费者被排在重量级的资源消费者之后。当大任务排在小任务前的时候,小任务的周转时长指标很差劲
3.2.1 最短任务优先

这种策略在所有任务都是一起到达的时候,周转时长指标很优秀 。但是现实情况下,大任务可能比小任务先到达。这是由于最短任务优先是一个非抢占式的调度算法。

3.3.3 最短完成时间优先

受到最短任务优先的启发,在它之上加上调度程序的抢占行为,每当新工作进入系统时,它就会确定剩余工作和新工作中, 谁的剩余时间最少,然后调度该工作

3.3.4 轮转

在一个时间片内运行一个工作,然后切换到运行队列中的下一个任务,而不是运行一个任务直到结束。它反复执行,直到所有任务完成。时间片长度必须是时钟中断周期的倍数。因此,如果时钟中断是每 10ms 中断一次, 则时间片可以是 10ms、20ms 或 10ms 的任何其他倍数。

时间片越短,那么在响应时间指标上更优先。但是频繁的上下文切换是影响整体上下文的。

轮转算法在周转时长指标上表现很糟糕,但是在进程执行io等操作时候进行轮转切换,这对于cpu的利用率和效率是有益的。

如上图7.9在执行磁盘io操作的时候,操作系统调度其他的紧凑,让两个任务执行总时间更短。

3.3.5 多级反馈队列

多级反馈队列中有许多独立的队列每个队列有不同的优先级。任何 时刻,一个工作只能存在于一个队列中。多级反馈队列总是优先执行较高优先级的工作(即在较高级队列中的工作)。同一个队列中的任务具备相同的优先级,相同优先级的任务使用轮转

多级反馈队列任务的优先级,会根据观察进程的行为进行调整。如果一个工作不断放弃 CPU 去等待键盘输入,这是交互型进程的可能行为, 因此会让它保持高优先级。相反,如果一个工作长时间地占用 CPU,会降低其优先级。

高优先级队列通常只有较短的时间片,因而这一层的交互工作(例如等待键盘输入这样的io操作)可以更快地切换。相反,低优先级队列中更多的是 CPU 密集型工作,配置更长的时间片会取得更好的效果

3.3.6 比例份额

基本思想很简单:每隔一段时间,都会举行一次彩票抽奖,以确定接下来应该运行哪个进程。越是应该频繁运行的进程,越是应该拥有更多地赢得彩票的机会。

彩票数代表了进程占有某个资源的份额。一个进程拥有的彩票数占总彩票数的百分比,就是它占有资源的份额。

《操作系统导论》读书笔记1——CPU虚拟化,进程的更多相关文章

  1. 《一步一步写嵌入式操作系统》读书笔记1—Skyeye介绍、安装和HelloWorld

    2013-11-14 最近在看<一步一步写嵌入式操作系统>,感觉此书甚好,许多地方讲得很清楚.可操作性强,计划边读边实践边写笔记,希望能够逐步熟悉嵌入式操作系统底层的东西,最终剪裁出一套实 ...

  2. 《30天自制操作系统》读书笔记(5) GDT&IDT

    梳理项目结构 项目做到现在, 前头的好多东西都忘了, 还是通过Makefile重新理解一下整个项目是如何编译的: 现在我们拥有这么9个文件: ipl10.nas    InitialProgramLo ...

  3. 《构建高性能web站点》读书笔记:CPU/IO并发策略

    服务器并发处理能力:单位时间内处理的请求数,吞吐率,reqs/s apache的mod_status,显示的 requests/sec,从启动开始的平均计算值.lighttpd的mod_status显 ...

  4. 《Linux内核设计与实现》读书笔记(四)- 进程的调度

    主要内容: 什么是调度 调度实现原理 Linux上调度实现的方法 调度相关的系统调用 1. 什么是调度 现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计 ...

  5. 《Linux内核设计与实现》读书笔记 第三章 进程管理

    第三章进程管理 进程是Unix操作系统抽象概念中最基本的一种.我们拥有操作系统就是为了运行用户程序,因此,进程管理就是所有操作系统的心脏所在. 3.1进程 概念: 进程:处于执行期的程序.但不仅局限于 ...

  6. 《Unix环境高级编程》读书笔记 第8章-进程控制

    1. 进程标识 进程ID标识符是唯一.可复用的.大多数Unix系统实现延迟复用算法,使得赋予新建进程的ID不同于最近终止所使用的ID ID为0的进程通常是调度进程,也常被称为交换进程.它是内核的一部分 ...

  7. 《Unix环境高级编程》读书笔记 第7章-进程环境

    1. main函数 int main( int argc, char *argv[] ); argc是命令行参数的数目,包括程序名在内 argv是指向参数的各个指针所构成的数组,即指针数组 当内核执行 ...

  8. Node.js高级编程读书笔记 - 2 文件和进程处理

    Outline 3 文件.进程.流和网络 3.1 查询和读写文件 3.2 创建和控制外部进程 3.3 读写数据流 3 文件.进程.流和网络 3.1 查询和读写文件 path 从Node 0.8起,pa ...

  9. 《30天自制操作系统》读书笔记(2)hello, world

    让系统跑起来 要写一个操作系统,我们首先要有一个储存系统的介质,原版书似乎是06年出版的,可惜那时候没有电脑,没想到作者用的还是软盘,现在的电脑谁有软驱?不得已我使用一张128M的SD卡来代替,而事实 ...

  10. 《30天自制操作系统》读书笔记(3) 引入C语言

    这一次的学习相当曲折, 主要是因为粗心, Makefile里面的错误导致了文件生成出现各种奇奇怪怪的问题, 弄得心力交瘁, 因此制作过程还是尽量按着作者的路子来吧. 作者提供的源码的注释在中文系统下是 ...

随机推荐

  1. node中get和post接口

    接口传参 使用ajax请求向服务器接口传参,按http协议的约定,每个请求都有三个部分: 请求行: 保存了请求方式,地址,可以以查询字符串的格式附加一部分数据. 请求头:它可以附加很多信息,其中con ...

  2. iOS包大小计算

    一.LinkMap文件分析 说明:LinkMap数据是根据文章<LinkMap文件分析>中方法实验实测数据. 如何获得LinkMap文件 1.在XCode中开启编译选项Write Link ...

  3. DAST 代码分析

    DA部分 输入图片大小: images.size: torch.Size([1, 3, 512, 1024])labels.size: torch.Size([1, 512, 1024]) input ...

  4. c++学习6 指针变量

    一 指针变量的定义 *是用来修饰指针变量的,通常情况下我们定义的手法都是"类型名"+"*"+"指针变量名称". 有一种简单无脑的" ...

  5. Communications link failure:The last packet successfully received from the server was 0 millisecond ago

    出现这种错误的大致情况如下: 1.数据库连接长时间未使用,断开连接后,再去连接出现这种情况.这种情况常见于用连接池连接数据库出现的问题 2.数据库连接的后缀参数问题 针对上述两种情况,解决方案如下 1 ...

  6. 网易二面-Arthas的底层原理

    众所周知,阿里开源的Arthas已经成为Java开发中调优的基本工具,其功能在于监控JVM运行情况,并对CPU.内存状况生成报告或者是火炬图. 从JDK5开始,java.lang.instrument ...

  7. 洛谷 P1832 A+B Problem(再升级)题解

    START: 2021-08-09 15:28:07 题目链接: https://www.luogu.com.cn/problem/P1832 给定一个正整数n,求将其分解成若干个素数之和的方案总数. ...

  8. JVM-创建一个对象的详细过程

    Person person=new Person(): 1.现在栈中申请一个自己的栈空间 2.类加载检查 每当使用new操作符创建一个对象时,类加载器都会从常量池中寻找该对象的符号引用,如果找到,则根 ...

  9. 量化交易 - matplotlib画candle图

    需要mplfinance包 pip install mplfinance --upgrade   from matplotlib import style import pandas as pd im ...

  10. Java数据类型基础

    Java 数据类型基础 数据类型 强类型语言 要求变量的使用要严格符合规定,所有变量必须先定义后使用 Java数据分为两大类 基本类型(primitive type) 数值类型 整数类型 byte(1 ...