🥫 操作系统 面试指南
本文首发于个人的Wiki库,欢迎前来交流学习 👉 CS-Wiki
一、操作系统概述
1. 什么是操作系统
- 操作系统(Operating System,简称 OS)是管理计算机硬件与软件资源的程序,是计算机系统的内核与基石;
- 操作系统本质上是运行在计算机上的软件程序 ;
- 操作系统为用户提供一个与系统交互的操作界面 ;
- 操作系统分内核与外壳(我们可以把外壳理解成围绕着内核的应用程序,而内核就是能操作硬件的程序)。
关于内核多插一嘴:内核负责管理系统的进程、内存、设备驱动程序、文件和网络系统等等,决定着系统的性能和稳定性。是连接应用程序和硬件的桥梁。 内核就是操作系统背后黑盒的核心。
![image.png](https://i-blog.csdnimg.cn/blog_migrate/870f66349d8ddf856da06b678d108563.png)
2. 什么是微内核
微内核(英文中常译作µ-kernel或者micro kernel)。是一种能够提供必要服务的操作系统内核;其中这些必要的服务包括任务,线程,进程通信(IPC,Inter-Process Communication)以及内存管理等等。所有服务(包括设备驱动)在用户模式下运行,而处理这些服务同处理其他的任何一个程序一样。因为每个服务只是在自己的地址空间运行。所以这些服务之间彼此之间都受到了保护。
微内核只提供一组“最基本”的服务,如进程调度、进程间通信、存储管理、处理I/O设备。其他服务,如文件管理、网络支持等通过接口连到微内核。与此相反,内核是集成的,比微内核更大。
![image.png](https://i-blog.csdnimg.cn/blog_migrate/a5349a77238e4388d853e882d71b311b.png)
微内核的目标是将系统服务的实现和系统的基本操作规则分离开来。
例如,进程的输入/输出锁定服务可以由运行在微核之外的一个服务组件来提供。这些非常模块化的用户态服务器用于完成操作系统中比较高级的操作,这样的设计使内核中最内核的部分的设计更简单。
一个服务组件的失效并不会导致整个系统的崩溃,内核需要做的,仅仅是重新启动这个组件,而不必影响其它的部分。
微内核将许多OS服务放入分离的进程,如文件系统,设备驱动程序,而进程通过消息传递调用OS服务.
华为发布的鸿蒙操作系统就是使用的微内核
3. 并发和并行的区别
-
并发
:并发是指宏观上在一段时间内能同时运行多个程序。这些程序宏观上是同时发生的,但微观上是交替发生的。操作系统通过引入进程和线程,使得程序能够并发运行。单核CPU同一时刻只能执行一个程序,各个程序只能并发地执行 ;
多核CPU同一时刻可以同时执行多个程序,多个程序可以并行地执行。比如Intel的第八代i3处理器就是
4核CPU
,意味着可以并行地执行4个程序
即使是对于4核CPU来说,只要有4个以上的程序需要“同时”运行,那么并发性依然是必不可少的,因此 并发性 是操作系统一个基本的特性 -
并行
:并行则指同一时刻能运行多个指令,指两个或多个事件在同一时刻同时发生。并行需要硬件支持,如多流水线、多核处理器或者分布式计算系统。
4. 硬中断和软中断是什么?区别是什么
软中断:
- 编程异常通常叫做软中断(比如
int i = 1/0
) - 软中断是通讯进程之间用来模拟硬中断的一种信号通讯方式。
- 中断源发中断请求或软中断信号后,CPU 或接收进程在适当的时机自动进行中断处理或完成软中断信号 对应的功能
- 软中断是软件实现的中断,也就是程序运行时其他程序对它的中断;而硬中断是硬件实现的中断,是程序运 行时设备对它的中断。
硬中断:
-
**硬中断是由外部事件引起的,**因此具有随机性和突发性;
软中断是执行中断指令产生的,无外部施加中断请求信号,因此中断的发生不是随机的而是由程序安排好的。
-
硬中断的中断响应周期,CPU 需要发中断回合信号(NMI 不需要),软中断的中断响应周期,CPU 不 需发中断回合信号。
-
硬中断的中断号是由中断控制器提供的(NMI 硬中断中断号系统指定为02H);软中断的中断号由指令直接给出,无需使用中断控制器。
-
硬中断是可屏蔽的(NMI 硬中断不可屏蔽),软中断不可屏蔽。
区别:
- 软中断发生的时间是由程序控制的,而硬中断发生的时间是随机的
- 软中断是由程序调用发生的,而硬中断是由外设引发的
- 硬件中断处理程序要确保它能快速地完成它的任务,这样程序执行时才不会等待较长时间。
5. 操作系统中使用了哪些数据结构
-
栈:
-
比如内存管理中页面置换算法的先进先出置换算法
-
磁盘调度算法中先来先服务算法FCFS
-
-
队列:
-
比如内存管理中页面置换算法的时钟算法CLock
-
磁盘调度算法中扫描算法
-
-
链表:
-
进程管理中进程控制块的链接
-
进程通信IPC中的消息队列方法(消息的链表)
-
-
树:
- 进程管理中进程家族关系描述:进程树
-
散列表:
-
内存管理中对于内存的分配与回收,连续分配管理方式使用的是Hash算法
-
文件管理:Hash文件
-
6. 什么是系统调用,简述系统调用的过程
操作系统作为用户和计算机硬件之间的接口,需要向上提供一些简单易用的服务。主要包括命令接口和程序接口。其中,程序接口由一组系统调用组成。
“系统调用”是操作系统提供给应用程序(程序员/编程人员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序可以通过系统调用来请求获得操作系统内核的服务
如果一个进程在用户态需要使用内核态的功能,就进行系统调用从而陷入内核,由操作系统代为完成。
系统调用的过程如下:
传递系统调用参数 ——> 执行陷入指令(用户态),引发一个内中断(保护被中断进程的CPU环境),使CPU进入内核态 ——> 执行相应的请求,内核程序处理系统调用(内核态)——> 返回应用程序(恢复被中断进程的CPU环境)
二、进程、线程、死锁
1. 进程和程序的区别
-
① 进程是动态的,程序是静止的。进程是程序的执行,程序是有序代码的集合。
-
② 进程是暂时的,程序是永久的。进程是一个状态变化的过程,程序可以长久保存。
-
③ 进程和程序的组成不同:进程包括程序,数据和进程控制块。
-
④ 进程和程序是密切相关的。通过多次执行,一个程序可以对应多个进程;通过调度关系,一个进程可以包括多个程序。
-
⑤ 进程可以创建其他进程,但是程序不能形成新的程序
2. 进程和线程的区别
- ① 调度:
线程是独立调度的基本单位,进程是资源分配的基本单位
。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,将会引起进程切换。 - ② 拥有资源:进程是拥有资源的基本单位,而线程不拥有系统资源(除了少量资源,比如栈,程序计数 器,寄存器),不过线程可以访问其隶属进程的系统资源。
- ③ 并发性:在引入线程的操作系统中,不仅进程之间可以并发执行,而且同一个进程内的多个线程之间 也可以并发执行,能提高系统的吞吐量,系统的并发性也更好。
- ④ 系统开销:在创建进程和撤销进程时,系统都要为之分配或回收资源,所以操作系统为进程付出的系统开销远大于创建线程或撤销线程的开销。
- ⑤ 同步和通信:多线程之间的同步和通信容易实现。
3. 进程和作业的区别
一个进程是一个程序对某个数据集的执行过程,是资源分配的基本单位。
作业是用户需要计算机完成的某项任务,是要求计算机所做工作的集合。一个作业的完成要经过作业提交、作业收容、作业执行和作业完 成4 个阶段。
其主要区别如下。
- 作业是用户向计算机提交任务的任务实体。在用户向计算机提交作业后,系统将它放入外存中的作业等 待队列中等待执行。而进程则是完成用户任务的执行实体,是向系统申请分配资源的基本单位。任一进程, 只要它被创建,总有相应的部分存在于内存中。
- 一个作业可由多个进程组成,且必须至少由一个进程组成,反过来则不成立。
- 作业的概念主要用在批处理系统中,像UNIX 这样的分时系统中就没有作业的概念。而进程的概念则 用在几乎所有的多道程序系统中进程是操作系统进行资源分配的单位。在Windows 下,进程又被细化为线程,也就是一个进程下有多个能独立运行的更小的单位。
4. 进程树/线程树
进程树是一个形象化的比喻,比如一个进程启动了一个程序,而启动的这个进程就是原来那个进程的子进 程,依此形成的一种树形的结构,我们可以在进程管理器选择结束进程树,就可以结束其子进程和派生的 子进程。
5. 进程的状态以及转换过程
三态模型:
- 运行态
- 就绪态
- 阻塞态
![image.png](https://i-blog.csdnimg.cn/blog_migrate/e8f164d55e1f694076d4aea6d03a9a3a.png)
五态模型:
- 创建态
- 就绪态
- 运行态
- 阻塞态
- 结束态
![img](https://i-blog.csdnimg.cn/blog_migrate/a8845981e17ed8e82b1d264ed23f9299.png)
进程状态切换过程:
- 进程被创建后进入就绪态,此时尚未获得CPU资源
- 通过进程调度,进程获得CPU资源,进入CPU执行,转入运行态
- 若运行过程中该进程时间片用完,则失去CPU资源,转入就绪态
- 若运行过程中该进程发生等待事件(比如缺少可用资源(不包括CPU资源,缺少CPU资源会转入就绪态)或者等待I/O操作完成),转入阻塞态
- 若阻塞态的进程已经获得全部所需资源,则转入就绪态,等待进程调度获得CPU资源转入运行态
6. 进程调度算法
-
先来先服务 FCFS
非抢占式按照到达的先后顺序调度,事实上就是等待时间越久的越优先得到服务。
-
短作业优先 SJF
非抢占式每次调度时选择当前 已到达 且 运行时间最短 的作业/进程。
-
最短剩余时间优先
SRTN 抢占式当一个新的作业到达时,其整个运行时间与当前进程的剩余时间作比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
-
高响应比优先 HRRN
非抢占式只有当前运行的进程主动放弃CPU时(正常/异常完成,或主动阻塞),才需要进行调度,调度时计算所有就绪进程的响应比,选响应比最高的 进程上处理机。
响应比 = (等待时间+要求服务时间) / 要求服务时间
7. 进程间的通信方式
大概有 7 种常见的进程间的通信方式。
-
管道/匿名管道(Pipes) :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。只存在于内存中的文件。
-
有名管道(Names Pipes) : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out)。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
-
信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
-
消息队列(Message Queuing) :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。
管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:
存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)
或者显示地删除一个消息队列时,该消息队列才会被真正的删除。
消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更
有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺。
-
信号量(Semaphores) :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
-
共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
-
套接字(Sockets) : 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
8. 操作系统中的信号量
信号量是一个确定的二元组(s,q),其中s是一个具有非负初值的整型变量,q是一个初始状态为空的队列。整型变量 s 表示系统中某类资源的数目,当其值大于 0 时,表示系统中当前可用资源的数目;当其值 小于0时,其绝对值表示系统中因请求该类资源而被阻塞的进程数目。
信号量分类:
- ① 整型信号量:所谓整型信号量就是一个用于表示资源个数的整型量
- ② 记录型信号量(资源信号量):就是用一个结构体实现,里面包含了表示资源个数的整型量和一个等待队列。
信号量的应用:
- ① 实现进程同步
- ② 实现进程互斥
9. PV操作
信号量的值除了初值外,仅能由这PV原语加以改变。
P、V 操作以原语形式实现,保证了对信号量进行操作过程中不会被打断或阻塞。
P 操作申请资源,V 操作释放资源。
P操作和V操作必定成对 出现,但未必在同一个进程中。
10. 死锁相关
▲ 什么是死锁:
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
比如有A和B两个进程,A需要请求B的资源,B需要请求A的资源,但是A和B都对自己拥有的资源不放手,就导致循环等待,即产生死锁
▲ 死锁产生的必要条件:
-
互斥
:即只有对必须互斥使用的资源的争抢才会导致死锁。 -
请求和保持
:进程已经拥有资源,但又提出了新的资源请求,而该资源又被其他进程所占有,于是该进程进入阻塞态,但还是对自己拥有的资源保持不放,贪得无厌。 -
不可抢占/不剥夺
:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。 -
循环等待
:存在一种进程资源的循环等待环路,该环路中的每个进程都在等待下一个进程所占有的资源。
▲ 死锁的处理策略:
死锁的产生必须满足四 个必要条件,只要其中一个或者几 个条件不满足,死锁就不会发生。
-
鸵鸟策略
:忽略死锁因为解决死锁问题的代价很高,因此鸵鸟策略这种不采取任务措施的方案会获得更高的性能。
当发生死锁时不会对用户造成多大影响,或发生死锁的概率很低,可以采用鸵鸟策略。
大多数操作系统,包括 Unix,Linux 和 Windows,处理死锁问题的办法仅仅是忽略它。
-
死锁预防
::这是一种较简单和直观的事先预防的方法。方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或者几个,来预防发生死锁(比如破坏请求和保持条件,一次性给足进程所需的全部资源,不允许进程在运行过程中请求资源)。预防死锁是一种较易实现的方法,已被广泛使用。但是由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。
-
死锁避免
:该方法同样是属于事先预防的策略,但它并不须事先采取各种限制措施去破坏产生死锁的 的四个必要条件,而是在资源的动态分配过程中,用某种方法去防止系统进入不安全状态,从而避免 发生死锁。比如银行家算法(主要思想是避免系统进入不安全状态,在每次进行资源分配时,它首先检查系统是否有足够的资源满足要求,如果有,则先进行试分配,并对分配后的新状态进行安全性检查。如果新状态安全,则正式分配上述资源,否则拒绝分配上述资源。这样就保证系统始终处于安全状态,从而避免死锁现象的发生)
-
死锁检测和解除
:如果系统中既不采取预防死锁的措施,也不采取避免死锁的措施。在这种情况下,系统应当提供两个算法:- ①死锁检测算法:用于检测系统状态,以确定系统中是否发生了死锁。
- ②死锁解除算法:当认定系统中已经发生了死锁,利用该算法可将系统从死锁状态中解脱出来。常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行。
11. 什么是饥饿,与死锁有啥区别
-
死锁(deadlock)
指的是两个或者两个以上的进程相互竞争系统资源,导致循环等待,致使进程永久阻塞。
例如: -
桌子上有慢慢一桌子的美食,但是只有一双筷子。
-
甲拿了一根,然后在找另一根。
-
乙拿了一根,然后也在自找另一根。
-
因为他们都掌握了对方必需的资源,导致最后他们俩谁都吃不到美食。
什么是活锁:
活锁和死锁很相似。 只是活锁的状态可以发生改变。不过虽然状态可以改变,却没有实质的进展。
比如两个人在一个很宅的胡同里。 一次只能并排过两个人。 两人比较礼貌,都要给对方让路。 结果一起要么让到左边,要么让到右边,结果仍然是谁也过不去。 类似于原地踏步或者震荡状态。
活锁一般是由于对死锁的不正确处理引起的。由于处于死锁中的多个线程同时采取了行动。 而避免的方法也是只让一个线程释放资源。
-
饥饿(starvation)
指的是等待时间长到已经影响到进程运行,此时成为饥饿现象。如果等待时间过长,导致进程使命已经没有意义时,称之为饿死
(一个线程在无限地等待另外两个或多个线程相互传递使用并且永远不会释放的资源)。但是有人饿死并不代表着出现了死锁。
例如:- 小明要告诉妈妈明天开家长会。
- 小明妈妈因为工作太忙,在公司加班,没有回家。
- 于是第二天,小问明的妈妈就错过了家长会。(“饿死”)
- 其实小明的妈妈没有出现“死锁”。只是小明的优先级过低,不如工作重要 ,所以就没有把自己拥有的时间资源分配给他。
三、内存管理
1. 操作系统的内存管理主要是做什么
操作系统作为系统资源的管理者,当然也需要对内存进行管理,要管些什么呢?主要包括以下四个方面:
- 内存空间的分配与回收
- 内存空间的扩充(虚拟存储技术)
- 地址转换(逻辑地址和物理地址的转化)
- 存储保护(保证各进程在各自存储空间内运行,互不干扰)
2. 内存管理有哪几种方式/常见的内存管理机制
分为连续分配管理方式和非连续分配管理方式这两种。
**连续分配管理方式:**是指为一个用户程序分配一个连续的内存空间。
单一连续分配
:内存中只能有一道用户程序,用户程序独占整个用户区空间。有内部碎片(如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间,我们称之为碎片),存储利用率低下。固定分区分配
:将整个用户空间划分为若干个固定大小的分区,在 每个分区中只装入一道作业动态分区分配
:这种分配方式不会预先划分内存分区,而是在进程装入内存时, 根据进程的大小动态地建立分区,并使分区的大小正好适合进程的需要。因此系统分区的大小和数目是可变的。
**非连续分配管理方式:**允许一个程序使用的内存分布在离散或者说不相邻的内存中。
-
页式管理
:把主存分为大小相等且固定的一页一页的形式,页较小,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。 -
段式管理
:页式管理虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义。段式管理把主存分为一段段的,每一段的空间又要比一页的空间小很多 。但是,最重要的是段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。
段式管理通过段表对应逻辑地址和物理地址。
-
段页式管理
:段页式管理机制结合了段式管理和页式管理的优点。简单来说段页式管理机制就是把主存先分成若干段,每个段又分成若干页.
也就是说 段页式管理机制 中段与段之间以及段的内部的都是离散的。
3. 连续分区分配和非连续分配的比较
连续分配:指为用户进程分配的必须是一个连续的内存空间。
非连续分配:为用户进程分配的可以是一些分散的内存空间。
4. 什么是内部碎片和外部碎片
**内部碎片:**分配给进程的存储空间中未被利用的部分
外部碎片:系统中无法利用的小的存储块,比如通过动态内存分配技术从空闲内存区上分配内存后剩下的那部分内存块
5. 什么是拼接技术?
在分区管理方式下,系统运行一段时间后,内存中会出现相当一部分的碎片,拼接技术是解决碎片问题的方法。 即将存储器中所有已分配分区移动到主存的一端,使本来分散的多个小空闲区连成一个大的空闲区,这种通过移动把多个分散的小分区拼接成一个大分区的方法即为拼接技术。
6. 什么是原子操作?
如果这个操作所处的层(layer)的更高层不能发现其内部实现与结构,那么这个操作是一个原子(atomic)操作。
原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割只执行其中的一部分。
将整个操作视作一个整体是原子性的核心特征。
7. 多级页表和快表
页表管理机制中有两个很重要的概念:快表和多级页表,这两个东西分别解决了页表管理中很重要的两个问题。
在分页内存管理中,很重要的两点是:
- 虚拟地址到物理地址的转换要快。(快表解决)
- 解决虚拟地址空间大,页表也会很大的问题。(多级页表解决)
页表:
进程的每个页面在内存中存放的位置,操作系统要为每个进程建立一张页表。
注:页表通常存在PCB(进程控制块)中
- 一个进程对应一张页表
- 进程的每个页面对应一个页表项
- 每个页表项由 页号 和 块号 组成
- 页表记录进程页面和实际存放的内存块之间的映射关系
- 每个页表项的长度是相同的
![image.png](https://i-blog.csdnimg.cn/blog_migrate/3db379fa4644005b3f58a22cc7b3cd4b.png)
即页表中的页号对应进程的逻辑地址,页表中的块号对应内存中的实例物理地址
多级页表
:引入多级页表的主要目的是为了避免把全部页表一直放在内存中占用过多空间,特别是那些根本就不需要的页表就不需要保留在内存中。多级页表属于时间换空间的典型场景。
快表 TLB:
为了解决虚拟地址到物理地址的转换速度,操作系统在 页表方案 基础之上引入了 快表TLB 来加速虚拟地址到物理地址的转换。
快表,又称联想寄存器(TLB,translation lookaside buffer ),是一种访问速度比内存快很多的 高速缓存(TLB不是内存!),用来存放最近访问的页表项的副本,可以加速地址变换的速度。 与此对应,内存中的页表常称为慢表。
若快表命中,就不需要再访问内存了,若快表中没有目标页表项,则需要查询内存中的页表
使用快表之后的地址转换流程是这样的:
- 根据虚拟地址中的页号查快表;
- 如果该页在快表中,直接从快表中读取相应的物理地址;
- 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
- 当快表填满后,又要登记新页时,就按照页面置换算法淘汰掉快表中的一个页。
**总结:**为了提高内存的空间性能,提出了多级页表的概念;但是提到空间性能是以浪费时间性能为基础的,因此为了补充损失的时间性能,提出了快表(即 TLB)的概念。 不论是快表还是多级页表实际上都利用到了程序的局部性原理
9. 分页机制和分段机制的共同点和区别
共同点 :
- 分页机制和分段机制都是为了提高内存利用率,减少内存碎片。
- 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
区别 :
-
页的大小是固定的,由操作系统决定;
而段的大小不固定,取决于我们当前运行的程序。
-
分页是信息的物理单位,仅仅是为了满足操作系统内存管理的需求,主要目的是为了实现离散分配,提高内存利用率;实现虚拟内存,从而获得更大的地址空间。对用户是不可见的
而段是信息的逻辑单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要,对用户可见,用户编程时需要显示的给出段名。
-
分页的用户进程地址空间是一维的,程序员只需给出一个记忆符即可表示一个地址。
分段的用户进程地址空间是二维的,程序员在标识一个地址时,既要给出段名,也要给出段内地址
10. 逻辑地址和物理地址
逻辑地址是相对地址,物理地址是绝对地址。
我们编程一般只有可能和逻辑地址打交道,比如在 C 语言中,指针里面存储的数值就可以理解成为内存里的一个地址,这个地址也就是我们说的逻辑地址,逻辑地址由操作系统决定。物理地址指的是真实物理内存中地址,更具体一点来说就是内存地址寄存器中的地址。物理地址是内存单元真正的地址。
11. CPU 寻址了解吗?为什么需要虚拟地址空间?
CPU寻址:
现代处理器使用的是一种称为 虚拟寻址(Virtual Addressing) 的寻址方式。使用虚拟寻址,CPU 需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。 实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为 内存管理单元(Memory Management Unit, MMU) 的硬件。如下图所示:
为什么要有虚拟地址空间呢?
没有虚拟地址空间的时候,程序都是直接访问和操作的都是物理内存 。但是这样有什么问题呢?
- 用户程序可以访问任意内存,寻址内存的每个字节,这样就很容易(有意或者无意)破坏操作系统,造成操作系统崩溃。
- 想要同时运行多个程序特别困难,比如你想同时运行一个微信和一个 QQ 音乐都不行。为什么呢?举个简单的例子:微信在运行的时候给内存地址 1xxx 赋值后,QQ 音乐也同样给内存地址 1xxx 赋值,那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值,这就造成了微信这个程序就会崩溃。
总结来说:如果直接把物理地址暴露出来的话会带来严重问题,比如可能对操作系统造成伤害以及给同时运行多个程序造成困难。
通过虚拟地址访问内存有以下优势:
- 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
- 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
- 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。
四、内存管理之虚拟内存
1. 什么是虚拟内存(Virtual Memory)?
我们都知道一个进程是与其他进程共享CPU和内存资源的。正因如此,操作系统需要有一套完善的内存管理机制才能防止进程之间内存泄漏的问题。
为了更加有效地管理内存并减少出错,现代操作系统提供了一种对主存的抽象概念,即是虚拟内存(Virtual Memory)。虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)。 正是因为 虚拟内存 的存在,**通过 虚拟内存 可以让程序可以拥有超过系统物理内存大小的可用内存空间。**目前,大多数操作系统都使用了虚拟内存,如 Windows 家族的“虚拟内存”;Linux 的“交换空间”等
虚拟内存和交换空间,这两个概念要和操作系统一起说,window下的“虚拟内存”实际上是存在磁盘上的页面文件,和linux的交换空间概念一样,都是物理上实际存在的东西,如果内存够用,没有缺页,你禁掉这两个东西不会影响虚拟存储器;然而撇开操作系统,只谈理论,虚拟内存还是一种技术,它允许执行进程不必完全放在内存中。这两种其实都属于交换技术,交换技术除了因为内存紧张要使用之外,还有在一些操作系统如分时系统中为了改善进程组合(CPU密集型和IO密集型)也要用它进行中期调度。
-
电脑中所运行的程序均需经由内存执行,若执行的程序占用内存很大或很多,则会导致内存消耗殆尽。为解决该问题,Windows中运用了虚拟内存技术,即匀出一部分硬盘空间来充当内存使用。
虚拟内存在硬盘上其实就是为一个硕大无比的文件,文件名是 PageFile.Sys,所以虚拟内存也称页面文件。
虽然把它当做内存用,可这块空间毕竟是在硬盘,速度肯定不如真是内存,所以说它是虚的。
-
当内存耗尽时,电脑就会自动调用硬盘来充当内存,以缓解内存的紧张。若计算机运行程序或操作所需的随机存储器(RAM)不足时,则Windows会用虚拟存储器进行补偿。它将计算机的RAM和硬盘上的临时空间组合。
-
当RAM运行速率缓慢时,它便将数据从RAM移动到称为“分页文件”的空间中。将数据移入分页文件可释放RAM,以便完成工作。一般而言,计算机的RAM容量越大,程序运行得越快。
-
若计算机的速率由于RAM可用空间匮乏而减缓,则可尝试通过增加虚拟内存来进行补偿。但是,计算机从RAM读取数据的速率要比从硬盘读取数据的速率快,因而扩增RAM容量(可加内存条)是最佳选择。
2. 局部性原理
早在 1968 年的时候,就有人指出我们的程序在执行的时候往往呈现局部性规律,也就是说在某个较短的时间段内,程序执行局限于某一小部分,程序访问的存储空间也局限于某个区域。
局部性原理表现在以下两个方面:
- 时间局部性 :如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。
- 空间局部性 :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。
3. 什么是虚拟存储器
虚拟存储器是一种机制,并不是一个实际存在的物理设备,是整个CPU访问内存过程的体现。
基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其他部分留在外存,就可以启动程序执行。由于外存往往比内存大很多,所以我们运行的软件的内存大小实际上是可以比计算机系统实际的内存大小大的。
在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换到外存上,从而腾出空间存放将要调入内存的信息。这样,计算机好像为用户提供了一个比实际内存大的多的存储器——虚拟存储器。
4. 如何实现虚拟内存技术
虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。 虚拟内存的实现有以下三种方式:
-
请求分页存储管理 :建立在分页管理之上,**为了支持虚拟存储器功能而增加了请求调页功能和页面置换功能。**请求分页是目前最常用的一种实现虚拟存储器的方法。
请求分页存储管理系统中,在作业开始运行之前,仅装入当前要执行的部分段即可运行。假如在作业运行的过程中发现要访问的页面不在内存,则由处理器通知操作系统按照对应的页面置换算法将相应的页面调入到主存,同时操作系统也可以将暂时不用的页面置换到外存中。
-
请求分段存储管理 :建立在分段存储管理之上,增加了请求调段功能、分段置换功能。请求分段储存管理方式就如同请求分页储存管理方式一样,在作业开始运行之前,仅装入当前要执行的部分段即可运行;在执行过程中,可使用请求调入中断动态装入要访问但又不在内存的程序段;当内存空间已满,而又需要装入新的段时,根据置换功能适当调出某个段,以便腾出空间而装入新的段。
-
请求段页式存储管理
5. 请求分页存储管理 和 (基本)分页存储管理 的区别
请求分页存储管理建立在分页管理之上。他们的根本区别是是否将程序所需的全部地址空间都装入主存,这也是请求分页存储管理可以提供虚拟内存的原因。
请求分页存储管理不要求将作业全部地址空间同时装入主存。**在分页存储管理的基础上增加了请求调页和页面置换的功能,**基于这一点,请求分页存储管理可以提供虚存,而分页存储管理不能提供虚拟内存。
6. 页面置换算法有哪些?
-
最佳置换算法 OPT
被换出的页面将是最长时间内不再被访问的,通常可以保证获得最低的缺页率。
是一种理论上的算法,因为无法知道一个页面多长时间不再被访问。
-
先进先出置换算法 FIFO
选择换出的页面是最先进入的页面。
该算法会将那些经常被访问的页面也被换出,从而使缺页率升高。
-
最近最久未使用置换算法 LRU
虽然无法知道将来要使用的页面情况,但是可以知道过去使用页面的情况。
LRU 将最近最久未使用的页面换出。
-
时钟置换算法 Clock / 最近未用算法 NRU
为每个页面设置一个访问位,当被访问时,访问位置为1。
当发生缺页中断时,NRU 算法只需检查页的访问位,如果是0(未被访问),就将该页换出;
如果是1,就将其置为0,暂不换出,继续检查下一个页面
五、文件管理
1. 磁盘调度算法
-
先来先服务 FCFS
First Come First Served
按照磁盘请求的顺序进行调度。
优点是公平和简单。缺点也很明显,因为未对寻道做任何优化,使平均寻道时间可能较长。
-
最短寻道时间优先 SSTF
Shortest Seek Time First
优先调度与当前磁头所在磁道距离最近的磁道。
虽然平均寻道时间比较低,但是不够公平。如果新到达的磁道请求总是比一个在等待的磁道请求近,那么在等待的磁道请求会一直等待下去,也就是出现饥饿现象。具体来说,两端的磁道请求更容易出现饥饿现象。
-
扫描/电梯算法 SCAN
SSTF算法会产生饥饿的原因在于:磁头有可一直在一个小区域内来回移动。为了防止这个问题,可以规定,只有磁头移动道最外层磁道的时候才能往内移动,移动到最内侧磁道的时候才能往外移动。这就是扫描算法,类似于电梯,电梯总是保持一个方向运行,直到该方向没有请求为止,然后改变运行方向。因此也称电梯算法
-
循环扫描算法 C-SCAN
SCAN算法对于各个位置磁道的响应频率不平均,而C-Scan算法就是为了解决这个问题。规定只有磁头朝某个特定方向移动时才处理磁道访问请求,而返回时直接快速移动到起始端而不处理任何请求
六、设备管理
1. 什么是DMA?什么是中断?两者的区别
**中断:**是指CPU在执行程序的过程中,出现了某些突发事件时CPU必须暂停执行当前的程序,转去处理突发事件,处理完毕后CPU又返回源程序被中断的位置并继续执行。
DMA:是以数据块为单位传输的,在所要求传送的数据块全部传送结束时要求CPU进行中断处理,这样大大减少CPU进行中断处理的次数。
DMA方式不需CPU干预传送操作,不占用CPU任何资源, 整个数据传输操作在一个称为"DMA控制器"的控制下进行的。CPU除了在数据传输开始和结束时做一点处理外,在传输过程中CPU可以进行其他的工作。这样,在大部分时间里,CPU和输入输出都处于并行操作。因此,使整个计算机系统的效率大大提高。
总结:中断和DMA的最大区别就是DMA不需CPU参与,而中断是需要CPU参与的。
七、链接
1. 程序的装入方式有哪些?
应用程序从用户编写的源文件到内内存中执行的进程大致分为三个阶段,经过编译程序将源代码编译为若干个目标模块,在通过链接程序将编译好的目标模块以及所需的库函数链接到一起,形成完整的装入模块,最后通过装入程序将这些装入模块装入内存并执行。(编译,链接,装入)
装入方式:
- ①
绝对装入
:在编译时就知道程序将要驻留在内存的物理地址,编译程序产生含有物理地址的目标代码, 不适合多道程序设计。 - ②
可重定位装入
:根据内存当前情况,将装入模块装入到内存的适当位置,地址变换通常在装入时一次 完成,之后不再改变,也称静态重定位。当操作系统为程序分配一个以某地址为起始地址的连续主存 区域后,重定位时将程序中指令或操作数的逻辑地址加上这个起始地址就得到了物理地址。 - ③
动态运行装入
:允许程序运行时在内存中移动位置,把装入模块装入到内存后的所有地址都是相对地址,在程序执行过程中每当访问到相应指令或数据时,才将要访问的程序或数据的相对地址转换为物 理地址。动态重定位的实现要依靠硬件地址变换机构。
2. 程序的链接方式有哪些?
- ① 静态链接:在程序运行之前,先把各个目标模块及所需库链接为一个完整的可执行程序,以后不再拆开。
- ② 装入时动态链接:将应用程序编译后所得到的一组目标模块在装入内存时采用边装入边链接的链接方式。
- ③ 运行时动态链接:知道程序运行过程中需要一些模块时,才对这些模块进行链接