【操作系统】虚拟化CPU、Memory,共享文件

几个概念

  1. CPU、虚拟CPU
  2. 进程
  3. 内存、虚拟地址空间

在这里插入图片描述

  • 物理的CPU被OS虚拟成了多个虚拟的CPU,这些虚拟CPU分别运行各自的程序,这些正在运行的程序被称为进程。
  • 物理内存被OS虚拟成了多个虚拟地址空间,每个进程都有独立的、自己的地址空间,程序的指令和数据都在地址空间中
  • 磁盘被OS虚拟化为文件系统,文件是被多个程序共享的,它并不是多个虚拟的磁盘,不过也不是无条件共享,涉及到例如互斥共享等多个问题,以后再谈。

1 Virtualizing the CPU

我们在Linux系统上运行C语言程序,体会一下虚拟化的意义。

Windows对多用户的支持不是很好,相关的系统API可能也没有,推荐适用Linux或Unix系统。

// cpu.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <assert.h>int main(int argc,int *argv[]){if(argc != 2){fprintf(stderr,"usage:cpu <string>\n");exit(1);}char *str = argv[1];for(int i = 0;i < 4;i++){sleep(1);printf("%s\n",str);}return 0;
}

这个程序很容易,需要运行的时候输入一个参数,比如一个字符,值得解释的是sleep(1),也就是让程序暂停1秒,这非常重要,这意味着物理的CPU在这1s时间可以不用执行该进程,转而执行其他进程。

注意,虚拟后的CPU,最终仍然要在真实物理CPU来执行,要想让每个进程都得到执行,那就应该以合理的方式让他们切换执行

我们先运行一个进程试试看,输入命令./cpu A1
在这里插入图片描述
打印了4个A1,并且是每隔1s打印一个,这与我们的预期相符。

接下来,我们同时运行多个进程试试看,输入命令./cpu A1 & ./cpu B2 & ./cpu C3 &
在这里插入图片描述
按照直观的理解,不应该是

A1
A1
A1
A1
B2
B2
B2
B2
C3
C3
C3
C3

不应该是这样吗?但是看起来这3个进程并不是顺序执行的,而是并发执行的,也就是它们趁着其他进程在sleep的时候,抢占了CPU去执行自己了(注意,我们假设计算机只有1个CPU,而且是单核的)。

这样一来,就出现了图中的乱序了。

我们也能充分的感受到,不要让物理CPU闲着的重要理念,同时我们也能想象到,多个进程同时执行,就会涉及到更多的问题,如果是之前的顺序执行,我们只需要进程1执行,其他等待–>进程1执行完成,进程2执行,其他等待–>进程2执行完成,进程3执行–>进程3执行完成。

也就是说,我们只需要等着一个程序执行完,再执行其他程序,这样很简单,但是效率非常低,比如,如果正在执行的程序不使用CPU,去“sleep”了,或者去找I/O设备“玩”了,CPU就只能呆着,其他程序也不能进来执行,CPU利用率很低

在这里插入图片描述
为了避免这种问题,现代OS都采用了类似多道批处理的技术,正在执行的程序不执行时,其他程序会进入CPU执行,而不会允许CPU空闲,要榨干CPU!

就如上面的程序,当一个进程sleep的时候,其他进程就会进入CPU执行,但是,具体如何执行,取决于OS的调度程序,取决于OS设计的策略,所以目前我们还不能得知它具体是如何运作的(也许你可以查看Linux内核,不过如果你有此能力,就不会看见这篇文章了)。

1.1 补充:实例中的C语言知识

以下请自学

1.1.1 main函数参数,argc和argv

1.1.2 fprintf()

1.1.3 sleep()

2 Virtualizing Memory

我们先上代码

// mem.c

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>int main(int argc,int *argv[]){int *p = malloc(sizeof(int));
//    assert(p != NULL);printf("(%d) memory address of p: %08x\n",getpid(),(unsigned)p);*p = 0;for(int i = 0;i < 4;i++){sleep(1);*p = *p + 1;printf("(%d) p: %d\n",getpid(),*p);}return 0;
}

