操作系统笔记

知识速览

简介

什么 是操作系统

回答这个问题,不如反问你**“没有操作系统,你怎么使用计算机”**

比如你想做一个程序,输入什么二进制数值,对应位置的二极管就会发光。

你需要手动控制计算机的输入设备,使得接下来输入的数据会直接送到内存中。

于是有了下面这段代码

![](https://img-blog.csdnimg.cn/img_convert/c8deefd01bdc9dd7741f5076bbea779f.png#align=left&display=inline&height=795&margin=[object Object]&originHeight=795&originWidth=824&size=0&status=done&style=none&width=824)

接通了微型计算机的电源后,请按下Z80 CPU上的DMA请求开关。
在这个状态下,拨动用于输入内存程序和指定内存输入地址的两个指拨开关,把代码清单3.1所示的程序一行接一行地输入内存。
先来输入第一行代码,拨动用于指定地址的指拨开关,设定出第一行代码所在的内存地址00000000,然后拨动用于输入程序的指拨开关,设定出程序代码00111110。
再然后按下用于向内存写入程序的按键开关。接下来输入第二行代码,设定出内存地址00000001,设定出程序代码11001111,再次按下按键开关。
反复进行这三步操作,直至输入完程序代码的最后一行。
所有的指令都输入完成后,按下用于重置CPU的按键开关,控制DMA请求的快动开关就会还原成关闭状态,与此同时程序也就运行起来了。

关于为什么每次往内存输入8位数据

因为内存中每一位地址都能存8个比特,即1个字节。

下面可以通过一个程序来验证,我们知道c语言中int是占4个字节的

#include <iostream>
using namespace std;
int main()
{
    int v[30] = {1,2,3};
    cout<<v<<" "<<v+1<<" "<<v+2<<" "<<v+3<<endl;
    return 0;
}
运行结果:
0x6dfe68 0x6dfe6c 0x6dfe70 0x6dfe74

画图来解释就是

![](https://img-blog.csdnimg.cn/img_convert/9ca729570f7fc9bee556718f652c061c.png#align=left&display=inline&height=655&margin=[object Object]&originHeight=655&originWidth=566&size=0&status=done&style=none&width=566)

一个hello world的执行过程

有了操作系统,我们就不用自己做这些棘手的事了,比如想输出一个hello world,操作系统做的工作有:

![](https://img-blog.csdnimg.cn/img_convert/311e3c71d4e04137f6a329e8e0443a79.png#align=left&display=inline&height=620&margin=[object Object]&originHeight=620&originWidth=944&size=0&status=done&style=none&width=944)

![](https://img-blog.csdnimg.cn/img_convert/3a64db63679826dacc6697aa5eff4e03.png#align=left&display=inline&height=551&margin=[object Object]&originHeight=551&originWidth=911&size=0&status=done&style=none&width=911)

比较高大尚的概括

计算机系统由硬件(子)系统和软件(子)系统组成。前者是借助电、磁、光、机械等原理构成的各种物理部件的有机组合,是系统赖以工作的实体。后者是各种程序和文件,用于指挥全系统按指定的要求进行工作。

操作系统就是负责登记谁在使用什么样的资源,系统中还有哪些资源空闲,当前响应谁对资源的要求,以及收回哪些不再使用的资源等。操作系统要提供一些机制去协调程序间的竞争与同步,提供机制对资源进行合理使用,施加保护,以及采取虚拟技术来“扩充”资源等。

操作系统种类

单道批处理系统

通常是把一批作业以脱机输入方式输入到磁带(盘)上,并在系统中配上监督程序,在监督程序的控制下使这批作业能一个接一个地连续处理。其自动批处理过程是:首先,由监督程序将磁带(盘)上的第一个作业装入内存,并把运行控制权交给该作业。当该作业处理完成时又把控制权交还给监督程序,然后由监督程序将磁带(盘)上的第二个作业调入内存。计算机系统就这样自动地一个作业一个作业地进行处理,直至磁带(盘)上的作业全部完成,这样便形成了早期的批处理系统。

问题明显,做I/O时处理机处于空闲状态

多道批处理系统

在多道批处理系统中,用户所提交的作业都先存放在外存并排成一个队列,该队列称为“后备队列”;然后,由作业调度程序按一定的算法从后备队列中选择若干个作业调入内存,使它们共享处理机和系统中的各种资源,以达到提高资源利用率和系统吞吐量的目的。

作业从提交给系统开始直至完成,需要经过以下两次调度:

  • 作业调度:指按一定的作业调度算法,从外存的后备程序队列中选择若干个作业调入内存。
  • 进程调度:指按一定的进程调度算法,从已在内存的作业中选择一个作业,将处理机分配给它,使之执行。

实时操作系统

由于是批处理,用脱机方式进行,所以没有交互能力

分时系统

分时系统是指一台主机上连接了多个带有显示器和键盘的终端,同时允许多个用户共享主机中的资源,每个用户都可通过自己的终端以交互方式使用计算机。

为了确保系统能及时处理,必须彻底改变原来批处理系统的运行方式。一方面用户程序不能先进入磁盘,然后再调入内存。因为程序在磁盘上不能运行,当然用户也就无法与程序进行交互,因此,程序应直接进入内存;另一方面不允许一个程序长期占用处理机直至它运行结束或发生I/O请求后,才调度其他程序运行。应该规定每个程序只运行一个很短的时间(例如,0.1s,通常把这段时间称为时间片),然后便暂停该程序的运行并立即调度下一个程序运行。如果在不长的时间(例如3s)内,能使所有的用户程序都执行一次(一个时间片时间),便可使每个用户都能及时地与自己的程序交互,从而可使用户的请求及时得到响应。

window属于单用户多任务操作系统;
分时系统貌似属于多用户多任务操作系统;
不过这些好像又被称为微机系统,概念很模糊。

实时系统

实时系统是指系统能及时(或即时)响应外部事件的请求,在规定的时间内完成该事件的处理,并控制所有实时任务协调一致地运行。

关于开机

这部分参考《图解Linux内核》,大致意思如下:

启动电源的时候,硬件被设置为访问0xffff0位置的指令,这个位置的程序会把BIOS写入到内存中,BIOS应该是刻录在主板上的一块ROM硬件。之后BIOS会把操作系统给引入内存,同时擦除自己,最终结果就是操作系统位于内存中第一个位置,即地址从0x00000000开始。

进程

在多道程序环境下,要使程序运行,必须先为它创建一个或几个进程,并为之分配必要的资源。

作业调度的基本任务是从后备队列中按照一定的算法,选择若干个作业,为它们分配必要的资源(首先是分配内存)。在将它们调入内存后,便为它们建立进程,使之成为可能获得处理机的就绪进程;并将它们按一定算法插入就绪队列。而进程调度的任务则是从进程的就绪队列中,按照一定的算法选出一进程,把处理机分配给它,并为它设置运行现场,使其投入运行。

程序、进程、线程、协程关系盘点

先比较程序和进程

程序:由若干条具有一定功能的指令所组成的解题顺序和步骤

进程:程序的一次执行,是操作系统进行资源分配和保护的基本单位

为什么要引入进程?

因为程序不适合并发执行,会出现很多问题:

  • 程序执行的间断性
  • 程序执行失去封闭性
  • 程序执行结果的不可再现性

最要命的是这个不可再现性,如果连这个都不能保证,那程序的执行就失去了意义。因为你本来就是要运行这个程序来达到一个结果,而这个结果应该是可以预期的。

在程序并发下,程序并不能保护好自己的现场。

所以引入了进程,其实在我看来,进程就是对程序的代理。程序段、数据段及进程控制块3部分构成了一个进程的实体。其实就是增加了一些信息(PCB进程控制块),让程序并发变得可控

引入进程的目的也正是为了使其程序的执行能和其他程序的执行并发执行,而程序是不能并发执行的。

PCB进程控制块的作用

进程控制块PCB的作用是使一个在多道程序环境下不能独立运行的程序(含数据)成为一个能独立运行的基本单位,一个能与其他进程并发执行的进程。

也就是说,操作系统是根据PCB来对并发执行的进程进行控制和管理的。

例如,当操作系统要调度某进程执行时,要从该进程的PCB中查出其现行状态及优先级;

在调度到某进程后,要根据其PCB中所保存的处理机状态信息去设置该进程恢复运行的现场,并根据其PCB中的程序和数据的内存地址找到其程序和数据;

进程在执行过程中,当需要和与之合作的进程实现同步、通信或访问文件时,也都需要访问进程控制块PCB;

当进程因某种原因而暂停执行时,又需将其断点的处理机环境保存在PCB中。

可见,在进程的整个生命期中,系统总是通过其PCB对进程进行控制。亦即,系统是根据进程的PCB而感知到该进程的存在,进程控制块PCB是进程存在的唯一标志

再说说进程和线程

为什么引入线程的概念?

纵观操作系统的发展,可以看到其都是在不断提高CPU的吞吐量,即让它不用闲下来。

一开始的单道程序,后来想引进多道程序并发,但是会出问题,所以引进了进程。从某种意义上说,开启越多进程就能提高吞吐量。

这样会有什么问题呢

问题其实是很明显的。

我们根据常识都能知道,电脑运行太多进程就会卡,假如操作系统采用时间片轮转的方式,那每个进程的轮转周期就会变长,渲染也就跟着慢了。

既然不能通过增加进程的方式,还有什么办法提高吞吐量?

方法很简单,就是在进程里面再划分出多个线程。

你可以类比于—一个操作系统程序有n个进程,同样一个进程可以有n的线程

如果没有线程,当一个进程进行I/O请求时遭到阻塞,那么操作系统就会把这个进程加入阻塞队列,处理好现场,并通过算法把处理机分给就绪队列中的其他进程。其实这就是上下文切换,而做这些事比较花时间。

那线程的好处是什么呢

一个进程有多个线程时,当一个线程阻塞,并不会使进程阻塞,而是会把处理机分配给进程中的另一个线程。

以操作系统的角度看,这个进程还在运行,所以就不需要做进程的切换了。

线程还有什么其他特点

由于会有线程切换,所以线程本身也保留有一点必要的现场信息

之后,其他数据包括资源都是共享的,所以线程切换时间比较快

新建线程时,由于内存空间已经由进程申请好了,所以也不用花费太多时间

目前,线程实现有用户自己和操作系统实现两种,前者操作系统只感觉到进程,后者操作系统感觉到线程

貌似JVM的线程切换是内核级线程,据说能利用多核优势

使用系统线程时,当线程执行系统调用时,用户线程实现的进程整个被阻塞,而系统线程则只阻塞那个线程。

什么是协程?和进程、线程的区别

线程是系统级别的,它们由操作系统调度,而协程则是程序级别的由程序根据需要自己调度。

协程的优点

(1)无需线程上下文切换的开销,协程避免了无意义的调度,由此可以提高性能(但也因此,程序员必须自己承担调度的责任,同时,协程也失去了标准线程使用多CPU的能力)

(2)无需原子操作锁定及同步的开销

(3)方便切换控制流,简化编程模型

(4)高并发+高扩展性+低成本:一个CPU支持上万的协程都不是问题。所以很适合用于高并发处理。

协程的缺点

(1)无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上.当然我们日常所编写的绝大部分应用都没有这个必要,除非是cpu密集型应用。

(2)进行阻塞(Blocking)操作(如IO时)会阻塞掉整个程序

Python中可用Gevent来实现

协程

协程,英文Coroutines,是一种比线程更加轻量级的存在。

最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。

这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。

Python语言
正如刚才所写的代码示例,python可以通过 yield/send 的方式实现协程。在python 3.5以后,async/await 成为了更好的替代方案。
可以看看这篇博客的代码
Go语言
Go语言对协程的实现非常强大而简洁,可以轻松创建成百上千个协程并发执行。
Java语言
如上文所说,Java语言并没有对协程的原生支持,但是某些开源框架模拟出了协程的功能,有兴趣的小伙伴可以看一看Kilim框架的源码:

用户态和内核态

这里把内核态称为管态,划分这两个概念是方便对进程安全访问的控制。

为了方便用户、保证系统的正确执行,将“启动I/O”等一类可能影响系统安全的指令定为特权指令。

当处理机处于管态时可以执行包括特权指令在内的一切机器指令,当处理机处于目态时不允许执行特权指令。因此,操作系统程序占用处理机时,应让处理机在管态下工作,而用户程序占用处理机时,应让处理机在目态下工作。如果处理机在目态工作,却取到了一条特权指令,此时处理机将拒绝执行该指令,并形成一个“非法操作”事件。中断装置识别到该事件后,转交给操作系统处理,由操作系统通知用户“程序中有非法指令”,必须修改。

补充系统调用

系统调用会执行敏感信息,所以要在内核态下进行。

传统的用户进程调用一个系统调用时,要由用户态转入核心态,用户进程将被阻塞。当内核完成系统调用而返回时才将该进程唤醒继续执行。

进程的几个过程

这个过程貌似用到挺多原语的

进程的创建

系统中只允许进程,所以肯定是进程创建进程,然后就有了进程树的关系

![](https://img-blog.csdnimg.cn/img_convert/4331ad7c7cadbdbdaedac9e555fd7e8b.png#align=left&display=inline&height=425&margin=[object Object]&originHeight=425&originWidth=526&size=0&status=done&style=none&width=526)

基本步骤如下,太多被我缩小了,看着不那么累

![](https://img-blog.csdnimg.cn/img_convert/bbc511a3245521d559756ec270a7fd9e.png#align=left&display=inline&height=485&margin=[object Object]&originHeight=485&originWidth=821&size=0&status=done&style=none&width=821)

进程的销毁

一般是发送中断信号,通知操作系统来善后

![](https://img-blog.csdnimg.cn/img_convert/9a088d7a5db9968cb084602ff85f09d4.png#align=left&display=inline&height=341&margin=[object Object]&originHeight=341&originWidth=818&size=0&status=done&style=none&width=818)

进程的阻塞与唤醒

一般申请临界资源无果的情况下,自我阻塞,进入阻塞队列。那个抢到资源的进程,在使用完资源之后会去唤醒阻塞队列的进程,可以重新抢资源啦。

如果用信号量来表示

P(V) // 这一步失败时就会自我阻塞
do something
S(V) // 这一步成功后就会唤醒阻塞队列

多个进程争夺临界资源

同步机制的必要

同步机制有什么规则

  • 空闲让进
  • 忙则等待
  • 有限等待:不能死等
  • 让权等待:等不了,释放处理机

原语实现的信号量挺不错的,但每个进程都得自己实现,好麻烦啊

之后有了管程的概念,~~管理进程?名字取得真、、、~~管程以特定数据结构记录下硬件等资源使用情况

进程申请资源,统一向管程申请,  //额,这不就是单例模式吗

死锁的产生

![](https://img-blog.csdnimg.cn/img_convert/4e9436710ff768123a8ed0b300f01f21.png#align=left&display=inline&height=429&margin=[object Object]&originHeight=429&originWidth=508&size=0&status=done&style=none&width=508)

发生死锁时,我们可以推断出四个结论:

  • 互斥条件:一个资源只被一个进程独占
  • 请求和保持条件:进程被阻塞,但是死也不松开资源

如果能松开,就能像计算机网络中那个,释放资源,重新来过。监听没有冲突,边发边听

  • 不剥夺条件:抢到的资源,除非自己用完了释放,否则别人不能抢
  • 环路等待条件:如上图,死锁发生,肯定存在环
死锁解决方案
预防死锁

破解请求和保持:一次给所有资源,这样就不可能存在阻塞进程还拥有资源

但是好浪费啊,可能一些资源是进程最后才需要的

破解环路等待:资源按序申请,这样最前面的进程可以一路畅通。

如果加入新设备,容易翻车

避免死锁

著名的银行家算法:每次进程申请资源时,判断分给它资源后,有没有一种顺序保证所有进程的顺利执行。

进程的通信

  • 信号量:用于进程互斥与同步那里,算是低级通信
  • 三个高级通信:
    • 共享存储器:有点像线程共用进程同一个数据区域,不过线程那里是一开始就指定好的。两个进程要在同一台物理机上,读什么写什么很宽泛。
    • 消息传递:一般是利用操作系统原语实现
      1. 直接传递:直接发给对应进程,挂到其消息缓冲队列上
        发送伪代码
        ![](https://img-blog.csdnimg.cn/img_convert/babb54c25b126827d501b84669516ded.png#align=left&display=inline&height=274&margin=[object Object]&originHeight=274&originWidth=553&size=0&status=done&style=none&width=553)
        接收伪代码
        ![](https://img-blog.csdnimg.cn/img_convert/33a7b6a078c5d1e30b27416f350961ce.png#align=left&display=inline&height=229&margin=[object Object]&originHeight=229&originWidth=553&size=0&status=done&style=none&width=553)
      2. 间接传递:发到一个中间件上,接收进程再从中间件中取

这两种方式有什么区别呢?

直接传递,需要知道对应进程的关键词或进程ID;间接传递,不用管,每次都是发到一个中间件,工程学上应该叫解耦。间接传递应该能实现多对多通信。

  • 管道:两个进程建立连接,数据从一端输送到另一端。实际就是利用系统调用,在存储介质中划出一个空间,一个进程可以读,一个进程可以写。

![](https://img-blog.csdnimg.cn/img_convert/b0166c9104fd0e26ea1216f8c04e01d9.png#align=left&display=inline&height=199&margin=[object Object]&originHeight=199&originWidth=1041&size=0&status=done&style=none&width=1041)
Windows的剪切板也是一种通信,哈哈哈
套接字也是,客户端和服务端通信太常见了!!!

存储管理

干的事情有:

  • 地址转换

每个进程在访问数据时,只考虑逻辑地址。后面操作系统再帮忙转为物理地址。

  • 内存保护

这样也可以防止用户恶意访问操作系统的内存空间。

  • 内存分配

进程通过系统调用获得分配的内存

如何将一个用户源程序变为一个可在内存中执行的程序

首先是编译,由编译程序将用户源代码编译成若干个目标模块;其次是连接,由连接程序将编译后形成的目标模块以及它们所需要的库函数连接在一起,形成一个装入模块;最后是装入,由装入程序将装入模块装入内存。

减少内存碎片,提高内存利用率

内存分配采用分区的形式,容易形成碎片,统一向上挪,又会花费大量时间。

然后分页出现了

这里的页有时又称为块

系统在内存空间设置一片区域作为页表区,系统为每个进程提供一个页表。进程页表的起始地址存放在进程PCB中。

![](https://img-blog.csdnimg.cn/img_convert/8e50456fea45c481d5bd2f42c744a8eb.png#align=left&display=inline&height=401&margin=[object Object]&originHeight=401&originWidth=612&size=0&status=done&style=none&width=612)

当进程被调度时,根据其PCB中页表地址,初始化页表寄存器,然后该进程在执行过程中访问逻辑地址,操作系统根据页表寄存器和逻辑地址,找到页表,最后拼接成正确的物理地址。

后面为了优化,还加了快表,但快表一般只能存较少的表信息

![](https://img-blog.csdnimg.cn/img_convert/e6af033a3628abc6abdc4816211c6cd1.png#align=left&display=inline&height=445&margin=[object Object]&originHeight=445&originWidth=597&size=0&status=done&style=none&width=597)

PS:处处有缓存的影子,这个跟Redis类似,或者说高速缓存区。

快表的访问要比内存的快才行,就是因为页表在内存中,找物理地址时访问了两次内存,这被认为很慢。

页表的问题

![](https://img-blog.csdnimg.cn/img_convert/c4d31274613a702de329878c0c92e5c9.png#align=left&display=inline&height=181&margin=[object Object]&originHeight=181&originWidth=820&size=0&status=done&style=none&width=820)

结果有人就想到用二级页表

![](https://img-blog.csdnimg.cn/img_convert/9cdca480acd36800dfbc774b225b4254.png#align=left&display=inline&height=199&margin=[object Object]&originHeight=199&originWidth=589&size=0&status=done&style=none&width=589)

![](https://img-blog.csdnimg.cn/img_convert/6a503f3ab098f15cd24bbe44ee34a039.png#align=left&display=inline&height=639&margin=[object Object]&originHeight=639&originWidth=1108&size=0&status=done&style=none&width=1108)

好了,解决了页表要连续存放的问题(现在不是变成二级页表连续存放了吗,其实还可以继续三级页表啊,这样二级页表就不用连续存放了,手动滑稽。)

但是,现在访问了三次内存,而且页表的数量还是不减,反而还多了二级页表的空间。解决了缺乏连续空间的问题

那么,真相只有一个

接下来解决内存过大的问题

只把目前需要的页表调入内存,当然,二级页表一定得在,不过它的内存至少已经比页表小了很多。

等等,这莫非就是传说中的页面置换,那想必会有缺页的情况发生。


额,后面又多了段页式存储,这个段存储和之前的二级页表有点像

![](https://img-blog.csdnimg.cn/img_convert/8768a3e49cfdf29874173a7ed5a4508b.png#align=left&display=inline&height=282&margin=[object Object]&originHeight=282&originWidth=601&size=0&status=done&style=none&width=601)

![](https://img-blog.csdnimg.cn/img_convert/0fe1cec98535f3ede2f25641d30fc127.png#align=left&display=inline&height=350&margin=[object Object]&originHeight=350&originWidth=622&size=0&status=done&style=none&width=622)

文件管理

磁带方式

![](https://img-blog.csdnimg.cn/img_convert/2cb009dcfeb74ecedd0eed6730c686e8.png#align=left&display=inline&height=150&margin=[object Object]&originHeight=150&originWidth=613&size=0&status=done&style=none&width=613)

文件系统从磁带的始点开始搜索,首先读出第一个文件头标,比较用户名和文件名,若是用户指定文件,则可读出随后的信息。若不是用户指定的文件,则让磁头前进到下一个文件的文件头标的位置,然后读出文件头标进行比较,直到找到指定的文件。

磁盘方式

文件控制块需要记录该文件第一个盘块的盘块号和文件长度

系统只要找到该顺序文件所在的第一个物理块,从此开始顺序地、逐个物理块地往下读/写。通常相邻块大多数都位于同一柱面上,在进行读/写时不必移动磁头,当访问到一个柱面最后一条磁道的最末一个盘块时,才需要移动磁头到下一个柱面,于是又连续地读/写多个盘块。

但容易造成碎片、有时不知道要分配多大空间

所以还有一种类似页表的方式,

![](https://img-blog.csdnimg.cn/img_convert/903dc0fe226ee90c4477df6ebd26668e.png#align=left&display=inline&height=218&margin=[object Object]&originHeight=218&originWidth=823&size=0&status=done&style=none&width=823)

多人共享文档

原来是这样

![](https://img-blog.csdnimg.cn/img_convert/3aa69ba52906165eceec35119e24c386.png#align=left&display=inline&height=239&margin=[object Object]&originHeight=239&originWidth=614&size=0&status=done&style=none&width=614)

并不是直接编辑文件,而是修改一个编辑目标文件的程序,这样也保证了文件最多只能被一个访问

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值