操作系统读写者问题实验报告_什么是操作系统?

什么是操作系统为什么说C / C++ 更底层 电脑里只有一个CPU, 多线程是怎么实现的

一些简单口胡, 也算对本学期的学习做一个总结

一言蔽之, 操作系统是管理下层硬件, 为上层软件提供统一的, 容易理解的抽象API的 软件 .

硬件是什么样的 – 冯诺依曼结构

6aff15e932121a96a69dfece7ff26b85.png

CPU寄存器

图中, 读写速度可以认为和CPU运算一样快, 读取内的数据几乎可以认为不花费时间.断电失去数据.

Cache

这是比较复杂的一部分, 我们待会儿返回来讲

内存

图中部分. 就是我们熟知的电脑内存啦. 现在你的笔记本的内存一般是 4-16GB. 但是访问内存也比访问寄存器耗时得多, 你可以在图中看见大概是十倍时间.断电失去数据.

硬盘

图中部分, 最近几年火起来的SSD就是一种硬盘. 就像你可能知道的一样, SSD, 或者叫做固态硬盘, 比传统的机械硬盘快很多, 但是读写速度依然比内存慢得多. 从图中你可以看到, 大概慢了三个数量级

磁带

老掉牙的东西啦. 一般只用很老的老项目使用了.我们这次不讨论它.

输入输出设备

包括计算机和人交互的一切接口. 键盘, 鼠标, 屏幕, 网卡等等.

好了, 关于计算机硬件, 我们就说这么多了. 如果你有一点晕, 没关系! 你只需要大概记住这两条就可以了:

  • 只有CPU会计算
  • 存储设备是多级的. 从寄存器到内存到硬盘, 速度越来越慢, 容量越来越大

为什么你需要操作系统/h2>

假设你是一个程序员, 你写了一个程序

你的程序经过编译大概变成了这样的CPU指令

指令送进内存, CPU从内存里读取指令(读取指令和执行指令是CPU硬件实现的), 执行.访问内存, 执行计算. 一起看起来都很美好. 看起来我们不需要一个操作系统.

没错! 恭喜你发现一个惊天大秘密! 我们就是不需要操作系统!

如果我们的程序就一直这么简单的运行, 那可能的却不需要. 可这不符合我们对现代计算机的认知. 浏览器和音乐播放器是两个不同的程序,为什么你能一边刷网页一边听歌一定是这两个程序一起运行了, 可是按说你只有一个CPU, 一套内存, 只能跑一个程序, 为什么你能同时运行好几个程序呢

是的, 这就是操作系统最基本的功能, 操作系统负责管理CPU和内存, 让每一个程序都觉得自己是独立运行的.

操作系统怎么做到的虚拟化

虚拟化这个词可能和大数据和云计算一起在你的耳边回荡已久. 其实它就是欺骗和造假的另一个说法. 还记得我们说过, “要让每一个程序认为自己是独立运行的”吗我们怎么办呢, 我们假造一系列运行需要的硬件, 让每个程序都觉得自己占有了硬件, 正在独立运行. 而且让它们的执行互不干扰

进程

进程是欺骗的开端. 一个进程就是可以让一个程序感觉自己正在独立运行的所有环境. 想一想, 一个程序运行大致需要做什么/p>

  1. CPU要执行程序指定的CPU指令
  2. 执行的过程中必然要访问内存
  3. 执行的过程可能要与人交互

所以一个进程也围绕着虚拟出一个CPU, 一份内存展开.

CPU虚拟-分时复用

怎么虚拟出一个CPU呢好办, CPU运行速度比人能察觉的快得多. 它可以在两个进程中间切换, 这一个瞬间给你显示网页, 下一个瞬间给你播放音乐. 对,事实就是这样的, 你的CPU在负责给你唱歌的时候,只是在数十个任务中时不时唱一句, 但是你会觉得音乐是连续的.

虚拟内存

这是比较复杂而且比较细节的一节. 我希望我能讲明白为什么要虚拟内存, 解决思路大概是什么样的, 现在的方案做到了什么事情.至于实现细节其实不那么重要. 我建议阅读的时候考虑先跳过 “怎么实现虚拟内存管理” 一节.

为什么要虚拟内存/h2>

你刚刚可能就开始困惑, CPU只有一个, 在确定时间只能执行一条CPU指令, 的确可以说是只能执行一个程序, 可是我有16GB大内存, 为什么不能同时执行多个程序

是这样的, 我们重新看这两句CPU指令

我们刚刚说这两句CPU指令把a和b分别读入寄存器. 这是不准确的. 内存是不知道谁是a谁是b的, 内存只能由一个确定的地址, 访问内存里特定的一块地方. 存储器是按地址访问、线性编址的空间,像一条数轴

我们假设自己拥有一个4GB的内存. 每Byte是一个最小读取单位. 一个内存地址由32bit 二进制数描述(你会发现刚好能寻址4GB) , 刚刚两句CPU指令可能会是这样

在这两条CPU指令运行之前, a和b就被放在那个地址了.(这个事情也是操作系统保证的, 我们晚点谈)

绕了这么一大圈, 问题终于出现了, 程序员写代码的时候能考虑自己的程序运行的时候是不是和别人一起运行吗或者说, 程序员能够一开始就知道自己的数据被放到什么地方, 从而更改自己的代码吗

不行. 每一个程序员写代码的时候, 总是假设只有自己的程序在运行,(如果不这样, 编程会变得异常困难) 对应到内存上, 每一个程序员总是假设计算机的4G内存全部属于他.所以他会从处开始使用内存.

这段对你来说可能有一点难理解. 因为你编程的时候好像从来没有考虑过内存的事. 在大部分高级语言实现里, 程序员是不能直接看见内存的. 这是同为高级语言, 我们说C更加底层的原因之一. 在这里, 你需要记住的是编译器也不能做到这样的事情.

说到这里, 你可能有点明白问题在哪里了, 问题在于数据放在内存的什么位置, 在程序运行之前就写定了.

好了, 假设我们有一个程序A, A里的数据被放在地址, 又有一个程序B, B里的数据也被放在位置. 他们要同时运行, 怎么办内存的空间放得下两个程序的数据, 但是我们通过访问的时候, A程序和B程序都希望访问到自己的数据, 如果他们一起运行, 内存处到底应该放什么呢

所谓虚拟内存, 就是给每个进程一个它自己的映射关系F, 使

物理内存地址=F(虚拟内存地址)” role=”presentation” style=”text-align: center; position: relative;”>=F() A和B都在访问自己的, 但是由于他们的映射关系F不同, 这两个访问会访问不同的物理地址(内存的真正的地址).为什么要叫虚拟化呢因为程序A和B(或者说开发A和B的程序员) 是看不到这个映射关系的. (从时间关系上也没法看到, 这个映射关系到程序运行的时候才真正确定下来) , 他们就是认为整个机器的4GB内存都属于他, 他访问内存使用的全是虚拟内存地址.

操作系统分配和管理这个映射关系

你可能会好奇, 那每一个进程需要的数据到进程运行前一刻才导入内存行不行呢运行进程A就把进程A需要的数据全部导入内存, 到需要切换到进程B的时候才把B的数据导入内存行不行呢不行, 因为CPU比内存快得多, 这种方式会导致每次CPU切换任务的时候浪费大量的时间把数据填入内存, 那CPU运行多快都没用了.所有的时间都浪费在等内存了.

怎么实现虚拟内存管理

