4

《操作系统导论》读书笔记1——CPU虚拟化,进程 - Cuzzz

 1 year ago
source link: https://www.cnblogs.com/cuzzz/p/17237235.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

系列文章目录和关于我

一丶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)到内存中,加载到进程的地址空间中

    image-20230306072917581
  • 为进程分配空间

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

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

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

  • 其他初始化任务

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

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

5.进程的状态#

image-20230306073241109

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

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

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

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

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

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

6.进程API#

6.1.fork#

image-20230320155547399

调用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 请求。这 样做会导致处理器引发异常,操作系统可能会终止进程。

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

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

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

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

image-20230320163811459

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

image-20230320164045240

2.进程切换#

2.1 协作和抢占#

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

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

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

2.2 上下文切换#

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

  • 为当前执行的线程

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

  • 为即将执行的进程

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

image-20230320170607859

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的利用率和效率是有益的。

image-20230320173232314

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

3.3.5 多级反馈队列#

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

image-20230320173740536

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

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

3.3.6 比例份额#

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

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK