basis.md 21.2 KB
Newer Older
S
shuang.kou 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
> 更多操作系统相关的面试题问题,见:《》

### 什么是操作系统?

👨‍💻**面试官** : 先来个简单问题吧!**什么是操作系统?**
🙋 **我** :我通过以下四点向您介绍一下什么是操作系统吧!

1. **操作系统(英语:Operating System,简称OS)是管理计算机硬件与软件资源的程序,是计算机系统的内核与基石;**
2. **操作系统本质上是运行在计算机上的软件程序 ;**
3. **为用户提供一个与系统交互的操作界面 ;**
4. **操作系统分内核与外壳(我们可以把外壳理解成围绕着内核的应用程序,而内核就是能操作硬件的程序)。**

![操作系统分内核与外壳](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/a72a930ca61bc2b424bb900c94c063cf.png)

## 进程和线程

###进程和线程的区别

👨‍💻**面试官**: 好的!我明白了!那你再说一下: **进程和线程的区别**

🙋 **我:** 好的! 下图是 Java 内存区域,我们从 JVM 的角度来说一下线程和进程之间的关系吧!

> 如果你对 Java 内存区域 (运行时数据区) 这部分知识不太了解的话可以阅读一下这篇文章:[《可能是把 Java 内存区域讲的最清楚的一篇文章》](<[https://snailclimb.gitee.io/javaguide/#/docs/java/jvm/Java%E5%86%85%E5%AD%98%E5%8C%BA%E5%9F%9F](https://snailclimb.gitee.io/javaguide/#/docs/java/jvm/Java内存区域)>)

![jvm运行时数据区域](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/ff96fed0e2a354bb16bbc84dcedf503a.png)

从上图可以看出:一个进程中可以有多个线程,多个线程共享进程的**堆****方法区 (JDK1.8 之后的元空间)**资源,但是每个线程有自己的**程序计数器****虚拟机栈****本地方法栈**

**总结:** 线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极有可能会相互影响。线程执行开销小,但不利于资源的管理和保护;而进程正相反。

###进程有哪几种状态?

👨‍💻**面试官** : 那你再说说**进程有哪几种状态?**
🙋 **我** :我们一般把进程大致分为 5 种状态,这一点和[线程](https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/Multithread/JavaConcurrencyBasicsCommonInterviewQuestionsSummary.md#6-%E8%AF%B4%E8%AF%B4%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%92%8C%E7%8A%B6%E6%80%81)很像!

- **创建状态(new)** :进程正在被创建,尚未到就绪状态。
- **就绪状态(ready)** :进程已处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
- **运行状态(running)** :进程正在处理器上上运行(单核CPU下任意时刻只有一个进程处于运行状态)。
- **阻塞状态(waiting)** :又称为等待状态,进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
- **结束状态(terminated)** :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。

![process-state](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/d38202593012b457debbcd74994c6292.png)

### 进程间的通信方式

👨‍💻**面试官****进程间的通信常见的的有哪几种方式呢?**
🙋 **我** :大概有 7 种常见的进程间的通信方式。

> 下面这部分总结参考了:[《进程间通信IPC (InterProcess Communication)》](https://www.jianshu.com/p/c1015f5ffa74) 这篇文章,推荐阅读,总结的非常不错。

1. **管道/匿名管道(Pipes)** :用于具有亲缘关系的父子进程间或者兄弟进程之间的通信。
1. **有名管道(Names Pipes)** : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循**先进先出(first in first out)**。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
1. **信号(Signal)** :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;
1. **消息队列(Message Queuing)** :消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显示地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。**消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺。**
1. **信号量(Semaphores)** :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
1. **共享内存(Shared memory)** :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
1. **套接字(Sockets)**  : 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持TCP/IP的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。

### 线程间的同步的方式

👨‍💻**面试官** :那线程间的同步的方式有哪些呢?
🙋 **我** :线程同步是两个或多个共享关键资源的线程的并发执行。应该同步线程以避免关键的资源使用冲突。操作系统一般有下面三种线程同步的方式:

1. **互斥量(Mutex)**:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
1. **信号量(Semphares)** :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
1. **事件(Event)** :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操

### 进程的调度算法

👨‍💻**面试官****你知道操作系统中进程的调度算法有哪些吗?**
🙋 **我** :嗯嗯!这个我们大学的时候学过,是一个很重要的知识点!

为了确定首先执行哪个进程以及最后执行哪个进程以实现最大CPU利用率,计算机科学家已经定义了一些算法,它们是:

- **先到先服务(FCFS)调度算法** : 从就绪队列中选择一个最先进入该队列的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用CPU时再重新调度。
- **短作业优先(SJF)的调度算法** : 从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用CPU时再重新调度。
- **时间片轮转调度算法** : 时间片轮转调度是一种最古老,最简单,最公平且使用最广的算法,又称RR(Round robin)调度。每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
- **多级反馈队列调度算法** :前面介绍的几种进程调度的算法都有一定的局限性。如**短进程优先的调度算法,仅照顾了短进程而忽略了长进程** 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成。,因而它是目前**被公认的一种较好的进程调度算法**,UNIX操作系统采取的便是这种调度算法。
- **优先级调度** : 为每个流程分配优先级,首先执行具有最高优先级的进程,依此类推。具有相同优先级的进程以FCFS方式执行。可以根据内存要求,时间要求或任何其他资源要求来确定优先级。

## 操作系统内存管理基础

### 内存管理介绍

👨‍💻 **面试官**: **操作系统的内存管理主要是做什么?**

🙋 **我:** 操作系统的内存管理主要负责内存的分配与回收(malloc 函数:申请内存,free 函数:释放内存),另外地址转换也就是将逻辑地址转换成相应的物理地址等功能也是操作系统内存管理做的事情。

### 常见的几种内存管理机制

👨‍💻 **面试官**: **操作系统的内存管理机制了解吗?内存管理有哪几种方式?**

🙋 **我:** 这个在学习操作系统的时候有了解过。我了解的有下面 3 种:

1. **块式管理** : 远古时代的计算机操系统的内存管理方式。将内存分为几个固定大小的块,每个块中只包含一个进程。如果程序运行需要内存的话,操作系统就分配给它一块,如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间,我们称之为碎片。
2. **页式管理** :把主存分为大小相等且固定的一页一页的形式,页较小,相对相比于块式管理的划分力度更大,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。
3. **段式管理** : 页式管理虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义。 段式管理把主存分为一段段的,每一段的空间又要比一页的空间小很多 。但是,最重要的是段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址。

👨‍💻**面试官** : 回答的还不错!不过漏掉了一个很重要的 **段页式管理机制** 。段页式管理机制结合了段式管理和页式管理的优点。简单来说段页式管理机制就是把主存先分成若干段,每个段又分成若干页,也就是说 **段页式管理机制** 段页式管理中段与段之间以及段的内部都是离散的。在段页管理机制中,程序的地址空间首先被分成若干个逻辑段,每段都有自己的段号,然后再将每一段分成若干个大小固定的页。对内存空间的管理仍然和分页存储管理一样,将其分成若干个和页面大小相同的存储块,对内存的分配以存储块为单位。

🙋 **我** :谢谢面试官!刚刚把这个给忘记了~

<img src="http://ww4.sinaimg.cn/large/6af89bc8gw1f8txoxc2asj20k00k0mxv.jpg" alt="这就很尴尬了_尴尬表情" height="200px"/>

### 分页机制和分段机制的共同点和区别

👨‍💻**面试官****分页机制和分段机制有哪些共同点和区别呢?**
🙋 **我**

1. **共同点**
   - 分页机制和分段机制都是为了提高内存利用率,较少内存碎片。
   - 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
2. **区别**
   - 页的大小是固定的,由操作系统决定;而段的大小不固定,取决于我们当前运行的程序。
   - 分页仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要。

### 逻辑(虚拟)地址和物理地址

👨‍💻**面试官** :你刚刚还提到了**逻辑地址和物理地址**这两个概念,我不太清楚,你能为我解释一下不?

🙋 **我:** em...好的嘛!我们编程一般只有可能和逻辑地址打交道,比如在 C 语言中,指针里面存储的数值就可以理解成为内存里的一个地址,这个地址也就是我们说的逻辑地址,逻辑地址由操作系统决定。物理地址指的是真实物理内存中地址,更具体一点来说就是内存地址寄存器中的地址。物理地址是内存单元真正的地址。

👨‍💻**面试官****CPU寻址了解吗?为什么需要虚拟地址空间?**
🙋 **我** :这部分我真不清楚!

<img src="http://wx2.sinaimg.cn/bmiddle/a9cf8ef6ly1fhqpdipcyfj20ce0b4wex.jpg "  height="300px" />

于是面试完之后我默默去查阅了相关文档!留下了没有技术的泪水。。。

> 这部分内容参考了Microsoft官网的介绍,地址:<https://msdn.microsoft.com/zh-cn/library/windows/hardware/hh439648(v=vs.85).aspx>

现代处理器使用的是一种称为 **虚拟寻址(Virtual Addressing)** 的寻址方式。**使用虚拟寻址,CPU需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。** 实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为 **内存管理单元(Memory Management Unit, MMU)**的硬件。MMU 需要借助存放在内存中的页表来动态翻译虚拟地址,该页表由操作系统管理。

如果虚拟地址,直接把物理地址暴露出来的话会带来严重问题,比如可能对操作系统造成伤害以及给同时运行多个程序造成困难。

通过虚拟地址访问内存有以下优势:

- 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区。
- 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动。
- 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存。

![MMU_principle_updated](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2019-11/2b27dac8cc647f8aac989da2d1166db2.png)

## 虚拟内存

###什么是虚拟内存(Virtual Memory)?

👨‍💻**面试官** :再问你一个常识性的问题!**什么是虚拟内存(Virtual Memory)?**
🙋 **我** :这个在我们平时使用电脑特别是 Windows 系统的时候太常见了。很多时候我们使用点开了很多占内存的软件,这些软件占用的内存可能已经远远超出了我们电脑本身具有的物理内存。**为什么可以这样呢?** 正是因为 **虚拟内存** 的存在,通过 **虚拟内存** 可以让程序可以拥有超过系统物理内存大小的可用内存空间。另外,**虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)**。这样会更加有效地管理内存并减少出错。

 **虚拟内存**是计算机系统内存管理的一种技术,我们可以手动设置自己电脑的虚拟内存。不要单纯认为虚拟内存只是“使用硬盘空间来扩展内存“的技术。**虚拟内存的重要意义是它定义了一个连续的虚拟地址空间**,并且 **把内存扩展到硬盘空间**。推荐阅读:[《虚拟内存的那点事儿》](https://juejin.im/post/59f8691b51882534af254317)

维基百科中有几句话是这样介绍虚拟内存的。

> **虚拟内存** 使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。与没有使用虚拟内存技术的系统相比,使用这种技术的系统使得大型程序的编写变得更容易,对真正的物理内存(例如RAM)的使用也更有效率。目前,大多数操作系统都使用了虚拟内存,如Windows家族的“虚拟内存”;Linux的“交换空间”等。From:<https://zh.wikipedia.org/wiki/虚拟内存>

### 局部性原理

👨‍💻**面试官** :要想更好地理解虚拟内存技术,必须要知道计算机中著名的局部性原理。另外,局部性原理既适用于程序结构,也适用于数据结构,是非常重要的一个概念。
🙋 **我** :局部性原理是虚拟内存技术的基础,正是因为程序运行具有局部性原理,才可以只装入部分程序到内存就开始运行。

> 以下内容摘自《计算机操作系统教程》 第4章存储器管理。

早在1968年的时候,就有人指出我们的程序在执行的时候往往呈现局部性规律,也就是说在某个较短的时间段内,程序执行局限于某一小部分,程序访问的存储空间也局限于某个区域。

局部性原理表现在以下两个方面: 

1. **时间局部性** :如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。 
2. **空间局部性** :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。 

时间局部性是通过将近来使用的指令和数据保存到高速缓存存储器中,并使用高速缓存的层次结构实现。空间局部性通常是使用较大的高速缓存,并将预取机制集成到高速缓存控制逻辑中实现。虚拟内存技术实际上就是建立了 “内存一外存”的两级存储器的结构,利用局部性原理实现髙速缓存。 

### 虚拟存储器

👨‍💻**面试官** :虚拟存储了解么?
🙋 **我**

> 这部分内容来自:[王道考研操作系统知识点整理]( https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html)。

基于局部性原理,在程序装入时,可以将程序的一部分装入内存,而将其他部分留在外存,就可以启动程序执行。在程序执行过程中,当所访问的信息不在内存时,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不使用的内容换到外存上,从而腾出空间存放将要调入内存的信息。这样,计算机好像为用户提供了一个比实际内存大的多的存储器,成为 **虚拟存储器**

### 页面置换算法

👨‍💻**面试官****页面置换算法的作用?常见的页面置换算法有哪些?**
🙋 **我**

> 这个题目经常作为笔试题出现,网上已经给出了很不错的回答,我这里只是总结整理了一下。

地址映射过程中,若在页面中发现所要访问的页面不在内存中,则产生缺页中断 。当发生缺页中断时,如果操作系统内存中没有空闲页面,则操作系统 必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。而用来选择淘汰哪一页的规则叫做页面置换算法,页面置换算法的作用是 **实现虚拟存储管理。**  

- **OPT页面置换算法(最佳页面置换算法)** :理想情况,不可能实现,一般作为衡量其他置换算法的方法。
- **FIFO页面置换算法(先进先出页面置换算法)**  : 总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。
- **LRU页面置换算法(最近未使用页面置换算法)** :LRU(Least Currently Used)算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间T,当须淘汰一个页面时,选择现有页面中其T值最大的,即最近最久未使用的页面予以淘汰。
- **LFU页面置换算法(最少使用页面排序算法)** : LFU(Least Frequently Used)算法会让系统维护一个按最近一次访问时间排序的页面链表,链表首节点是最近刚刚使用过的页面,链表尾节点是最久未使用的页面。访问内存时,找到相应页面,并把它移到链表之首。缺页时,置换链表尾节点的页面。也就是说内存内使用越频繁的页面,被保留的时间也相对越长。

### Reference


- 《计算机操作系统—汤小丹》第四版
- [《深入理解计算机系统》](https://book.douban.com/subject/1230413/)
- [https://zh.wikipedia.org/wiki/输入输出内存管理单元](https://zh.wikipedia.org/wiki/输入输出内存管理单元)
- <https://www.studytonight.com/operating-system>
- <https://www.geeksforgeeks.org/interprocess-communication-methods/>
- <https://juejin.im/post/59f8691b51882534af254317>
- 王道考研操作系统知识点整理: https://wizardforcel.gitbooks.io/wangdaokaoyan-os/content/13.html