Skip to content

Commit 3d5dc96

Browse files
committed
update
1 parent 1750ce7 commit 3d5dc96

File tree

8 files changed

+737
-96
lines changed

8 files changed

+737
-96
lines changed

OperatingSystem/1.操作系统简介.md

Lines changed: 139 additions & 19 deletions
Large diffs are not rendered by default.

OperatingSystem/2.进程与线程.md

Lines changed: 217 additions & 20 deletions
Large diffs are not rendered by default.

OperatingSystem/3.内存管理.md

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@
3434

3535
上述几种内存区域中数据段、BSS和堆通常是被连续存储的——内存位置上是连续的,而代码段和栈往往会被独立存放。有趣的是,堆和栈两个区域关系很“暧昧”,他们一个向下“长”(i386体系结构中栈向下、堆向上),一个向上“长”,相对而生。但你不必担心他们会碰头,因为他们之间间隔很大(到底大到多少,你可以从下面的例子程序计算一下),绝少有机会能碰到一起。
3636

37+
## 地址空间
38+
39+
如果要使多个应用程序同时运行在内存中,必须要解决两个问题:`保护``重定位`。第一种解决方式是用`保护密钥标记内存块`,并将执行过程的密钥与提取的每个存储字的密钥进行比较。这种方式只能解决第一种问题(破坏操作系统),但是不能解决多进程在内存中同时运行的问题。
40+
41+
还有一种更好的方式是创造一个存储器抽象:`地址空间(the address space)`。就像进程的概念创建了一种抽象的 CPU 来运行程序,地址空间也创建了一种抽象内存供程序使用。
42+
43+
#### 基址寄存器和变址寄存器
44+
45+
最简单的办法是使用`动态重定位(dynamic relocation)`技术,它就是通过一种简单的方式将每个进程的地址空间映射到物理内存的不同区域。还有一种方式是使用基址寄存器和变址寄存器。
46+
47+
- 基址寄存器:存储数据内存的起始位置
48+
- 变址寄存器:存储应用程序的长度。
49+
50+
每当进程引用内存以获取指令或读取、写入数据时,CPU 都会自动将`基址值`添加到进程生成的地址中,然后再将其发送到内存总线上。同时,它检查程序提供的地址是否大于或等于`变址寄存器` 中的值。如果程序提供的地址要超过变址寄存器的范围,那么会产生错误并中止访问。
51+
3752

3853

3954
## 内存的演变
@@ -90,7 +105,17 @@ Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自
90105

91106
### 伙伴系统
92107

93-
固定分区和动态分区方案都有缺陷。固定分区方案限制了活动进程数量,且如果可用分区的大小与进程大小很不匹配,那么内存空间的利用率会非常低。动态分区的维护特别复杂,并且会引入进行压缩的额外开销。更有吸引力的一种折中方案是伙伴系统。
108+
固定分区和动态分区方案都有缺陷。固定分区方案限制了活动进程数量,且如果可用分区的大小与进程大小很不匹配,那么内存空间的利用率会非常低。动态分区的维护特别复杂,并且会引入进行压缩的额外开销。更有吸引力的一种折中方案是伙伴系统。 它是一种经典的内存分配方案,是一种特殊的“分离适配”算法。主要思想是将内存按2的幂进行划分,组成若干空闲块链表,查找该链表找到能满足进程需求的最佳匹配块。
109+
110+
算法:
111+
112+
- 首先将整个可用空间看做一块:2的幂,这里假设一共有1M的内存。
113+
- 假设进程申请的空间大小为s,这里假设为100k,如果满足2的u-1次幂 < s <= 2的u次幂,则分配整个块,否则,将块划分为两个大小相等的伙伴,大小为2的u-1次幂,那这两个大小相等的伙伴就是伙伴关系。等内存回收后,具有伙伴关系的两块还可以合并到一起,组成一个更大的内存块。
114+
- 1M内存分为两块。
115+
- 512k、512k仍然大于100k,将第一块再分为两块。
116+
- 256k、256k、512k,而第一块256k仍然大于100k,第一块256k再分配。
117+
- 128k、128k、256k、512k。这个时候64k < 100k < 128k,所以把第一个128k分配给当前申请100k内存的进程。
118+
- 等该进程的内存回收后,前两个128k的内存具有伙伴关系,还可以合并成256k。合并后和后面的256k又是一个伙伴,又合并成512k,发现和后面的512k又是一个伙伴,就又合并成了1M的内存块。
94119

95120

96121

@@ -101,11 +126,13 @@ Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自
101126
在大小相等的分区中一个进程在其声明周期中可能占据不同的分区。首次创建一个进程映像时,它被装入内存中的某个分区。以后该进程可能被换出,当它再次被换入时,可能被指定到与上一次不同的分区中。动态分区也存在同样的情况,压缩后内存中的进程也可能发生移动。因此,进程访问(指令和数据单元)的位置不是固定的。进程被换入或在内存中移动时,指令和数据单元的位置也发生变化。为了解决这个问题,需要区分几种地址类型:
102127

103128
- 逻辑地址(logical address)是指与当前数据在内存中的物理分配地址无关的访问地址,在执行对内存的访问之前必须把它转换为物理地址。
104-
- 相对地址(relative address)是逻辑地址的一个特例,他是相对于某些已知点(通常是程序的开始处)的存储单元。
129+
- 相对地址(relative address)是逻辑地址的一个特例,他是相对于某些已知点(通常是程序的开始处)的存储单元。用户程序经过编译、汇编后形成目标代码,目标代码通常采用相对地址的形式,其首地址为0,其余地址相对于首地址而编址。
105130
- 物理地址(physical address)或绝对地址是数据在内存中的实际位置。
106131

107132
系统采用运行时动态加载的方式把使用相对地址的程序加载到内存。通常情况下,被加载进程中所有内存访问都相对于程序的开始点。因此,在执行包括这类访问的指令时,需要有把相对地址转换为物理内存地址的硬件机制。这类地址转换就需要一个特殊的处理器寄存器(基址寄存器),其内容是程序在内存中的起始地址。还有一个界限寄存器指明程序的终止位置。
108133

134+
将逻辑地址转换为物理地址的操作就叫做重定位。而把地址进行转换的功能部件就叫做内存管理单元(MMU, Memory Management Unit)。
135+
109136

110137

111138
### 分页
@@ -126,7 +153,7 @@ Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自
126153

127154
### 分段
128155

129-
细分用户程序的另一种可选方案是分段。采用分段技术,可以把程序和与其相关的数据划分到几个段(fragment)中。尽管段有最大长度限制,但并不要求所有程序的所有段的长度都相等。和分页一样,采用分段技术时的逻辑地址也是由两部分组成:段号和偏移量。
156+
细分用户程序的另一种可选方案是分段。采用分段技术,可以把用户进程地址空间按程序的自身的逻辑关系和与其相关的数据划分到几个段(fragment)中。尽管段有最大长度限制,但并不要求所有程序的所有段的长度都相等。同样内存空间也会被动态的划分为若干长度不相同的取悦,称为物理段,每个物理段由起始地址和长度确定。和分页一样,采用分段技术时的逻辑地址也是由两部分组成:段号和偏移量。 以段为单位进行分配,每段在内存中占据连续空间,但各段之间可以不相邻。
130157

131158
由于使用大小不等的段,分段类似于动态分区。在未采用覆盖方案或使用虚存的情况下,为执行一个程序,需要把它的所有段都装入内存。与动态分区不同的是,在分段方案中,一个程序可以占据多个分区,并且这些分区不要求是连续的。分段消除了内部碎片,但是和动态分区一样,他会产生外部碎片。不过由于进程被分成多个小块,因此外部碎片也会很小。
132159

@@ -154,12 +181,22 @@ Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自
154181

155182

156183

184+
### 段页式存储管理方案
185+
186+
综合页式、段式方案的优点,客服两者的缺点。用户进程先按段划分,每一段再按页面划分。内存分配还是以页为单位进行分配。
187+
188+
189+
190+
191+
157192

158193

159194
## 虚拟内存
160195

161196
面对越来越大的程序,常常产生程序>内存的问题,为解决这种问题,虚拟内存的概念得到普及.
162197

198+
虚拟内存是一种内存分配方案,是一项可以用来辅助内存分配的机制。我们知道,应用程序是按页装载进内存中的。但并不是所有的页都会装载到内存中,计算机中的硬件和软件会将数据从 RAM 临时传输到磁盘中来弥补内存的不足。如果没有虚拟内存的话,一旦你将计算机内存填满后,计算机会对你说呃,不,对不起,您无法再加载任何应用程序,请关闭另一个应用程序以加载新的应用程序。对于虚拟内存,计算机可以执行操作是查看内存中最近未使用过的区域,然后将其复制到硬盘上。虚拟内存通过复制技术实现了 妹子,你快来看哥哥能装这么多程序 的资本。复制是自动进行的,你无法感知到它的存在。
199+
163200
要有效的使用处理器和I/O设备,就需要在内存中保留尽可能多的进程。此外,还需要解除程序在开发时对程序使用内存大小的限制。解决这两个问题的途径就是虚拟内存技术。采用虚拟内存技术时,所有的地址访问都是逻辑访问,并在运行时转换为实地址。
164201