运行程序./mem
在这里插入图片描述
运行多个进程:./mem & ./mem & ./mem &
在这里插入图片描述
这里,我们依然能够看到的是虚拟化CPU,不过,虚拟化内存在哪里呢?目前还看不出来,因为Linux默认是启动地址空间随机化的,这样会让系统更安全,不易受到攻击,不过为了展现虚拟化内存,我们应该关掉它。

输入命令sysctl -w kernel.randomize_va_space=0,再输入./mem & ./mem & ./mem &

在这里插入图片描述
我们可以看到,三个进程居然地址完全一样!按理说,1个地址只能对应1个进程,所以,你就能体会到虚拟地址空间的含义了,这并不是真实的物理地址,它会通过某种机制,映射到真实物理地址去。

3 Sharing Disk Information

还记得我们刚才的两个程序吗?他们同时启动了多个进程,并且,这几个进程是同一个程序,也就是说,同一个存储在磁盘的文件,被多次读取到了内存,这也就意味着,磁盘信息是可以被同时多次读取的,我们也可以说,这几个进程共享了一个磁盘文件

在这里插入图片描述


思考:为什么内存和CPU要虚拟化为多个,而磁盘却是共享的?

  • 进程是运行中的程序,它是“活的
  • 程序是静止在磁盘中的指令和数据,它是“死的”。

对于正在运行的进程来说,我们需要为其独立地分配一整套生态系统,保证它正常执行,并且每个程序运行时候的结果可能不同,所以,就虚拟地提供了CPU和地址空间,让它们是相互独立的;而对于静止的指令和数据来说,完全没有必要虚拟成多份,那反而是浪费空间,当然这是针对读取而言,写入还需要视情况,不过整体来说,读取信息是及其场景的,将磁盘设为共享也是合理的。


另外要谈的是,磁盘文件必须通过软件和硬件协作的方式,使其持久地保存,而不是很快就消失了,或者被其他数据覆盖掉了。

4 Concurrency

虚拟化对应的是进程,而并发对应的不仅仅是OS的进程,在OS之上的应用程序,也存在并发的问题,他就是多线程编程;虚拟化让一个CPU能并发地执行多个进程,而一个进程,也能并发地执行多个线程。

你一定知道多线程编程,是的,就是那个,我们现在重新审视一下它。

// threads.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>volatile int counter = 0;
int loops;void *worker(void *arg) {int i;for (i = 0; i < loops; i++) {counter++;}return NULL;
}int main(int argc, char *argv[]) {if (argc != 2) {fprintf(stderr, "usage: threads <value>\n");exit(1);}loops = atoi(argv[1]);pthread_t p1, p2;printf("Initial value : %d\n", counter);pthread_create(&p1, NULL, worker, NULL);pthread_create(&p2, NULL, worker, NULL);pthread_join(p1, NULL);pthread_join(p2, NULL);printf("Final value : %d\n", counter);return 0;
}

我们进行编译gcc threads.c -o threads -lpthread,注意,<pthread.h>不是Linux默认的库,编译链接需要加上参数-lpthread,也就是需要链接额外的Import Library:libpthread.a

我们进行测试:
在这里插入图片描述
对于输入的参数N,输出结果应该是2N(先知道事实,看不懂多线程程序没有关系),但是最后两个,当参数足够大,比如5亿的时候,结果就诡异了

这是由于计数器的值的更新不是原子操作,他需要:

  • 内存–>寄存器
  • 寄存器递增
  • 寄存器–>内存

3个步骤,但是,这几个步骤可能被其他操作打断,这就造成了结果的诡异。关于原子操作以后再说。
在这里插入图片描述

5 小结

我们谈了几件事儿

  • 物理CPU – 虚拟化CPU – 多进程并发
  • 物理内存 – 虚拟地址空间 – 进程独立地址空间
  • 磁盘(持久性) – 文件系统 – 共享磁盘信息
  • OS之上的并发:单个进程中的多线程

