-
Notifications
You must be signed in to change notification settings - Fork 14
/
计算机的工作原理.txt
343 lines (311 loc) · 37.9 KB
/
计算机的工作原理.txt
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
·阿兰·图灵:用机器代替人脑进行计算分析的可行性分析
1936年,图灵提出了一种抽象的计算模型——图灵机,即将人们使用纸笔进行数学运算的过程进行抽象,这样可以由一个虚拟的机器替代人类进行数学运算
·冯·诺依曼:图灵机的具体实现构想
他提出冯·诺依曼结构计算机模型,该模型是图灵机的具体实现,他确定当今计算机运行方式:
-- 确定了计算机的体系结构——即“冯诺依曼结构”:输入设备 → 主机[中央处理器[控制单元、算术逻辑单元]、内存] → 输出设备
-- 提出计算机应采用二进制编码(之前采用十进制);
-- 提出计算机需要存储程序、按照程序进行工作(这是软件编程的理论基础,而之前让计算机工作是通过插拔电线)
·晶体管:从构想到计算机实现的硬件基础
-- 晶体管的发明让人们可以实现冯·诺依曼结构计算机。
-- 晶体管可以控制和存储高、低电平,而高、低电平可以表示二进制当中的“1”和“0”。
-- 晶体管实现逻辑运算和存储简介文档:
-- 《CPU是怎么实现运算的》:https://blog.csdn.net/qq_29025955/article/details/108476729
-- 《CPU 是怎么认识代码的? - Zign的回答 - 知乎》:https://www.zhihu.com/question/348237008/answer/843382847
·逻辑门:从硬件到逻辑运算的桥梁
-- 逻辑门是晶体管通往逻辑运算和数据存储的桥梁
-- 《电工学》中详细介绍了不同的晶体管可以控制高低电平输出状态,从而实现逻辑门
-- 逻辑门的种类:
-- 或门:或门有多个输入端,一个输出端,多输入或门可由多个2输入或门构成。只要输入中有一个为高电平时(逻辑1),输出就为高电平(逻辑1);只有当所有的输入全为低电平时,输出才为低电平。
-- 与门:有多个输入端,一个输出端。当所有的输入同时为高电平(逻辑1)时,输出才为高电平,否则输出为低电平(逻辑0)。
-- 非门:非门有一个输入和一个输出端,输入端和输出端的电平状态总是反相的
-- 与非门:由与门与非门组合而成。
-- 或非门:由或门和非门组合而成。
-- 为什么逻辑门能够进行运算?
下面展示逻辑门进行运算的实例分析,例如计算5+7=12这个加法:
人脑计算过程为:5加7等于12,写下2,然后心中默记产生一个进位。最终写成12。
计算机晶体管计算过程为:
1、先转换为二进制:
0000 0101
+ 0000 0111
-------------
0000 1100
2、分析步骤1,其中每一位的加法还能分解成两个动作:
动作1:同一位的两个数字相加
动作2:如果当前位结果大于1,则向前进一位
3、分析步骤2中的动作1,同一位的两个数字相加的结果有4种情况:
情况1:0 运算符 0=0
情况2:1 运算符 0=1
情况3:0 运算符 1=1
情况4:1 运算符 1=0
4、分析步骤3,4种情况中的运算符可以用“异或门”晶体管替代
5、分析步骤2中的动作2,只有1+1才需要进位1,运算结果有4种情况:
情况1:0 AND 0=0
情况2:1 AND 0=0
情况3:0 AND 1=0
情况4:1 AND 1=1
6、分析步骤5,4种情况中的运算符可以用“与门”晶体管替代
7、所以把一个“异或门”和一个“与门”组合到一起,就构成了一个“一位半加器”。
但事实上一位半加器只适用于末位数的加法。高位的二进制加法需要考虑3个输入,就是还需要额外考虑上一位得到的进位,这个过程可以用两个半加器来完成。
两个半加器组合起来,构成一个完整的 “一位全加器”。把8个这样的一位全加器组合起来,就构成了一个 “八位全加器”。
8、从最末尾开始相加,刚才的5+7:0000 0101 + 0000 0111
9、反过来就变成:
1010 0000
+ 1110 0000
-------------
0011 0000
10、把最初的进位设为0,得到的结果反过来就是:0000 1100 = 12。
-- 逻辑门进行其他运算的原理可参考《CPU内部实现“加,减,乘,除”过程》:https://blog.csdn.net/bjbz_cxy/article/details/77165290
-- 为什么逻辑门能够存储数据?
有一种叫SR锁存器的晶体管组,本质就是两个“或非门”,锁存器能够使电流通过互相博弈达到0或1的稳态,从而实现数据存储
-- 寄存器的逻辑门组成:把SR锁存器的两个输入端捏合成一个D输入端,再加上一个由时钟信号控制端E,就得到一个更高级的“时序D锁存器”,它是一个能在时钟开关E打开的情况下,记住D输入端进来的信号的装置。把两个相反的时序D锁存器组合在一起,就能构成一个 “D触发器(D Flip Flop)”。触发器和之前的锁存器的区别是,只有当时钟信号处在上升沿(从1V向5V跃迁)的一瞬间,D端的输入值才能写入触发器。并在随后的时间内,只要没有新的D输入写入,Q一直保持这个值。1bit肯定是不够的,不过我们可以并联嘛,用4个或者8个来保存4位或者8位数据。这种我们称之为寄存器(Register)。
·cpu:多种逻辑门的抽象组合
-- 文档:
-- 《cpu结构框图》:https://blog.csdn.net/chengchaonan/article/details/90742417
-- cpu的结构:CPU从逻辑上可以划分成3个模块,分别是控制单元、运算单元和存储单元,这三部分由CPU内部总线连接起来,可参考:专题/cpu的工作原理.jpg。
-- 控制单元:控制单元是整个CPU的指挥控制中心,由程序计数器PC(Program Counter), 指令寄存器IR(Instruction Register)、指令译码器ID(Instruction Decoder)和操作控制器OC(Operation Controller)等,对协调整个电脑有序工作极为重要。它根据用户预先编好的程序,依次从存储器中取出各条指令,放在指令寄存器IR中,通过指令译码(分析)确定应该进行什么操作,然后通过操作控制器OC,按确定的时序,向相应的部件发出微操作控制信号。操作控制器OC中主要包括节拍脉冲发生器、控制矩阵、时钟脉冲发生器、复位电路和启停电路等控制逻辑。
-- 指令寄存器(IR):当CPU执行一条指令时,先把它从内存储器取到缓冲寄存器中,在送入IR暂存,指令译码器根据IR的内容产生各种微操作指令,控制其他的组成部件工作,完成所需的功能。
-- 程序计数器(PC):PC具有寄存信息和计数两种功能,又称为指令计数器。程序的执行分为两种情况,一是顺序执行,而是转移执行。当程序开始执行前,将程序的起始地址送入PC,该地址在程序加载到内容是确定,因此PC的内容即是程序第一条指令的地址。执行指令时CPU自动修改PC的内容,以便使其保持的总是将要执行的下一条指令的地址。
-- 地址寄存器(AR):AR保存当前CPU所访问的内存单元的地址。由于内存和CPU存在着操作速度上的差异,所以需要使用AR保持地址信息,知道内存的读写操作完成为止。
-- 指令译码器(ID):指令分为操作码和操作数两部分,为了能执行任何给定的指令,必须对操作码进行分析,以便识别所完成的操作。ID就是对指令中的操作码字段进行分析解释,识别该指令规定的操作,向操作控制器发出具体的控制信号,控制各部件工作,完成所需的功能
-- 运算器:运算器由算术逻辑单元(Arithmetic and Logic Unit,ALU),累加寄存器(AC),数据缓冲寄存器(DR),和状态条件寄存器组成,它是数据加工处理部件,完成计算机的各种算术和逻辑运算。相对于控制器而言,运算器接受控制器的命令而进行动作,即运算器所进行的全部操作都是由控制器发出的控制信号来指挥的,所以它是执行部件。
-- 算术逻辑单元(ALU):ALU是运算器的重要组成部件,负责处理数据,实现对数据的算术运算和逻辑运算。
-- 累计寄存器(AC):AC通常简称为累计器,它是一个通用寄存器,其功能是当运算器的算术逻辑单元执行算术或逻辑运算时,为ALU提供一个工作区。最后的运算结果放到AC中,因此运算器中至少有一个累加寄存器。
-- 数据缓冲寄存器(DR):在对内部存储器进行读写操作时,用DR暂时存放由内存储器读写的一条指令或一个数据字,将不同时间段内读写的数据隔离开来。DR作为CPU和内存、外部设备之间的数据传送中转站;作为CPU和内存、外围设备之间在操作速度上的缓冲;在单累加器结构的运算器中,DR还可以件作为操作数寄存器。
-- 状态条件寄存器(PSW):PSW保存由算术指令和逻辑指令运行或测试的结果建立的各种条件码内容,主要分为状态标志和控制标志。这些标志通常由一位触发器保存,保存了当前指令执行完成之后的状态。通常一个算术操作产生一个运算结果,一个逻辑操作产生一个判决。
-- 存储单元:包括CPU片内缓存和寄存器组,是CPU中暂时存放数据的地方,里面保存着那些等待处理的数据,或已经处理过的数据,CPU访问寄存器所用的时间要比访问内存的时间短。采用寄存器,可以减少CPU访问内存的次数,从而提高了CPU的工作速度。但因为受到芯片面积和集成度所限,寄存器组的容量不可能很大。寄存器组可分为专用寄存器和通用寄存器。专用寄存器的作用是固定的,分别寄存相应的数据。而通用寄存器用途广泛并可由程序员规定其用途,通用寄存器的数目因微处理器而异。
-- 寄存器组:寄存器组可分为专用寄存器和通用寄存器。运算器和控制器中的寄存器是专用寄存器,起作用是固定的。通用寄存器用途广泛并可由程序员规定其用途,其数目因处理器不同有所差异。
-- 缓存:分为L1、L2、L3缓存,用于解决CPU运行处理速度与内存读写速度不匹配的矛盾
-- 运行原理/指令周期:控制单元在时序脉冲的作用下,将指令计数器里所指向的指令地址(这个地址是在内存里的)送到地址总线上去,然后CPU将这个地址里的指令读到指令寄存器进行译码。对于执行指令过程中所需要用到的数据,会将数据地址也送到地址总线,然后CPU把数据读到CPU的内部存储单元(就是内部寄存器)暂存起来,最后命令运算单元对数据进行处理加工。周而复始,直到停电。具体步骤:
1、取指令:CPU的控制器从内存读取一条指令并放入指令寄存器。指令的格式一般是这个样子滴:操作码+操作数的地址
操作码就是汇编语言里的mov,add,jmp等符号码;操作数地址说明该指令需要的操作数所在的地方,是在内存里还是在CPU的内部寄存器里。
2、指令译码:指令寄存器中的指令经过译码,决定该指令应进行何种操作(就是指令里的操作码)、操作数在哪里(操作数的地址)。
3、执行指令,分两个阶段“取操作数”和“进行运算”。
4、修改指令计数器,决定下一条指令的地址。
-- EIP:指令寄存器(extended instruction pointer),其内存放着一个指针,该指针永远指向下一条等待执行的指令地址。 可以说如果控制了EIP寄存器的内容,就控制了进程——我们让EIP指向哪里,CPU就会去执行哪里的指令。
-- 时钟:
-- 时钟就是一个固定频率的方波发生器
-- 工作原理:时钟产生一个方波,低电平代表逻辑0,高电平时代表逻辑1。逻辑电路中专门有电路能侦测到时钟的跳变,也就是从0 到1或者从1到0的过程。当检测到这个跳变后,CPU就开始执行下一个指令。因此时钟越快,CPU运转越快。超频就是把时钟都加快,让CPU工作在一个更快的频率下。
-- Q&A:
-- 5000 * 0.2的运算过程是怎样的?寄存器1中的5000和寄存器2中的0.2导入ALU,计算结果1000返回到寄存器3中
-- 《为什么CPU需要时钟才能工作》https://www.zhihu.com/question/21981280/answer/126921197
·cpu指令集:cpu运算的抽象集合
-- 指令的基本概念:
-- 计算机通过指令指挥计算机工作
-- cpu被时钟驱动,不断读取PC指针指向的指令,并增加PC指针,从内存中读取指令并执行。如此周而复始
-- 不同的CPU架构使用不同指令。目前使用最广泛的是RISC
-- 一条指令一般包括两个部分:操作码(opcode)和操作数
MIPS-32指令示意1:
000010 00000000000000000010000010
000010 → opcode=2 → PC跳转指令
00000000000000000010000010 → target=520 → 操作数为520
该条二进制指令翻译过来就是:将PC指针跳转到520位置,所以高级语言中的goto、break、swich..case都可能被编译为该指令
MIPS-32指令示意2:
100011 00011 00101 0000000000100100
000010 → opcode=25 → 将值和偏移合并后的值存入寄存器
00011 → rs=3 → 寄存器3
00101 → rt=5 → 寄存器5
0000000000100100 → offset=144 →指针偏移量
该条二进制指令翻译过来就是:将寄存器5中的值拿出来和offset合并(相加)得到最终读取的地址。读取最终地址的值,存入寄存器3
-- opcode:
-- 代表指令的类型
-- 决定寻址模式
-- opcode助记符:用英文替代二进制
-- 计算(ALU寄存器):ADD、SUB、MUL、AND、OR、SIN等
-- RAM:LOAD、STORE、MOV
-- 控制程序流程:BRANCH、BREQ、BRNE、BRIO
-- 指令的格式:
-- 基本格式:操作码 [操作数] [操作数]
-- 操作数:执行对象
-- 第一个操作数:目标操作数
-- 第二个操作数:源操作数
-- []表示可以缺省
-- 指令格式分类:
-- 零操作数指令:操作码
-- 单操作数指令:操作码 [操作数]
-- 双操作数指令:操作码 [操作数] [操作数]
-- 指令中的操作数:
-- 立即数:
-- 是一个常数
-- 立即数本身是参加操作的本身,可以是8位或者16位。
-- 只能作为源操作数
-- 例子:
mov AX,1234H
mov BL,22H
-- 寄存器操作数:
-- 是一个地址,计算速度最快
-- 参加运算的数存放在指令给出的寄存器中,可以是8位,16位
-- 例子:
mov AX,BX
mov DL,CH
-- 存储器操作数:
-- 是一个地址,计算速度最慢
-- 参与运算的数据存放在存储器的某一个或两个单元中
-- 表现形式:[]:方括号里面是地址(偏移地址)
-- 例子:
MOV AL,[1200H]
-- 指令周期:fetcg → decode → execute → store → fetcg
-- 指令分析:
-- MOV指令
取指阶段:程序计数器PC装入第一条指令的地址101,PC的内容被放到指令地址总线上,对指令进行译码并启动读命令。从101号地址读出MOV指令,通过指令总线IBus装入指令寄存器IR,程序计数器PC内容加1,变成102,为下一条指令做好准备。指令寄存器IR中的操作码被译码,CPU识别出是MOV指令,至此取指阶段完成。
执行阶段:操作控制器OC送出控制信号到通用寄存器,选择R1(10)为源寄存器,RO(00)为目标寄存器。OC送出控制信号到ALU,指定ALU做传送操作,打开ALU输出三态门,将ALU输出(10)送的数据总线DBus上,任何时刻DBus上只能有一个数据。将DBus上的数据打入数据缓冲寄存器DR,将DR中的数据打入目标寄存器RO,RO的内容由00变为10至此MOV指令执行完毕。
-- LAD指令
取指阶段:同MOV指令
执行阶段:OC发出控制命令,打开IR输出三态门,将指令中的直接操作数6放到数据总线DBus上,装入地址寄存器AR,将数存6号单元中的数100读出到DBus上,装入缓冲寄存器DR。将DR中的数100装入通用寄存器R1,原来R1中的值10被覆盖,至此LAD指令执行完毕。
-- ADD指令
取指阶段:同MOV指令
执行阶段:操作控制器OC送出控制信号到通用寄存器,选择R1(100)为源寄存器,R2(20)为目标寄存器。ALU做R1和R2的加法运算,打开ALU输出三态门,将运算结果120放到数据总线DBus上,然后打入缓冲寄存器DR。ALU产生的进位信号保存在状态字寄存器PSW中,将DR中数值120装入R2中,R2原来的数20被覆盖。到此ADD指令执行结束。
-- STO指令
取指阶段:同MOV指令
执行阶段:操作控制器OC送出控制信号到通用寄存器,选择R3(30)作为数据存储器的地址。打开通用寄存器输出三态门,将地址30放到DBus上并装入地址寄存器AR,并进行地址译码。操作控制器OC送出控制信号到通用寄存器,选择R2(120)作为数存的写入数据放到DBus上。将数值120写入数存30单元,原先的数据40被冲掉。至此STO指令执行结束。
-- JMP指令
取指阶段:同MOV指令
执行阶段:OC发出控制命令,打开IR输出三态门,将IR中的操作数101发送到DBus上,将DBus上的操作数101打入到程序计数器PC中,PC中原先的地址106被更换。于是下一条指令不是从106单元取出,而是转移到101单元取出。至此JMP指令执行周期结束。
-- 指令集:指令集指的是一个CPU所能够处理的全部指令的集合
·中断:cpu与人的交互
-- 外界发生中断时,中断信号经过中断控制器,最终达到CPU的中断引脚上,当那个管脚电平变低(或者变高)的时候,cpu就会被打断,并从特定地址开始执行。
-- 中断的意义:中断是处理器对外开放的实时受控接口。一个没有中断的计算机体系是决定论的:得知某个时刻CPU和内存的全部数据状态,就可以推衍出未来的全部过程。这样的计算机无法交互,只是个加速器。添加中断后,计算机指定了会兼容哪些外部命令,并设定服务程序,这种服务可能打断当前任务。这使得CPU“正在执行的程序”与“随时可能发生的服务”,二者形成了异步关系,外界输入的引入使得计算机程序不再是决定论。由人实时控制的中断输入,是无法预测的。再将中断响应规则化,推广开,非计算机科学人群就能控制计算机,发挥创造力。电竞鼠标微操,数码板绘,音频输入合成,影像后期数值调整,键盘点评天下大势,这些都不是定势流程,是需要人实时创造参与其中的事件,就由中断作为载体,与计算机结合了起来。
-- 提高工作效率
例如如果没有中断,将产生如下代码,要监听打印机是否就绪就花费大量资源
while(!printer.ready()){
sleep(10ms)
}
doPrint()
-- 故障恢复(异常处理,紧急事件等)
-- 简化编程模型(try/catch,计时器等)
-- 中断请求:
-- 硬件设备发给主板(打印机、键盘、鼠标等)
-- 硬件中断:CPU异常(除以0),时钟信号等
-- 软件中断:发出(异常、切换到内核态等)
-- 中断向量表:
-- (一般在内存中)一块区域,存储了中断类型和中断响应程序的对应关系。每一行叫做一个中断向量
-- 向量表实例:
中断类型 ISR地址 描述
00 0x0000 除以0
01 0x0004 调试单步
09 0x0024 键盘
18 0x0048 打印机
100 0x0190 自定义
·汇编语言:指令集的语义化
-- 指令由操作码和操作数组成,操作码和操作数都是二进制码,导致的结果就是这种代码难以阅读,而汇编码是比机器码更容易被人理解的代码
例如(1+4)*2+3的二进制计算为:
0100 0001 ;寄存器存入1
0001 0100 ;寄存器的数字加4
0010 0000 ;乘2
0001 0011 ;再加三
汇编码计算为:
MOV 1 ;寄存器存入1
ADD 4 ;寄存器的数字加4
SHL 0 ;乘2(介于我们设计的乘法器暂时只能乘2,这个0是占位的)
ADD 3 ;再加三
-- 机器指令和汇编码是一一对应的,汇编码可以通过汇编器转换为机器指令
-- MARS(MIPS汇编程序和运行时模拟器):https://blog.csdn.net/y_universe/article/details/82875244
·内存:存储空间的抽象
-- 数据表示方法:
-- Bit:"位"或"比特",是计算机运算的基础
-- Byte:"字节",是计算机文件大小的基本计算单位
-- 1byte=8bits
-- 数据的存储:
-- 种类:寄存器、缓存、RAM、ROM、硬盘等
-- 离cpu越远,速度越慢,但通常容量越大
-- 文档:
-- 《请问CPU,内核,寄存器,缓存,RAM,ROM的作用和他们之间的联系? - 罗成的回答 - 知乎》:https://www.zhihu.com/question/24565362/answer/54451597
-- 物理内存的特点:
-- 内存按访问的方式来看,就像长方形的带子,地址依次升高
-- 内存是一个随机读写设备,即可以访问内部任何一处,不需要从头开始找,只要直接给出地址即可,如访问内存0xc00,只要将该地址写入地址总线便可
-- 内存是稀缺资源,程序执行过程中经常可能出现内存超载(即内存空间不够用)的情况
-- CPU常见3种内存工作模式:
-- 实模式:
-- 在实模式下,所有的段都是可以读、写和可执行的。实模式下没有分段或是分页机制,逻辑地址和物理地址相等,不存在用户态、内核态之分。
-- 在BIOS加载、MBR、ntdlr启动阶段都处在实模式下。
-- 保护模式:
-- 是目前操作系统的运行模式,利用内存管理机制来实现线性地址到物理地址的转换,具有完善的任务保护机制。
-- 现在应用程序运行的模式均处于保护模式
-- 后续的内存管理相关概念和分析都是在保护模式基础下进行的
-- 虚拟8086模式:
-- 是运行在保护模式中的实模式
-- 可以把8086程序当做保护模式的一项任务来执行
-- 物理内存与虚拟内存:
-- 物理内存:
-- 概念:真实的内存硬件。
-- 直接访问物理内存带来的问题:
-- 内存空间利用率的问题:各个进程对内存的使用会导致内存碎片化
-- 读写内存的安全性问题:物理内存本身是不限制访问的,任何地址都可以读写,而现代操作系统需要实现不同的页面具有不同的访问权限,例如只读的数据等等
-- 进程间的安全问题:各个进程之间没有独立的地址空间,一个进程由于执行错误指令或是恶意代码都可以直接修改其它进程的数据,甚至修改内核地址空间的数据,这是操作系统所不愿看到的
-- 内存读写的效率问题:当多个进程同时运行,需要分配给进程的内存总和大于实际可用的物理内存时,需要将其他程序暂时拷贝到硬盘当中,然后将新的程序装入内存运行。由于大量的数据频繁装入装出,内存的使用效率会非常低
-- 虚拟内存:操作系统对物理内存的抽象
-- 背景:通过将物理内存抽象成虚拟内存可以解决直接访问物理内存带来的问题
-- 通过虚拟内存访问物理内存的过程:
-- 情况1——物理内存的常规访问:CPU想访问某个虚拟内存地址,找到进程对应的页表中的条目,判断有效位, 如果有效位为1,说明在页表条目中的物理内存地址不为空,根据物理内存地址,访问物理内存中的内容,返回
-- 情况2——物理内存不足的访问:CPU想访问某个虚拟内存地址,找到进程对应的页表中的条目,判断有效位,如果有效位为0,但页表条目中还有地址,这个地址是磁盘空间的地址,这时触发缺页异常,系统把物理内存中的一些数据拷贝到磁盘上,腾出所需的空间,并且更新页表。此时重新执行访问之前虚拟内存的指令,就会发现变成了情况1
-- 情况2也称为物理内存与磁盘的空间交换,是解决内存超载的一种方式
-- 虚拟内存实现原理:内存的不连续分配
-- 把程序分割成一块一块的装入内存,在物理上不用彼此相连,在逻辑上使用段表或者页表将离散分布的这些小块串起来形成逻辑上连续的程序。
-- 内存的分段和分页管理方式和由此衍生的段页式等都属于内存的不连续分配
-- 内存分段和分页的原因:
-- 在分段这个技术还没有出现之前,程序运行是需要从内存中分配出足够多的连续的内存,然后把整个程序装载进去。举个例子,某个程序大小是10M,然后,就需要有连续的10M内存空间才能把这个程序装载到内存里面。如果无法找到连续的10M内存,就无法把这个程序装载进内存里面,程序也就无法得到运行。
-- 编译器在编译程序时,要根据CPU访问内存的规则将代码编译成机器指令,这样编译出来的程序才能在该CPU上运行,所以说,在直接以绝对物理地址访问内存的CPU上运行程序,该程序中指令也必须得是物理地址,若加载程序运行,不管是内核程序还是用户程序,程序中地址若是绝对物理地址,那么该程序必须放在内存中的固定地方,于是两个编译出来地址相同的用户程序肯定没法同时运行,于是CPU采用“段基址+段内偏移地址”的方式来访问任意内存,这样的好处就是程序可以重定位了,尽管程序中指令给的是绝对物理地址,但可以运行多个程序了。
-- 内存分段:
-- 接操作物理内存,这样的话每次只能运行一个程序,并且不安全;另外,内存最大使用有限制。基于以上两点产生内存分段
-- 内存分段图:专题/内存的分段.jpg
-- 内存分页:
-- 分段的内存碎片太大,而内存分页通过页表方式,将虚拟地址映射到物理地址
-- 内存分页图:专题/内存的分页.jpg
-- 内存分段和分页的区别:分段和分页有很多类似的地方,但是最大的区别在于分页对于用户来说是没什么逻辑意义的,分页是为了完成离散存储,所有的页面大小都一样,对程序员来说这就像碎纸机一样,出来的东西没有完整意义。但是分段不一样,分段不定长,分页由系统完成,分段有时在编译过程中会指定划分,因此可以保留部分逻辑特征,容易实现分段共享。
-- 内存段页式:
-- 是分段和分页的结合:先将程序分段,然后再将分成的段再分成大小均等的页。即兼容分段模式,又可用分页模式。
-- 段页式图:专题/内存的段页式.jpg
-- 操作系统对虚拟内存的管理:https://blog.csdn.net/jmw1407/article/details/107674999
-- 每个进程创建加载的时候,操作系统(32位)会为每一个进程分配一个大小为4G的连续的虚拟地址空间。虚拟的意思就是,其实这个地址空间时不存在的,仅仅是每个进程“认为”自己拥有4G的内存,而实际上,它用了多少空间,操作系统就在内存或磁盘上划出多少空间给它
-- 图例:《操作系统对内存的管理.png》
-- 操作系统在设计之初就必须考虑不允许应该程序直接访问物理内存,原因有2点:
-- 应用程序可能破环操作系统,导致系统崩溃。操作系统将内存大概分成操作系统内核内存和给程序员使用的内存2大部分。这样的好处是:操作系统的内存不会被大量占用,避免机器卡住、卡死、死机等状态,可通过操作系统把应用程序关闭,使得操作系统更安全。
-- 应用程序之间相互访问或修改内容,存在安全问题
-- 操作系统限制应用程序访问内存的方式:
-- cpu中存在基地址寄存器和界限寄存器
-- 基地址寄存器是该程序段的起始内存地址
-- 界限寄存器是允许的访问的最大内存地址或者以基地址寄存器为基准的最大偏移量,应用程序进程访问超过界限的内存即被禁止
-- 一个进程(支持递归)对内存的管理:
-- 内存从低位到高位分别是:
-- 代码段(文本段):函数编译成opcode(二进制)后存到磁盘,运行时将二进制从磁盘加载到内存中
-- 数据段(静态空间):全局变量、常量、静态变量存数据段
-- 堆:比栈内存大得多,一般有几G大小。
-- 自由可分配内存:运行时逐渐被堆栈占据,运行完后数据释放空间恢复
-- 栈:第一个函数运行时压入栈底,函数调新函数时再往栈里压入该新函数,新函数运行完即出栈,第一个函数运行完再出栈,栈为空
-- 内核:内核区是所有进程共享的。内核区保存的是系统线程调度、内存管理、设备驱动等数据,这部分数据供所有的进程共享以及操作系统的使用——程序在运行的时候处于操作系统的监管下,监管进程的虚拟空间,当进程进行非法访问时强制结束程序
-- 该规划是利用虚拟内存原理对物理内存管理的抽象
-- 该规划是针对需要支持递归调用的程序的规划,对于静态内存分配的程序内存规划又不一样
-- 更多内存分配策略参考:《04_运行时刻环境/readme.md/运行存储分配》的分析
-- JVM对内存管理与操作系统对内存管理的关系?
-- 地址的种类:
-- 逻辑地址(虚拟地址):在进行C语言编程中,能读取变量地址值(&操作),实际上这个值就是逻辑地址,也可以是通过malloc或是new调用返回的地址。该地址是相对于当前进程数据段的地址,不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,CPU不进行自动地址转换)。应用程序员仅需和逻辑地址打交道,而分段和分页机制对一般程序员来说是完全透明的,仅由系统编程人员涉及。应用程序员虽然自己能直接操作内存,那也只能在操作系统给你分配的内存段操作。一个逻辑地址,是由一个段标识符加上一个指定段内相对地址的偏移量,表示为 [段标识符:段内偏移量]。
-- 线性地址:是逻辑地址到物理地址变换之间的中间层。程序代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。Intel 80386的线性地址空间容量为4G(2的32次方即32根地址总线寻址)。
-- 物理地址:是指出目前CPU外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址就直接成为物理地址了,比如在实模式下。
-- 内存寻址过程:
-- 过程简介:虚拟地址 ------(段机制)-------> 线性地址 ------(页机制)-------> 物理地址
-- 过程详解:
1、当程序中的指令访问某一个逻辑地址时,CPU首先会根据段寄存器的内容将虚拟地址转化为线性地址。
2、如果CPU发现包含该线性地址的内存页不在物理内存中就会产生缺页异常,该异常的处理程序通过是操作系统的内存管理器例程。内存管理器得到异常报告后会根据异常的状态信息。特别是CR2寄存器中包含的线性地址,将需要的内存页加载到物理内存中。
3、然后异常处理程序返回使处理器重新执行导致页错误异常的指令,这时所需要的内存页已经在物理内存中,所以便不会再导致页错误异常。
-- 文档:
《20 张图揭开「内存管理」的迷雾,瞬间豁然开朗》:https://zhuanlan.zhihu.com/p/152119007
《内存寻址原理》:https://blog.csdn.net/fivedoumi/article/details/50493575
·寻址:cpu对存储单元的访问
-- 存储单元中存放的数据信息大致可分为两大类,两类信息的寻址方式既有相同之处,又各有特点:
-- 指令寻址:由于程序中的指令序列通常是顺序排列的,对于顺序推进的指令序列,采用程序计数器PC加1的方式自动形成下一条指令的地址。当程序发生转移时,就不能采用上述方式,此时把指令地址的形成转换为操作数地址的寻址。把指令不当指令信息,而当作操作数信息来处理、按操作数的寻址方式获得指令地址。
-- 操作数寻址:操作数地址的寻址方式比较复杂,主要原因是,操作数本身不能像指令那样顺序排列,很多操作数是公用的,集中放在某一划定的区域。有些操作数是原始值存放在存储器中,有些则是中间运算的结果或先前运算的结果。它们的来源并无规律,具有很大的随机性和浮动性,这样就增加了获得有效地址的难度。另一方面随着程序设计技巧的发展,为了提高程序的质量也提出了很多操作数设置方法,丰富了寻址的手段。
-- 一个指令系统具有哪几种寻址方式,能否为编制程序提供方便是指令系统设计的关键,也是一个技巧性极强的问题,它是初学者理解一个指令系统的难点。在不同的计算机中,寻址方式的分类和名称不很统一,但大多可以归纳为以下几种方式:立即寻址、直接寻址、间接寻址、变址寻址以及它们的变型与组合
-- 寻址的分类:根据指令和操作数在不同的位置可将寻址方式如下划分
-- 立即数寻址:操作数包含在指令中,即指令中直接给出了操作数,操作数紧跟在操作码的后面,在取出指令的同时,也就取出了操作数。立即有操作数可供操作之用,所以称为立即操作数,或立即寻址。立即寻址的指令执行起来很快,CPU将数据与指令一起从存储器取出,不必通过寻址计算就获得了操作数。立即寻址也便于程序员使用。但是,因为数据和地址都是固定的,所以这种寻址方式灵活性最差。立即寻址方式可用来提供常数,设置初始值等。例如:MOV AX,1290H //1290H这个地址移动到AX寄存器中,这样,给出的地址就是操作数本身
-- 寄存器寻址:当操作数不放在内存中,而是放在CPU的寄存器中时,从寄存器中取操作数的方式称为寄存器寻址。
-- 寄存器直接寻址:在指令中,直接给出操作数所在的寄存器编号,例如:MOV AX,BX
-- 寄存器间接寻址:寄存器中给出的不是一个操作数,而是操作数所在的主存单元的地址,EA是间接从寄存器得到,例如:MOV AX,[BX]
-- 寄存器相对寻址方式:EA是一个基址或变址寄存器内容再加上8或16位位移量之和,例如:MOV AX,[BX+1234H]
注意:位移量可以为TOP[],TOP为符号地址,即位移量,或者在中括号里,也可以用小点连接
-- 内存寻址:
-- 直接寻址:即操作数字段直接给出操作数所在的内存地址,例如:MOV AX [200] //将地址为200处的数据,存放到AX中
-- 基址寻址:基址寻址就是在操作数中用基址寄存器(在实模式下只能用bp和bx寄存器)作为地址的起始,地址的变化以它为基础。就是将CPU中的基址寄存器的内容,加上变址寄存器的内容而形成操作数的有效地址。例如:add word[bx], 0x1234 //将0x1234加上内存地址ds:bx处的值后再存入内存地址ds:bx中
-- 变址寻址:变址寻址和基址寻址类似,只是寄存器由bx、bp换成了si、和di。例如:mov [di], ax //将寄存器ax的值存入ds:di指向的内存
-- 基址变址寻址方式:EA为一个基址寄存器加变址寄存器的内容,例如: MOV AX,[BX+DI]
-- 相对基址变址寻址方式:EA为一个基址寄存器加变址寄存器加位移量,例如:MOV AX,[MASK+BX+SI]
·思考:
-- 从人脑计算到冯诺依曼机、从晶体管到逻辑门、从二进制到汇编码、从物理内存到虚拟内存,就是一个不断虚拟(抽象)的过程