165202
虚拟内存机制使得期望运行大于物理内存的程序成为可能。其方法是将程序放在磁盘上,而将主存作为一种缓存,用来保存最频繁使用的部分程序。这种机制需要快速的映像内存地址,以便把程序生成的地址转换为有关字节在RAM中的物理地址。这种映像由CPU中的一个称为存储器管理单元(Memory Management Unit,MMU)的部件来完成。
@@ -211,7 +248,17 @@ Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自
211248

212249
以便处理在必须读取一个新页时,应该置换内存中的哪一页。当内存中的所有页框都被占据,且需要读取一个新页以处理一次缺页中断时,置换策略决定置换当前内存中的哪一页。所有策略的目标都是移出最近最不能访问的页。
213250

251+
- `最优算法`在当前页面中置换最后要访问的页面。不幸的是,没有办法来判定哪个页面是最后一个要访问的,`因此实际上该算法不能使用`。然而,它可以作为衡量其他算法的标准。
252+
- `NRU` 算法根据 R 位和 M 位的状态将页面氛围四类。从编号最小的类别中随机选择一个页面。NRU 算法易于实现,但是性能不是很好。存在更好的算法。
253+
- `FIFO` 会跟踪页面加载进入内存中的顺序,并把页面放入一个链表中。有可能删除存在时间最长但是还在使用的页面,因此这个算法也不是一个很好的选择。
254+
- `第二次机会`算法是对 FIFO 的一个修改,它会在删除页面之前检查这个页面是否仍在使用。如果页面正在使用,就会进行保留。这个改进大大提高了性能。
255+
- `时钟` 算法是第二次机会算法的另外一种实现形式,时钟算法和第二次算法的性能差不多,但是会花费更少的时间来执行算法。
256+
- `LRU` 算法是一个非常优秀的算法,但是没有`特殊的硬件(TLB)`很难实现。如果没有硬件,就不能使用 LRU 算法。
257+
- `NFU` 算法是一种近似于 LRU 的算法,它的性能不是非常好。
258+
- `老化` 算法是一种更接近 LRU 算法的实现,并且可以更好的实现,因此是一个很好的选择
259+
- 最后两种算法都使用了工作集算法。工作集算法提供了合理的性能开销,但是它的实现比较复杂。`WSClock` 是另外一种变体,它不仅能够提供良好的性能,而且可以高效地实现。
214260

261+
总之,**「最好的算法是老化算法和WSClock算法」**。他们分别是基于 LRU 和工作集算法。他们都具有良好的性能并且能够被有效的实现。还存在其他一些好的算法,但实际上这两个可能是最重要的。
215262

216263
#### 4. 驻留集管理
217264

@@ -237,6 +284,16 @@ Linux操作系统采用虚拟内存管理技术,使得每个进程都有各自
237284

238285

239286

287+
## 内存映射
288+
289+
进程通过一个系统调用(mmap)将一个文件(或部分)映射到其虚拟地址空间的一部分,访问这个文件就像访问内存中的一个大数组,而不是对文件进行读写。
290+
291+
在多数实现中,在映射共享的页面时不会实际读入页面的内容,而是在访问页面时,页面才会被每次一页的读入(虚拟内存的缺页处理),磁盘文件则被当做后备存储。
292+
293+
当进程退出或显式地解除文件映射时,所有被修改页面会写回文件。
294+
295+
296+
240297

241298

242299
## Android内存管理

OperatingSystem/5.I:O.md

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,25 @@
2929

3030

3131