版权声明

本文是读书笔记,来自于书籍《Operating System:Three Easy Pieces》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/384680.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】编译C语言文件(-o -lpthread)

在gcc中使用-o编译 对于一个一般的程序&#xff0c;直接使用gcc <C语言文件名> -o <编译后生成的文件名>即可&#xff0c;例如以下程序&#xff1a; // cpu.c #include <stdio.h> #include <unistd.h> #include <stdlib.h>int main(int argc,…

【Linux】Ubuntu下进行C语言编程

前言 需要您会使用Windows下cd切换目录的基本命令&#xff0c;否则请先自学相关知识&#xff0c;之后再阅读本文。 0 基础命令 介绍最基础的Linux终端命令。 su - root&#xff1a;切换到root用户&#xff08;不用也可以&#xff09;ls&#xff1a;查看当前目录位置cd&…

【Linux】Ubuntu 18下安装Vim自动补全插件YouCompleteMe(可高速下载安装)

前言 本文写于2020年10月&#xff0c;如果你多年后看见这篇文章&#xff0c;方法可能已经失效&#xff0c;但是请牢记&#xff0c;尽量下载你所处时代的最新版本的软件&#xff0c;会减少很多麻烦。 摆正心态 即便按照本文操作&#xff0c;由于你的系统状态和我的不一样&…

【操作系统】进程调度(1):FIFO(先进先出)算法 原理与实践

0 前言 本文基于书籍《Operating System&#xff1a;Three Easy Pieces》。 中译本&#xff1a;《操作系统导论》&#xff0c;中译本质量还可以&#xff0c;但是英文版后来的更新&#xff0c;中文版目前没有进行同步更新&#xff08;写下此文的时间是2020年10月&#xff09; 1…

【操作系统】进程调度(2a):SJF(短任务优先) 算法 原理与实践

0 前言 接上一篇文章&#xff1a;进程调度&#xff08;1&#xff09;&#xff1a;FIFO&#xff08;先进先出&#xff09;算法 原理与实践 1 前提铺垫 请参考上一篇文章的前提铺垫部分&#xff0c;本文与之完全一致。 2 SJF 原理 SJF&#xff08;Shortest Job First&#x…

【操作系统】进程调度(2b):STCF(最短完成时间优先) 算法 原理与实践

0 前言 接上一篇文章&#xff1a;进程调度&#xff08;2a&#xff09;&#xff1a;SJF&#xff08;短任务优先&#xff09; 算法 原理与实践 1 前提铺垫 与上一篇同。 2 STCF 原理 STCF&#xff08;Shortest Time-to-Completion First&#xff09;最短完成时间优先。 2.1…

【操作系统】进程调度(3):RR(轮转) 算法 原理与实践

0 前言 接上一篇文章&#xff1a;进程调度&#xff08;2b&#xff09;&#xff1a;STCF&#xff08;最短完成时间优先&#xff09; 算法 原理与实践 1 前提铺垫 除了与上一篇相同的&#xff0c;这里介绍新的基础知识。 1.1 三种类型的程序 计算密集型&#xff08;CPU导向&…

【操作系统】进程调度(4):I/O、不可预测的运行时间

0 前言 上一篇文章&#xff1a;进程调度&#xff08;3&#xff09;&#xff1a;RR&#xff08;轮转&#xff09; 算法 原理与实践 1 前提铺垫 与上一篇同。 2 引入I/O操作 之前我们一直提及的是计算密集型程序&#xff0c;现在我们的程序可以进行I/O交互了&#xff0c;它会…

【计算机系统设计】重点 · 学习笔记(0)

HDL等硬件描述语言&#xff0c;例如Verilog&#xff0c;是并行的&#xff0c;而不像软件一样的顺序执行的&#xff0c;例如很多的always块&#xff0c;initial块&#xff0c;都是并行的&#xff0c;他们会转换为硬件电路&#xff0c;而在仿真的时候&#xff0c;他们也是并发执行…