我们绕了好大的圈子, 终于说明白了问题在哪. 我们再描述一下我们的问题:

  1. 我们得给所有每一个的进程一个虚拟的内存映射, 让它认为从开始所有的内存都属于自己, 每一个进程都可以使用从0到4GB的内存空间.
  2. 分配和管理这个映射关系, 让每一个进程都能访问到他想要的数据 (这并不简单 , 举例说一个一个进程认为自己把数据a保存在处, 操作系统得做两件事情来保证他能访存到)
  3. 把数据a保存在真实的内存的某个地方, 假设是.
  4. 给进程映射管理里添加一条F( ) =
  5. 保证每个进程的访存不相互干扰. 更具体一点, 保证进程A不能访问到进程B在内存中的数据(不然网页云音乐就能监视你所有的应用使用情况了)

接下来我要谈论具体的方案了, 这部分细节多, 很难避免讲的又臭又长, 我建议你可以先跳过去, 或者先看看图了解个大概, 总之我对这一节能不能写好最没有把握

在谈论具体的方案之前, 你需要先记住这几个概念: (太不公平了! 我还什么都没解决就要记住这么多概念)

  • 32位系统和64位系统: 内存是按Byte访问的(即访问内存0读出的是第0byte,即第0bit到第7bit).所谓32位系统就是指, 内存编址是由一个32bit的数字确定的. 你会发现32bit能描述的最大地址空间就是4GB, 我们基本是基于32位系统做讨论.
  • 内存虚拟地址, 每个独立运行的进程看见的内存的地址, 也是编写应用程序的程序员”看见的”地址.
  • 内存物理地址, 数据被实际上保存的地址, CPU访存的时候, 内存接到的真正的访存地址( 如果你觉得他们没有区别, 直接停止阅读联系我, 我上面一定没写好)
  • 现代解决方案一共有两套, 两套解决方案都是需要特殊硬件协助的. 虽然我们强调操作系统是软件, 但是它会需要和一些特殊的, 应用程序员看不见的硬件打交道. 关于内存管理, 这些特殊硬件都是CPU生产商做在CPU里的, 是CPU提供的功能.

现代解决方案有两套, 分别叫做段式管理页式管理.你会发现其实他们挺相似. Windows采取的是段式管理 + 页式管理.Linux采取的是纯页式管理.

段式管理

段式管理示意图

04c4472f08d836f59b10681fe35c47df.png

MMU: Memory management unit, CPU的一部分, 负责和内存管理相关的硬件, 我们刚刚说的检查访问是否合法, 添加基地址偏移这些过程都是它去做的.

逻辑地址: 虚拟地址

操作系统的工作就是了解用户进程申请多少内存, 分配内存, 维护段表(段表里维护着所有最近可能运行的进程的基地址偏移和最大访存长度), 总之就是保证每一个进程都能找到自己的物理内存的一切杂活.

页式管理

你会意识到, 段式管理是建立在运行前申请和分配内存机制之上的.如果你想在进程运行的过程中申请内存, 段式管理就会变得很困难. 可是C/C++确实支持动态分配(C中的, C++中的), 这部分内存是在运行中才知道需要分配的.( C/C++的静态数据动态分配的区别就在这里, 普通数组长度必须是一个常数, 就是因为这部分长度必须在编译的时候就确定下来.从而在运行前申请好. 而动态分配的数组长度可以是一个表达式, 即可以在运行到那一句代码的时候才决定申请内存空间大小).所以我们需要一个可以方便地在运行的时候分配内存空间的方案.

在讨论页式管理的虚拟地址->物理地址映射关系之前, 我们先想一想, 最灵活的映射关系是什么样的不借助任何公式, 简单的记录每一条映射记录 应该是最灵活的. 用说, 就是我们有一个 记录每一个虚拟地址应该映射到某个物理地址.

但是真的这么存的话, 需要太多的空间存储映射关系了. 那么我们怎么办呢我们把每的内存空间划分为一个页, 从内存处开始给页编号. 即是第0页, 是第1页. 你可能已经发现了, 一个32bit的地址, 截前20bit就是页号.

那么我们就可以稍微更改一下我们的dict了

来源:weixin_39675289

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2020年9月18日
下一篇 2020年9月18日

相关推荐