32+
## I/O进程
33+
34+
I/O进程是专门处理系统中的I/O请求和I/O中断工作的。是系统进程,一般赋予最高优先级。一旦被唤醒,它可以很快抢占处理机投入运行。当I/O进程开始运行后,首先关闭中断,然后用receive去接收消息。如果没有消息,则开中断将自己堵塞,如果有消息,就去判断消息的类型是I/O请求还是I/O中断,然后再分别处理。
35+
36+
- I/O请求的进入
37+
38+
- 用户程序:调用send将I/O请求发送给I/O进程。调用block将自己堵塞,直到I/O任务完成后被唤醒。
39+
- 系统:利用wakeup唤醒I/O进程,完成用户所要求的I/O处理。
40+
41+
- I/O中断的进入
42+
43+
当I/O中断发生时,内核中的中断处理程序发送一条消息给I/O进程,由I/O进程负责判断并处理中断。
44+
45+
![image](https://raw.githubusercontent.com/CharonChui/Pictures/master/io_intercept_os.png?raw=true)
46+
47+
当一个 I/O 设备完成它的工作后,它就会产生一个中断(默认操作系统已经开启中断),它通过在总线上声明已分配的信号来实现此目的。主板上的中断控制器芯片会检测到这个信号,然后执行中断操作。
48+
49+
50+
3251
### 磁盘高速缓存
3352

3453

@@ -46,6 +65,58 @@
4665

4766

4867

68+
69+
70+
71+
72+
73+
74+
每个设备控制器都会有一个应用程序与之对应,设备控制器通过应用程序的接口通过中断与操作系统进行通信。设备控制器是硬件,而设备驱动程序是软件。
75+
76+
### 内存映射 I/O
77+
78+
每个控制器都会有几个寄存器用来和 CPU 进行通信。通过写入这些寄存器,操作系统可以命令设备发送数据,接收数据、开启或者关闭设备等。通过从这些寄存器中读取信息,操作系统能够知道设备的状态,是否准备接受一个新命令等。
79+
80+
为了控制`寄存器`,许多设备都会有`数据缓冲区(data buffer)`,来供系统进行读写。
81+
82+
那么问题来了,CPU 如何与设备寄存器和设备数据缓冲区进行通信呢?存在两个可选的方式。第一种方法是,每个控制寄存器都被分配一个 `I/O 端口(I/O port)`号,这是一个 8 位或 16 位的整数。所有 I/O 端口的集合形成了受保护的 I/O 端口空间,以便普通用户程序无法访问它(只有操作系统可以访问)。使用特殊的 I/O 指令像是
83+
84+
-
85+
86+
```
87+
IN REG,PORT
88+
```
89+
90+
CPU 可以读取控制寄存器 PORT 的内容并将结果放在 CPU 寄存器 REG 中。类似的,使用
91+
92+
-
93+
94+
```
95+
OUT PORT,REG
96+
```
97+
98+
CPU 可以将 REG 的内容写到控制寄存器中。大多数早期计算机,包括几乎所有大型主机,如 IBM 360 及其所有后续机型,都是以这种方式工作的。
99+
100+
第二个方法是 PDP-11 引入的,它将**「所有控制寄存器映射到内存空间」**中。
101+
102+
### 直接内存访问
103+
104+
无论一个 CPU 是否具有内存映射 I/O,它都需要寻址设备控制器以便与它们交换数据。CPU 可以从 I/O 控制器每次请求一个字节的数据,但是这么做会浪费 CPU 时间,所以经常会用到一种称为直接内存访问(Direct Memory Access) 的方案。它意味着 CPU 授予 I/O 模块权限在不涉及 CPU 的情况下读取或写入内存。也就是 DMA 可以不需要 CPU 的参与。这个过程由称为 DMA 控制器(DMAC)的芯片管理。由于 DMA 设备可以直接在内存之间传输数据,而不是使用 CPU 作为中介,因此可以缓解总线上的拥塞。DMA 通过允许 CPU 执行任务,同时 DMA 系统通过系统和内存总线传输数据来提高系统并发性。为了简化,我们假设 CPU 通过单一的系统总线访问所有的设备和内存,该总线连接 CPU 、内存和 I/O 设备,如下图所示
105+
106+
![image](https://raw.githubusercontent.com/CharonChui/Pictures/master/bus_os.png?raw=true)
107+
108+
#### DMA 工作原理
109+
110+
首先 CPU 通过设置 DMA 控制器的寄存器对它进行编程,所以 DMA 控制器知道将什么数据传送到什么地方。DMA 控制器还要向磁盘控制器发出一个命令,通知它从磁盘读数据到其内部的缓冲区并检验校验和。当有效数据位于磁盘控制器的缓冲区中时,DMA 就可以开始了。
111+
112+
DMA 控制器通过在总线上发出一个`读请求`到磁盘控制器而发起 DMA 传送,这是第二步。这个读请求就像其他读请求一样,磁盘控制器并不知道或者并不关心它是来自 CPU 还是来自 DMA 控制器。通常情况下,要写的内存地址在总线的地址线上,所以当磁盘控制器去匹配下一个字时,它知道将该字写到什么地方。写到内存就是另外一个总线循环了,这是第三步。当写操作完成时,磁盘控制器在总线上发出一个应答信号到 DMA 控制器,这是第四步。
113+
114+
然后,DMA 控制器会增加内存地址并减少字节数量。如果字节数量仍然大于 0 ,就会循环步骤 2 - 步骤 4 ,直到字节计数变为 0 。此时,DMA 控制器会打断 CPU 并告诉它传输已经完成了。
115+
116+
117+
118+
119+
49120
---
50121

51122

0 commit comments

Comments
 (0)