【计算机系统设计】学习笔记(1)03,04

疑问&#xff1a;sw和lw指令&#xff0c;获取的地址不是4的整倍数&#xff08;字节不对齐&#xff09;的时候&#xff0c;应该如何处理&#xff1f; 东南大学MOCC 计算机系统综合设计 03 03-1 寄存器 介绍了MIPS寄存器&#xff0c;32个寄存器的基本功能和使用&#xff0c;注…

【期末考试】计算机网络、网络及其计算 考试重点

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 计算机网络及其计算 期末考点 &#x1f680;数…

【计算机系统设计】学习笔记(2)

5.1 对于CPU与外界的读写&#xff0c;只有load和store指令能够做&#xff0c;所以很多情况下&#xff0c;直接通过bypass跳过去了&#xff0c;或者闲置&#xff0c;尤其对于流水线&#xff0c;更应该直接跳过而不是闲置&#xff08;如何设计?&#xff09;。 另一方面&#xf…

【计算机系统设计】重点 · 学习笔记(1)(资源消耗)

这一点先浅显理解&#xff0c;就好比我要造一个楼 我是用现成的材料造节省?还是需要用XX材料&#xff0c;但是XX材料还需要现成材料造呢&#xff1f; 这也不一定&#xff0c;但是基本来说&#xff0c;如果使用现有资源&#xff0c;能够直接用&#xff0c;那其实是最好不过的…

【计算机系统设计】重点 · 学习笔记(0)(数据通路设计思想)

重点1&#xff1a;05.1 设计思想 设计思想至关重要&#xff0c;这决定了你能不能自己根据ISA设计出来CPU架构&#xff0c;而不是只是抄别人的&#xff0c;也决定你能不能完成自己的设计更优化的架构。 描述方式约定 6 数据通路 ≠ Verilog代码 我们构建的数据通路&#…

【计算机系统设计】实践笔记(1)数据通路构建:取指部件分析

0 核心思想 根据指令功能&#xff0c;分析出需求&#xff0c;从而得出需要的部件、控制信号以及其他设计。 1. 针对的指令 取指阶段&#xff0c;针对所有指令&#xff0c;任何指令都需要进行取指。 2 功能&#xff08;需求&#xff09;分析 CPU的内部采用的是字节编址&…

【计算机系统设计】实践笔记(2)数据通路构建:第一类R型指令分析(1)

0 回顾 上一次实践笔记&#xff08;0&#xff09;我们实现了一个最简单的&#xff0c;能够每个上升沿4的PC。 我们最需要关注的就是器件功能的独立性&#xff0c;避免内外功能混杂&#xff0c;同时一定要注意脑中有电路&#xff08;RTL级描述的抽象电路而不是实际的门级电路&…

接口的抽象与实现(概述)

概述 我们先建立一个整体的接口格局观&#xff0c;建立知识地图&#xff0c;了解接口的大概面貌。 整体来说&#xff0c;就这点事儿&#xff0c;4个箭头代表了所有&#xff01; 三个器件4个箭头 把这几个都想明白&#xff0c;就完事儿了。 第一层&#xff08;顶层&#xf…

从功能层次,阐述CPU、接口和外设之间的交互

我们从功能抽象层次&#xff0c;阐述一下CPU、接口芯片和外设之间的交互情况&#xff1a; 三个器件4个箭头 我们依次将其描述清楚。 数据 箭头①和③ CPU给接口可以发送数据&#xff0c;然后接口暂存数据&#xff0c;之后再发给外设&#xff0c;这就是数据缓冲。 发送的数…

Vivado工程文件分类

只需要在创建的时候&#xff0c;选择自定义路径即可&#xff0c;最好在原有的new文件夹下新建文件夹。 至于路径的匹配&#xff0c;可以自己试试&#xff0c;在原有默认new下创建文件夹&#xff0c;选中新的文件夹后&#xff0c;内部的Verilog文件可以访问外部new文件夹的文件&…