-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
1037 lines (801 loc) · 85.5 KB
/
index.html
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
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>CoDeleven的个人博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="Codeleven的博客">
<meta property="og:type" content="website">
<meta property="og:title" content="CoDeleven的个人博客">
<meta property="og:url" content="http://yoursite.com/GithubPages/index.html">
<meta property="og:site_name" content="CoDeleven的个人博客">
<meta property="og:description" content="Codeleven的博客">
<meta property="og:locale" content="zh">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="CoDeleven的个人博客">
<meta name="twitter:description" content="Codeleven的博客">
<link rel="alternate" href="/atom.xml" title="CoDeleven的个人博客" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/codeleven.github.io/css/style.css">
</head>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/codeleven.github.io/" id="logo">CoDeleven的个人博客</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/codeleven.github.io/">Home</a>
<a class="main-nav-link" href="/codeleven.github.io/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://yoursite.com/GithubPages"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-(未埋)了解阻塞队列之PriorityBlockingQueue" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/08/05/(未埋)了解阻塞队列之PriorityBlockingQueue/" class="article-date">
<time datetime="2018-08-05T03:28:15.683Z" itemprop="datePublished">2018-08-05</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/08/05/(未埋)了解阻塞队列之PriorityBlockingQueue/">了解阻塞队列之PriorityBlockingQueue</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>下次埋</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/08/05/(未埋)了解阻塞队列之PriorityBlockingQueue/" data-id="cjkgaepk50005lcvjfg2f92av" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/数据结构和算法/">数据结构和算法</a></li></ul>
</footer>
</div>
</article>
<article id="post-《七周七并发模型》第一章第一天" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/08/05/《七周七并发模型》第一章第一天/" class="article-date">
<time datetime="2018-08-05T03:28:15.478Z" itemprop="datePublished">2018-08-05</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/08/05/《七周七并发模型》第一章第一天/">《七周七并发模型》第一章第一天</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="前言收获"><a href="#前言收获" class="headerlink" title="前言收获"></a>前言收获</h1><p>并行和并发的区别</p>
<ul>
<li>并发:面对多个任务同时存在的情况下,能够处理它,不用管如何执行(可以交替执行、可以并行执行)</li>
<li>并行:面对多个任务同时存在的情况下,能够处理它,任务和任务间互不影响,一起执行</li>
</ul>
<p>并行概念是并发概念的一个子集,如下图所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/JavaConcurrent/Concurrency_Parallelism.jpg" alt=""></p>
<p>并行架构:</p>
<ul>
<li>位级并行</li>
<li>指令级并行</li>
<li>数据集并行</li>
<li>任务级并行</li>
</ul>
<p>全书要讲解的7个模型:</p>
<ul>
<li>线程与锁:其他模型的技术基础,虽然存在不足但仍是众多软件的首选</li>
<li>函数式编程:消除了可变状态,根本上是线程安全的,易于并行执行</li>
<li>Clojure之道——分离标识与状态:命令式编程和函数式编程混搭的方案,在两种编程方式中取得平衡来发挥两者的优势</li>
<li>actor: 适用于共享内存模型和分布式内存模型,也适合解决地理分布型问题,能够提供强大的容错</li>
<li>通信顺序进程(Communicating Sequential Processes ,CSP): 该模型和actor很类似,两者都基于消息传递。不过CSP侧重于传递信息的通道,而actor模型侧重于通道两端的实体,使用CSP模型的代码会有明显不同的风格</li>
<li>数据级并行:GPU,如果要进行有限元分析、流体力学计算或其他的大量数字计算,GPU的性能将是不二选择</li>
<li>Lambda: 综合了MapReduce和流式处理的特点,是一种可以处理多种大数据问题的架构</li>
</ul>
<p>后续学习要带上以下几个问题:</p>
<ul>
<li>这个模型适用于解决并发问题、并行问题还是两者皆可?</li>
<li>这个模型适合哪种并行架构</li>
<li>这个模型是否有利于我们写出容错性强的代码,或用于解决分布式问题的代码?</li>
</ul>
<h1 id="互斥和内存模型"><a href="#互斥和内存模型" class="headerlink" title="互斥和内存模型"></a>互斥和内存模型</h1><p>因为该部分讲的不深,而且经过两本并发书籍的洗礼,理解起来较为简单,就将总结的话记一下:</p>
<ul>
<li>对共享变量的所有访问都需要被同步化</li>
<li>读线程和写线程都需要同步化</li>
<li>按照约定的全局顺序来获取多吧锁</li>
<li>当持有锁时不要访问外星方法(不了解的方法,里面可能会对部分资源加锁)</li>
<li>持有锁的时间尽可能短</li>
</ul>
<h1 id="自学篇之JMM"><a href="#自学篇之JMM" class="headerlink" title="自学篇之JMM"></a>自学篇之JMM</h1><h1 id="自学篇之JSR-133的FAQ"><a href="#自学篇之JSR-133的FAQ" class="headerlink" title="自学篇之JSR-133的FAQ"></a>自学篇之JSR-133的FAQ</h1><p>原文出自(<a href="http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html" target="_blank" rel="noopener">http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html</a>)</p>
<h2 id="究竟什么是内存模型?"><a href="#究竟什么是内存模型?" class="headerlink" title="究竟什么是内存模型?"></a>究竟什么是内存模型?</h2><p>在现代处理器系统里,通常会划分几个内存缓冲层。它们能够提高访问数据的速度并且降低共享内存总线的流量(因为缓存层就直接可以满足处理器需求),它们的结构示意图如下所示。那么两个处理器在同一时间查看同个内存地址时,什么情况下能看到一样的值。这就是内存可见性问题</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/JavaConcurrent/CPUcache_architecture.PNG" alt="处理器、缓存、共享内存总线间的关系"></p>
<p>第一个影响可见性的原因是,强内存模型能直接保证 <strong>对同一个内存地址在任何时候都能看到相同的值</strong>;弱内存模型需要 <strong>内存屏障(memory barriers)</strong>,这些指令会要求处理器刷新或无效化本地缓冲区,所以就能看到其他处理器的写入。因为强内存模型对内存指令的需求较少(注意,不是不需要,只是需求少),所以强内存模型易于编程。然而近代处理器都是鼓励往弱内存模型发展,由于弱内存模型对缓存一致性比较放松,跨多处理器和大量内存会有更好的扩展性。</p>
<p>第二个影响可见性的原因就是重排序,在内存模型给定的边界里任由编译器、内存、处理器优化。</p>
<p>所以Java内存模型主要描述了程序中的变量和物理机中内存、寄存器间的关系,JMM尽可能让编译器、硬件可以执行优化。</p>
<h2 id="其他语言有内存模型吗"><a href="#其他语言有内存模型吗" class="headerlink" title="其他语言有内存模型吗"></a>其他语言有内存模型吗</h2><p>很多编程语言,像C、C++,就没有直接支持多线程。如果想避免所有重排序发生在编译器里,整个架构都需要依赖线程库、依赖编译器、系统</p>
<h2 id="JSR-133讲了什么"><a href="#JSR-133讲了什么" class="headerlink" title="JSR-133讲了什么"></a>JSR-133讲了什么</h2><p>JSR-133 为Java定义了一个新的内存模型,修复了早期的内存模型问题。为了实现它,final、volatile的语义均得到改变。通过<a href="http://www.cs.umd.edu/users/pugh/java/memoryModel" target="_blank" rel="noopener">完整的JSR-133语义</a>可以了解到JSR-133的全貌</p>
<p>JSR-133的目的包含以下几点:</p>
<ul>
<li>保持已存在的安全保证,加强其他安全,比如变量值不能凭空出现,每个变量值的出现都应该有理有据</li>
<li>正确同步的程序语义应该尽可能简单、直观</li>
<li>对于不正确同步的程序语义,应尽可能的减小风险。</li>
<li>对于每个程序员来说,都应该能正确推导多线程和内存间的交互关系</li>
<li>JVM应该被设计为:能为大部分主流的硬件架构提供高性能</li>
<li>应该保证初始化安全。如果一个对象被正确的构造(没有发生this逸出),就算没有同步,所有引用该对象的线程都将看到该构造器设置的final变量的值。</li>
<li>对现有代码造成最小的影响</li>
</ul>
<h2 id="重排序意味着什么"><a href="#重排序意味着什么" class="headerlink" title="重排序意味着什么"></a>重排序意味着什么</h2><p>当访问某个变量时,可能出现它的值和预期指定的值不一样。编译器会重排序指令,处理器也可能会重排序指令。数据在寄存器、缓冲、内存间移动的顺序不按程序指定的来。<br>尽管如此,单线程程序是看不到重排序的影响的,即中途发生重排序,结果仍然不会改变。然而重排序会在多线程中起到作用:由于一个线程的修改可以被另外一个线程观察到,所以重排序中途修改的值会被另一个线程观测到。</p>
<h2 id="旧的内存模型有什么问题"><a href="#旧的内存模型有什么问题" class="headerlink" title="旧的内存模型有什么问题"></a>旧的内存模型有什么问题</h2><p>难理解,出现范围广。具体表现在以下两个例子里:</p>
<ul>
<li>final变量被一个线程读时可能出现默认值,另一个线程读时可能出现构造值。这就意味着不可变对象发生变化了。</li>
<li>volatile写会和普通读/写发生重排序。</li>
</ul>
<h2 id="什么是不正确的同步"><a href="#什么是不正确的同步" class="headerlink" title="什么是不正确的同步"></a>什么是不正确的同步</h2><ol>
<li>一个线程对一个变量执行写入</li>
<li>另一个线程对同一个变量执行读取</li>
<li>写入和读取没有用同步排序<br>当发生这种情况时,都可以认为是发生了“数据竞争”</li>
</ol>
<h2 id="同步做了什么"><a href="#同步做了什么" class="headerlink" title="同步做了什么"></a>同步做了什么</h2><ul>
<li>排斥:一个线程获取了管程,另一个线程只能等到第一个线程释放后才能获取</li>
<li>可见性:在我们释放管程时,同步块会将本地内存刷新到主内存;获取管程之后,同步块会使本地缓存失效,重新去主内存中加载。</li>
<li>happens-before:当一个操作happens-before另一个操作,那么就能向程序员保证JMM会让第一个先于第二个发生并对第二个可见</li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/08/05/《七周七并发模型》第一章第一天/" data-id="cjkgaepm8001mlcvjuy188kxb" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/JMM/">JMM</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/Java并发/">Java并发</a></li></ul>
</footer>
</div>
</article>
<article id="post-无符号数和有符号数" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/08/04/无符号数和有符号数/" class="article-date">
<time datetime="2018-08-04T05:12:12.000Z" itemprop="datePublished">2018-08-04</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/08/04/无符号数和有符号数/">处理器(一)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="术语科普"><a href="#术语科普" class="headerlink" title="术语科普"></a>术语科普</h1><ul>
<li>机器数:在计算机里表示的方法,即正负不再是用 <code>-</code>,<code>+</code>来表示,而是采用<code>0</code>表示正数,<code>1</code>表示负数</li>
<li>真数:相对机器数,用<code>-</code>,<code>+</code>来表示正负的数就叫真数</li>
</ul>
<h1 id="原码"><a href="#原码" class="headerlink" title="原码"></a>原码</h1><p>原码即最原始的二进制表示法,这种方法简单直观。<br>比如 <code>+11</code>,通常表示为 <code>0000 1011</code><br>比如 <code>-11</code>,通常表示为 <code>1000 1011</code></p>
<p>不知道小伙伴会不会有疑问,那8位可以表示的范围不久小了吗?<br>答:是的,小了一半。而且 [+0]<sub>原</sub> != [-0]<sub>原</sub></p>
<h2 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h2><h3 id="整数"><a href="#整数" class="headerlink" title="整数"></a>整数</h3><p>计算机规定,任何正数的原码都是其本身。而对于负数来说,需要把最高位变为“1”,所以需要 2<sup>n</sup> - x,由于负负得正,其实是 2<sup>n</sup> + |x|,其示意图如下所示</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E6%95%B4%E6%95%B0%E5%8E%9F%E7%A0%81%E5%AE%9A%E4%B9%89.png" alt="整数的原码定义"></p>
<p>其中x为真值,n为整数的位数</p>
<p>比如:<br>当x = -1011 时,[x]<sub>原</sub> = 2<sup>4</sup>(即1 0000) - (-1011) = 1 1011,即1, 1011</p>
<blockquote>
<p>2<sup>4</sup>是指16,16即10000,如果是2<sup>3</sup>,那么就是8,即1000</p>
</blockquote>
<p><strong>注意,这里记得用逗号分隔 符号位和数值位</strong></p>
<h3 id="小数"><a href="#小数" class="headerlink" title="小数"></a>小数</h3><p>正的小数都是其本身。而对于负数来说,需要把最高位变成“1”,所以需要1 - x,由于负负得正,其实是 1 + |x|,其示意图如下所示</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E5%B0%8F%E6%95%B0%E5%8E%9F%E7%A0%81%E5%AE%9A%E4%B9%89.png" alt="小数的原码定义"></p>
<p>其中x为真值,n为整数的位数</p>
<p>比如:<br>当x = -0.1011 时,[x]<sub>原</sub> = 1 - (-0.1011) = 1.1011</p>
<p><strong>注意,这里记得用点号分隔 符号位和数值位</strong></p>
<h2 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h2><p>简单直观</p>
<h2 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h2><p>进行加减运算会带来许多麻烦,比如一个正数和负数相加(注意,这里进行运算时都要采用机器数,不能使用真数)</p>
<h1 id="补码"><a href="#补码" class="headerlink" title="补码"></a>补码</h1><p>由于原码在进行四则运算时,总需要判断正负号,所以科学家想到了一种用正数代替负数的方法,即补码</p>
<p>补码的原理类似于 <code>mod(即取余)</code>。比如有一个时钟,现在指向6点,如果想拨到8点钟,我们可以顺时针拨2个小时(可以理解为<code>(6 + 2) mod 12 = 8</code>),也可以逆时针拨10个小时(可以理解为 <code>(12 + (6 - 10)) mod 12 = 8</code>)。这说明了可以用加法代替减法,只要限定最大值,然后不断循环即可。</p>
<p>##定义</p>
<h3 id="整数-1"><a href="#整数-1" class="headerlink" title="整数"></a>整数</h3><p>计算机规定,正数的补码是其本身;负数的补码要取<code>模 + 真值(为负数)</code>。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E6%95%B4%E6%95%B0%E8%A1%A5%E7%A0%81%E5%AE%9A%E4%B9%89.png" alt="整数补码"></p>
<p>其中x为真值,n为整数的位数</p>
<p>比如:<br>当 x = -1010 时,[x]<sub>补</sub> = 2<sup>4</sup> + (-1010) = 10000 + (-1010) = 0110</p>
<h3 id="小数-1"><a href="#小数-1" class="headerlink" title="小数"></a>小数</h3><p>计算机规定,正数的补码是其本身;负数的补码要取<code>2 + 真值(为负数)</code>。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E5%B0%8F%E6%95%B0%E8%A1%A5%E7%A0%81%E5%AE%9A%E4%B9%89.png" alt="小数补码定义"></p>
<p>比如:<br>当 x = -0.1010 时,[x]<sub>补</sub> = 2 + (-0.1010) = 10.0000 + (-0.1010) = 1.0110</p>
<p>对于补码来说,由于经常会出现进位的算法,计算时很麻烦,所以这里给出一个简单的算法:</p>
<h2 id="优化"><a href="#优化" class="headerlink" title="优化"></a>优化</h2><p>除原码符号位,将其他数值为取反(相当于减),最后加一即可。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E6%95%B4%E6%95%B0%E8%A1%A5%E7%A0%81%E8%BF%87%E7%A8%8B.png" alt="整数补码过程"></p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E5%B0%8F%E6%95%B0%E8%A1%A5%E7%A0%81%E8%BF%87%E7%A8%8B.png" alt="小数补码过程"></p>
<h1 id="反码"><a href="#反码" class="headerlink" title="反码"></a>反码</h1><p>反码其实是补码的一个过渡表示法。将除符号位以外的数值位取反,就可以得到反码(这是我们最直观的做法,但是计算机没那么聪明,需要通过数学公式得到)。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E6%95%B4%E6%95%B0%E5%8F%8D%E7%A0%81%E5%AE%9A%E4%B9%89.png" alt="整数反码定义"></p>
<p>可以很明显发现反码其实就是在补码的基础上减去了一个“1”,就得到了反码。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FCPU%2FArithmeticMethod%2F%E5%B0%8F%E6%95%B0%E5%8F%8D%E7%A0%81%E5%AE%9A%E4%B9%89.png" alt="小数反码定义"></p>
<blockquote>
<p>2<sup>-n</sup> 代表前面有n个0,比如 2 <sup>-4</sup>,即为0.0001</p>
</blockquote>
<p>同理,也是在求补码的基础上减去了一个“1”(这里的“1”对于小数来说是最小的n位)</p>
<h1 id="移码"><a href="#移码" class="headerlink" title="移码"></a>移码</h1><p>待我学习一波定点和浮点。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/08/04/无符号数和有符号数/" data-id="cjkgaepnp0035lcvjm32wb667" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-DMA方式" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/08/02/DMA方式/" class="article-date">
<time datetime="2018-08-02T10:16:42.000Z" itemprop="datePublished">2018-08-02</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/08/02/DMA方式/">DMA方式</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>DMA方式是IO接口的控制方式之一。DMA和其他控制方式相比,它拥有专门的数据通路,所 <strong>以主存和设备交换信息时,可以不经过CPU</strong>,也不需要CPU参与数据交换(程序中断需要CPU参与中断服务),那么就省去了 保护现场、恢复现场的流程。</p>
<p>由于DMA接口的速度很快,所以经常用于高速IO设备,因为高速IO设备如果不及时交互信息,很可能产生数据丢失。</p>
<p>上面所有的优势均建立在 <strong>主存和IO设备直接交换信息,不需要经过CPU</strong>这个条件之上,而当CPU和IO设备同时访问主存时,就会发生冲突。为了解决冲突,通常会采用以下几个方法:</p>
<ul>
<li>停止CPU访存</li>
<li>周期挪用(又称周期窃取)</li>
<li>交替访问</li>
</ul>
<h1 id="停止CPU访存"><a href="#停止CPU访存" class="headerlink" title="停止CPU访存"></a>停止CPU访存</h1><p>假设IO设备要发送一些数据,DMA接口会向CPU发出一个停止信号,要求CPU放弃总线控制前,DMA接口获得总线控制权后,开始进行数据传送,在数据传输结束后,DMA接口通知CPU可以使用主存,并把总线使用权交给CPU。其时序图如下所示</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E5%81%9C%E6%AD%A2CPU%E8%AE%BF%E9%97%AE%E7%9A%84%E6%97%B6%E5%BA%8F%E5%9B%BE.png" alt="停止CPU访问主存"></p>
<p>这种方式的缺陷主要在于 <strong>CPU失去总线控制权的这段时间里</strong>,DMA接口也并不是百分百利用这段时间,因为IO设备传输数据给接口的数据缓冲器这段时间也总大于一个存期周期。换句话来说,当IO设备还在准备数据的时候,CPU也仍然处于空闲状态。</p>
<h1 id="周期挪用(周期窃取)"><a href="#周期挪用(周期窃取)" class="headerlink" title="周期挪用(周期窃取)"></a>周期挪用(周期窃取)</h1><p>当IO设备发出DMA请求时,IO设备便挪用或窃取总线占用权一个或几个周期,而DMA不请求时,CPU仍可继续访问。</p>
<p>而IO设备请求DMA时,会有三种状况:</p>
<ol>
<li>CPU不访问主存,那么DMA接口和CPU不会发生主存</li>
<li>CPU正在访问主存,那么DMA接口需要等待该次存储周期结束</li>
<li>DMA和CPU同时要访问主存时,DMA接口发出请求占用几个存取周期(即在CPU执行访存指令过程中插入DMA请求,使CPU延迟了几个周期再访问)</li>
</ol>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E5%91%A8%E6%9C%9F%E6%8C%AA%E7%94%A8%E6%97%B6%E5%BA%8F%E5%9B%BE.png" alt="周期挪用时序图"></p>
<p>IO设备每挪用一个主存周期都要申请总线控制权、建立总线控制权和归还总线控制权。因此,对于主存来说,虽然只传一个字只占用一个周期,但对DMA接口来说,要处理包括申请、建立、传输、归还等阶段,实质上DMA接口要占好几个周期。</p>
<p>因此周期挪用适合IO设备的读/写周期大于主存周期的情况。</p>
<h1 id="DMA与CPU交替执行"><a href="#DMA与CPU交替执行" class="headerlink" title="DMA与CPU交替执行"></a>DMA与CPU交替执行</h1><p>这种方式不需要总线使用权的申请、建立、归还过程,总线使用权分别由C<sub>1</sub>、C<sub>2</sub>控制的。CPU和DMA接口各自有独立的访存地址寄存器、数据寄存器、读写信号。实际上总线变成了C<sub>1</sub>、C<sub>2</sub>控制下的多路转换器,其总线控制权的转移几乎不需要时间,具有很高的DMA传送速率。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E4%BA%A4%E6%9B%BF%E8%AE%BF%E9%97%AE%E6%97%B6%E5%BA%8F%E5%9B%BE.png" alt="交替访问时序图"></p>
<h1 id="DMA接口的功能和组成"><a href="#DMA接口的功能和组成" class="headerlink" title="DMA接口的功能和组成"></a>DMA接口的功能和组成</h1><p>DMA接口应该具有以下几个功能:</p>
<ol>
<li>向CPU申请DMA传送</li>
<li>在CPU允许DMA工作时,处理总线控制权的转交,避免引起总线竞争</li>
<li>在DMA期间管理系统总线,控制数据传输</li>
<li>确定数据传送的起始地址和数据长度,更新数据传输过程中的数据地址和数据长度</li>
<li>在数据块传输结束时,给出DMA操作完成的信号</li>
</ol>
<p>DMA接口的组成原理如下图所示<br><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E7%AE%80%E5%8D%95DMA%E6%8E%A5%E5%8F%A3%E7%BB%84%E6%88%90%E5%8E%9F%E7%90%86.png" alt="DMA接口的组成"></p>
<h1 id="DMA数据传输的流程"><a href="#DMA数据传输的流程" class="headerlink" title="DMA数据传输的流程"></a>DMA数据传输的流程</h1><p>DMA的数据传送过程分为 <strong>预处理</strong>、<strong>数据传送</strong>、<strong>后处理</strong>三个阶段。<br>其示意图如下所示:<br><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2FDMA%E4%BC%A0%E8%BE%93%E8%BF%87%E7%A8%8B%E6%A6%82%E8%A7%88.png" alt="DMA传输过程概览"></p>
<h2 id="预处理"><a href="#预处理" class="headerlink" title="预处理"></a>预处理</h2><p>在DMA接口开始工作之前,CPU必须给它预置如下信息:</p>
<ul>
<li>给DMA控制逻辑指明数据传送方向</li>
<li>给设备地址寄存器(DAR)送入设备信号</li>
<li>向主存地址寄存器(AR)送入交换数据的起始地址</li>
<li>对字计数器赋予交换数据的个数</li>
</ul>
<p>当这些工作完成后,程序初始化结束。</p>
<h2 id="数据传输"><a href="#数据传输" class="headerlink" title="数据传输"></a>数据传输</h2><p>等IO设备准备好数据 或 处理完输出的数据后,就让DMA接口向CPU提出总线获取控制权,如果有多个DMA请求,则按轻重缓急排队等待。当IO设备获取到总线控制权后,数据的传输就由DMA进行管理。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2FDMA%E6%95%B0%E6%8D%AE%E4%BC%A0%E8%BE%93%E8%BF%87%E7%A8%8B.png" alt="DMA数据传输过程概览"></p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2FDMA%E4%BC%A0%E8%BE%93%E8%BF%87%E7%A8%8B%E8%AF%A6%E6%83%85.png" alt="DMA传输过程详情"></p>
<p>数据读取过程:</p>
<ol>
<li>IO设备发送数据到数据缓冲区内</li>
<li>IO设备发送DREQ请求给DMA控制逻辑</li>
<li>DMA控制逻辑发送HRQ给总线申请获取总线控制权</li>
<li>获取成功后,DMA控制逻辑收到HLDA响应,将总线控制权交给DMA接口</li>
<li>将DMA主存地址寄存器的主存地址送到地址总线,并命令存储器写</li>
<li>通知设备已被授予一个DMA周期(DACK),并为下一个字做准备</li>
<li>将DMA数据缓存寄存器的内容送至数据总线</li>
<li>主存将数据总线上的信息写到地址总线指定的存储单元</li>
<li>修改AR和WC</li>
<li>判断数据块是否结束,若未结束继续传输;否则向CPU申请程序中断,标志数据传输完毕</li>
</ol>
<p>输出数据过程:</p>
<ol>
<li>当DMA的BR已将数据送至IO设备后,表示BR已空</li>
<li>设备向DMA接口发请求DREQ</li>
<li>DMA接口向CPU申请总线控制权HRQ</li>
<li>CPU发回HLDA信号,允许交出总线控制权</li>
<li>将DMA主存地址寄存器中的主存地址送到地址总线,并命令存储器读</li>
<li>通知设备已经被授予一个DMA周期(DACK),并为交换下一个字做准备</li>
<li>主存将相应地址单元的内容通过数据总线读入DMA的BR中</li>
<li>将BR的内容送到输出设备</li>
<li>修改AR和WC</li>
<li>判断数据块是否结束,若未结束继续传输;否则向CPU申请程序中断,标志数据传输完毕</li>
</ol>
<h2 id="后处理"><a href="#后处理" class="headerlink" title="后处理"></a>后处理</h2><p>当DMA的中断请求得到响应后,CPU停止原程序的执行,转去执行中断服务程序,做一些DMA的结束工作,包括校验数据的正确性、决定是否继续用DMA传送其他数据块、测试传输过程中是否发生错误。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/08/02/DMA方式/" data-id="cjkgaepk70008lcvjs4cui2py" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-程序中断方式" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/07/27/程序中断方式/" class="article-date">
<time datetime="2018-07-27T07:02:53.000Z" itemprop="datePublished">2018-07-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/07/27/程序中断方式/">程序中断方式</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>计算机在执行程序得过程中,当出现异常情况或特殊请求时,计算机停止现行程序((正在运行的程序)),转向对这些异常情况或特殊请求得处理,处理后再返回到现行程序的间断处。</p>
<p>由于CPU和IO设备速度的不匹配,CPU通常要等待一段时间,才能实现主机与IO设备之间的信息交换。所以当CPU启动了IO设备之后,就转去执行现行程序,让IO设备准备好后主动通知(中断)CPU,CPU转去执行中断程序,进行数据交换。这就是IO中断。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2FIO%E4%B8%AD%E6%96%AD%E6%89%93%E5%8D%B0%E6%9C%BA%E7%A4%BA%E6%84%8F%E5%9B%BE.png" alt="打印机的中断"></p>
<h1 id="中断方式的电路"><a href="#中断方式的电路" class="headerlink" title="中断方式的电路"></a>中断方式的电路</h1><h2 id="中断请求触发器和中断屏蔽触发器"><a href="#中断请求触发器和中断屏蔽触发器" class="headerlink" title="中断请求触发器和中断屏蔽触发器"></a>中断请求触发器和中断屏蔽触发器</h2><p>每台外部设备都必须配置一个中断请求触发器INTR,当其为“1”时,表示该设备向CPU提出中断请求。同时也需要一个配对的 <strong>中断屏蔽器</strong>,用于屏蔽一些低优先级的中断请求(面对多个中断请求时),当它为“1”时,表示封锁其中断源。其电路逻辑图如下所示</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2FD%E3%80%81INTR%E3%80%81MASK%E4%B8%89%E8%80%85%E7%9A%84%E4%BF%A1%E6%81%AF%E5%85%B3%E7%B3%BB.png" alt="接口电路中D、INTR、MASK和中断查询信号的关系"></p>
<p>当设备准备就绪D为“1”、且该设备MASK不为“0”时,由CPU在指令执行结束阶段时,由CPU发出中断查询信号,将INTR设置为“1”</p>
<h2 id="排队器"><a href="#排队器" class="headerlink" title="排队器"></a>排队器</h2><p>当多个中断源同时向CPU发出请求时,CPU会根据中断源的不同性质对其排队,给予不同等级的优先权,并按优先级等级给予响应。</p>
<blockquote>
<p>通常来说速度越快的IO设备,优先级越高,因为不及时响应高速的IO请求,信息可能会发生丢失。</p>
</blockquote>
<p>排队器分为硬件排队器和软件排队器:这里只介绍硬件排队器,下图是硬件排队器的电路</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E9%93%BE%E5%BC%8F%E6%8E%92%E9%98%9F%E5%99%A8.png" alt="链式排队器"></p>
<p>当各个中断源无请求时,各个INTR<sup>-</sup>均为高电平。一旦某个中断源提出中断请求时,就会让优先级低的中断源变为低电平,封锁其中的中断请求。比如当2、3号中断源同时有请求时(INTR2<sup>-</sup>、INTR3<sup>-</sup>均为0),流程如图所示</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E9%93%BE%E5%BC%8F%E6%8E%92%E9%98%9F%E5%99%A8%E4%B8%BE%E4%BE%8B%E5%9B%BE.png" alt="链式排队器距离图"></p>
<p>INTR<sub>i</sub><sup>-</sup> 没有提出请求时均为高电平,根据例子,二、三号中断源提出请求,二、三号输入低电压,由于二号经过与非门输出高电压,经过三号的非门,输出低电压,让三号无法被选。此时因为INTP<sub>1</sub><sup>‘</sup>和INTP<sub>2</sub><sup>‘</sup>输出都为高电压,又通过INTP<sub>i</sub>来和输出信号进行与运算,最终输出INTP<sub>2</sub></p>
<h2 id="中断向量地址形成部件(设备编址器)"><a href="#中断向量地址形成部件(设备编址器)" class="headerlink" title="中断向量地址形成部件(设备编址器)"></a>中断向量地址形成部件(设备编址器)</h2><p>CPU一旦响应了IO中断,就要暂停现行程序,转去执行该设备的中断服务程序。不同的设备有不同的中断服务程序,每个服务程序都有一个入口,而CPU需要通过设备编址码找到中断服务程序实际所在的位置。</p>
<p>设备编址码的输入是来自排队器的输出INTP<sub>i</sub>,它的输出是中断源的位置</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E8%AE%BE%E5%A4%87%E7%BC%96%E7%A0%81%E5%99%A8.png" alt="设备编码器"></p>
<p>比如编址器输出12H,CPU就能从主存的12H的位置查找(假设是统一编址),找到对应的中断服务程序的入口地址</p>
<h1 id="IO中断处理过程"><a href="#IO中断处理过程" class="headerlink" title="IO中断处理过程"></a>IO中断处理过程</h1><h2 id="中断的条件和时间"><a href="#中断的条件和时间" class="headerlink" title="中断的条件和时间"></a>中断的条件和时间</h2><ol>
<li>CPU中的EINT(允许中断触发器)为“1”,该触发器用开中断置位,用关中断使其复位</li>
<li>CPU响应中断的时间一定是在每条指令执行阶段的结束时刻</li>
</ol>
<p>接下来以输入设备为例,结合流程示意图进行讲解,流程图如下所示<br><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E7%A8%8B%E5%BA%8F%E4%B8%AD%E6%96%AD%E6%8E%A5%E5%8F%A3%E7%94%B5%E8%B7%AF%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%BB%84%E6%88%90.png" alt="中断流程图"></p>
<p>当选中设备后,设备选择电路发出SEL信号</p>
<ol>
<li>CPU发出启动命令,将触发器B置为“1”,D为“0”</li>
<li>接口启动输入设备,设备开始工作</li>
<li>IO设备将数据送入数据缓冲寄存器</li>
<li>当输入设备发出设备工作结束信号后,D被置为“1”,B被置为“0”,标志设备准备就绪</li>
<li>当设备准备就绪,且本设备未被屏蔽时,在指令执行结束阶段,CPU会发出中断查询信号</li>
<li>设备中断请请求触发器INTR被置为“1”,向CPU提出中断请求,INTR送至排队器,进行中断判优</li>
<li>若CPU允许中断(ENIT=1),设备又被排队选中,即进入中断响应阶段,由中断响应信号INTA将排队器输出送至编码器形成的向量地址</li>
<li>向量地址送至CPU,作为下一条指令的地址</li>
<li>向量地址里存放的是无条件转移指令(看中断流程图),待转移指令执行完毕,就转去对应设备的服务程序入口地址,开始执行中断服务程序,通过输入指令将数据缓冲寄存器的输入数据送至CPU的通用寄存器,再存入主存相关单元</li>
<li>中断服务程序的最后一条指令是中断返回指令,当其执行结束时,中断返回至原程序的断点处。</li>
</ol>
<h1 id="中断服务程序的流程"><a href="#中断服务程序的流程" class="headerlink" title="中断服务程序的流程"></a>中断服务程序的流程</h1><p>总共分为四个阶段</p>
<ol>
<li>保护现场</li>
<li>中断服务</li>
<li>恢复现场</li>
<li>中断返回</li>
</ol>
<h2 id="保护现场"><a href="#保护现场" class="headerlink" title="保护现场"></a>保护现场</h2><p>其一是保存程序的断点,其二是保存通用寄存器和状态寄存器的内容。前者涉及到中断隐指令,后者由中断服务程序完成。中断服务程序会在程序的起始部分安排若干条存数指令,将寄存器的内容存至存储器中保存;或者用进栈指令将各个寄存器的内容压入堆栈中保存。</p>
<h2 id="中断服务"><a href="#中断服务" class="headerlink" title="中断服务"></a>中断服务</h2><p>不同的设备中断服务不一样,比如打印机要求CPU将需要打印的字符代码送入打印机的缓冲器内;显示器设备要求CPU将需要显示的字符代码送入显示存储器中</p>
<h2 id="恢复现场"><a href="#恢复现场" class="headerlink" title="恢复现场"></a>恢复现场</h2><p>在中断服务程序退出前,将中断的现行程序恢复回来。通常可用取数指令或出栈指令,将保存在存储器中的信息送回到原来的寄存器中</p>
<h2 id="中断返回"><a href="#中断返回" class="headerlink" title="中断返回"></a>中断返回</h2><p>中断服务程序的最后一条指令通常是一条中断返回指令,使其返回到原程序的断点处</p>
<h2 id="单重中断和多重中断"><a href="#单重中断和多重中断" class="headerlink" title="单重中断和多重中断"></a>单重中断和多重中断</h2><p>当CPU响应了一个中断源的中断请求后,EINT会被置为“0”,无法让其他中断源继续请求。对于单重中断,在保护完现场后,即刻去执行设备服务,直到恢复完现场后,才将EINT置为“1”并返回。而对于多重中断来说,在保护完现场后,就立刻将EINT设置为“1”,允许其他中断源插入中断请求。示意图如下所示</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E5%8D%95%E9%87%8D%E4%B8%AD%E6%96%AD%E5%92%8C%E5%A4%9A%E9%87%8D%E4%B8%AD%E6%96%AD.png" alt="单重中断和多重中断"></p>
<p>多重中断就和方法里面再调用其他方法一样,如下图所示<br><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E5%A4%9A%E9%87%8D%E4%B8%AD%E6%96%AD%E5%B5%8C%E5%A5%97%E8%B0%83%E7%94%A8.png" alt="嵌套调用"></p>
<h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>从宏观上看,中断方式克服了程序查询方式的原地踏步,CPU和IO设备是并行操作的,提高了CPU的资源利用率</p>
<p>从微观上看,CPU在处理中断服务程序时仍然需要暂停原程序的运行,尤其是当高速IO或辅助存储器频繁地与主存交换信息时,需要不断打断CPU执行主程序而执行中断服务程序。</p>
<p><img src="https://blog-1252749790.cos.ap-shanghai.myqcloud.com/ComputerOrganization%2FIOSystem%2F%E4%B8%BB%E7%A8%8B%E5%BA%8F%E5%92%8C%E6%9C%8D%E5%8A%A1%E7%A8%8B%E5%BA%8F%E6%8A%A2%E5%8D%A0CPU.png" alt="主程序和服务程序抢占CPU资源"></p>
<p>为了完善中断程序,人们提出了DMA控制方式。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/07/27/程序中断方式/" data-id="cjkgaepnu003blcvj9yeu8j8c" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-程序查询方式" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/07/27/程序查询方式/" class="article-date">
<time datetime="2018-07-27T01:28:43.000Z" itemprop="datePublished">2018-07-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/07/27/程序查询方式/">程序查询方式</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h1><p>程序查询方式的核心问题在于 <strong>每时每刻不断查询IO设备是否准备就绪</strong>,查询流程图如下所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%A8%8B%E5%BA%8F%E6%9F%A5%E8%AF%A2%E6%96%B9%E5%BC%8F.png" alt="程序查询方式"></p>
<p>当IO设备比较多的时候,CPU会按照各个IO设备的优先级逐个查询,流程图如下所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E5%A4%9AIO%E8%AE%BE%E5%A4%87%E7%9A%84%E6%9F%A5%E8%AF%A2%E6%96%B9%E5%BC%8F.png" alt="多IO设备的查询方式"></p>
<h1 id="程序查询流程"><a href="#程序查询流程" class="headerlink" title="程序查询流程"></a>程序查询流程</h1><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%A8%8B%E5%BA%8F%E6%9F%A5%E8%AF%A2%E6%B5%81%E7%A8%8B%E5%9B%BE.png" alt="程序查询流程图"></p>
<ol>
<li>CPU先保存自己的寄存器情况</li>
<li>设置计数器,即要读取多少次</li>
<li>设置要存放的位置的起始位置</li>
<li>启动IO设备</li>
<li>获取IO设备状态标志到CPU,检查IO设备是否准备就绪:如果准备就绪,开始读取数据,反之原地踏步</li>
<li>CPU执行IO指令,或从IO接口中的数据缓冲寄存器中读取一个数据,或者写入数据到IO接口的数据缓冲寄存器内</li>
<li>修改主存地址</li>
<li>修改计数器(比如从正数递减到0,或从负数递增到0)</li>
<li>结束IO传输</li>
</ol>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/07/27/程序查询方式/" data-id="cjkgaepnv003dlcvjxf4bz1r5" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-输入输出系统概述" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/07/25/输入输出系统概述/" class="article-date">
<time datetime="2018-07-25T05:38:56.000Z" itemprop="datePublished">2018-07-25</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/07/25/输入输出系统概述/">输入输出系统之概述</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="I-O设备的发展"><a href="#I-O设备的发展" class="headerlink" title="I/O设备的发展"></a>I/O设备的发展</h1><h2 id="第一阶段"><a href="#第一阶段" class="headerlink" title="第一阶段"></a>第一阶段</h2><p>早期计算机采用 <strong>分散连接(指计算机各个部件间相连的方式)</strong> 的结构,如下图所示。每一个部件之间都需要连接。</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E5%88%86%E6%95%A3%E8%BF%9E%E6%8E%A5.png" alt="分散连接"></p>
<p>以下是分散连接的抽象图</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/CPU%E5%92%8CIO%E8%AE%BE%E5%A4%87%E7%9A%84%E8%81%94%E7%B3%BB%E7%AC%AC%E4%B8%80%E9%98%B6%E6%AE%B5.png" alt="CPU和IO设备的联系-第一阶段"></p>
<p>缺点:</p>
<ol>
<li>由于一个I/O设备需要一个专用的逻辑控制电路才能连接使用,所以线路会很多很繁琐。</li>
<li>该结构下对I/O设备的读写基本都是串行的,会严重影响CPU的运行速度</li>
<li>由于I/O设备和CPU之间的耦合性过于紧密,所以对于设备的增减过于麻烦</li>
</ol>
<h2 id="第二阶段"><a href="#第二阶段" class="headerlink" title="第二阶段"></a>第二阶段</h2><p>这个阶段计算机采用 <strong>总线连接</strong>的结构,(这里关注点在于I/O设备,所以拿单总线结构距离)。如下图所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E6%80%BB%E7%BA%BF%E8%BF%9E%E6%8E%A5%E4%B9%8B%E5%8D%95%E6%80%BB%E7%BA%BF%E8%BF%9E%E6%8E%A5.png" alt="单总线连接"></p>
<p>这个阶段I/O设备通过 <strong>接口</strong> 挂在在总线上与CPU相连。I/O接口通常包含 <strong>数据通路</strong>、 <strong>控制通路</strong> 两个部分:</p>
<ul>
<li>数据通路:用于缓冲数据,可以进行串-并转换,匹配I/O设备和CPU的速度。</li>
<li>控制通路:用于接收I/O指令</li>
</ul>
<p>这个阶段相比第一阶段,补足了缺点,并让CPU和I/O设备并发处理的时间更多,但是还不能做到完全的并发处理</p>
<h2 id="第三阶段"><a href="#第三阶段" class="headerlink" title="第三阶段"></a>第三阶段</h2><p>这个阶段更适用于I/O设备数量繁多的大型计算机,即在CPU和I/O接口之间又加入了 <strong>通道</strong>,通道用于负责管理整个I/O过程。当通道接收到CPU的指令后,先从主存中取出通道程序,然后单独执行,直至I/O设备输入或输出完毕,再通知CPU完成。这个过程不需要CPU参与,就大大提高了CPU和I/O设备的并行能力。</p>
<h2 id="第四阶段"><a href="#第四阶段" class="headerlink" title="第四阶段"></a>第四阶段</h2><p>采用I/O处理机,I/O处理机又称 <strong>外围处理机</strong>,其相当于一个小型CPU,基本是独立于CPU进行工作的</p>
<h1 id="输入输出系统的组成"><a href="#输入输出系统的组成" class="headerlink" title="输入输出系统的组成"></a>输入输出系统的组成</h1><p>输入输出系统主要包含两个部分:I/O软件、I/O硬件</p>
<h2 id="I-O软件"><a href="#I-O软件" class="headerlink" title="I/O软件"></a>I/O软件</h2><h3 id="I-O指令"><a href="#I-O指令" class="headerlink" title="I/O指令"></a>I/O指令</h3><p>I/O指令时CPU指令集的一部分,如下图所示<br><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/IO%E6%8C%87%E4%BB%A4%E7%9A%84%E4%B8%80%E8%88%AC%E6%A0%BC%E5%BC%8F.png" alt="I/O指令"></p>
<ul>
<li>操作码:标明该指令是 I/O指令还是其他指令</li>
<li>命令码: 标明该指令具体做什么事,比如从I/O设备取出数据到CPU里、或者将数据送到I/O设备里</li>
<li>设备码:标明要处理的目标对象</li>
</ul>
<h3 id="通道指令"><a href="#通道指令" class="headerlink" title="通道指令"></a>通道指令</h3><p>通道指令不是CPU指令集的一部分,通道指令是为拥有通道的I/O系统专门设置的指令,这类指令结构通常如下图所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E9%80%9A%E9%81%93%E6%8C%87%E4%BB%A4.png" alt="通道指令"></p>
<p>该指令通常存放再主存中,当需要运行时,由通道从主存中取出并执行。通道程序即由通道指令组成,它完成了某种外围设备与主存之间传送信息的操作。</p>
<h3 id="区别"><a href="#区别" class="headerlink" title="区别"></a>区别</h3><p>通道是通道自身的指令,用来执行I/O操作;<br>I/O指令是CPU指令系统的一部分,是CPU用来控制输入输出的操作命令。<br>在有通道结构的计算机中,I/O指令不实现I/O数据传输,主要是用于启动、暂停I/O设备;或者查询通道和I/O设备的状态及控制通道所做的其他操作。</p>
<p>一旦CPU启动了I/O设备后,就由通道接管CPU对I/O设备的管理。</p>
<h2 id="I-O硬件"><a href="#I-O硬件" class="headerlink" title="I/O硬件"></a>I/O硬件</h2><p>通常包括接口模块和I/O设备两大部分</p>
<h1 id="I-O设备与主机的联系方式"><a href="#I-O设备与主机的联系方式" class="headerlink" title="I/O设备与主机的联系方式"></a>I/O设备与主机的联系方式</h1><p>这个部分主要解决三个部分:</p>
<ul>
<li>设备地址</li>
<li>设备选择</li>
<li>传送方式</li>
<li>CPU和设备间的传输方式</li>
</ul>
<h2 id="设备地址"><a href="#设备地址" class="headerlink" title="设备地址"></a>设备地址</h2><ul>
<li><p>统一编址:<br>将主存划分一部分地址出来作为设备地址,比如在64K地址的存储空间中,划出8K地址作为I/O设备的地址,只要访问的地址在这8K范围内,就是对I/O设备的访问。</p>
</li>
<li><p>单独编址<br>单独设置一个存储空间作为I/O设备的地址,使用专用的指令来存储。</p>
</li>
</ul>
<h2 id="设备选择"><a href="#设备选择" class="headerlink" title="设备选择"></a>设备选择</h2><p>用设备选择电路识别是否被选中</p>
<h2 id="传送方式"><a href="#传送方式" class="headerlink" title="传送方式"></a>传送方式</h2><ul>
<li>串行</li>
<li>并行</li>
</ul>
<h2 id="联络方式"><a href="#联络方式" class="headerlink" title="联络方式"></a>联络方式</h2><h3 id="立即响应"><a href="#立即响应" class="headerlink" title="立即响应"></a>立即响应</h3><p>信号一到,立即响应</p>
<h3 id="异步工作-应答信号"><a href="#异步工作-应答信号" class="headerlink" title="异步工作 + 应答信号"></a>异步工作 + 应答信号</h3><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/IO%E8%AE%BE%E5%A4%87%E8%AF%BB%E6%B5%81%E7%A8%8B%E4%B9%8B%E5%BC%82%E6%AD%A5%E5%BA%94%E7%AD%94%E8%81%94%E7%BB%9C.png" alt="I/O设备读流程"></p>
<ol>
<li>在读过程中,I/O接口发送 <code>READY</code> 信号 给 I/O设备</li>
<li>I/O设备收到信号后,从I/O接口中取出数据,并回应一个 <code>Strobe</code> 信号给I/O接口</li>
</ol>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/IO%E5%86%99%E6%B5%81%E7%A8%8B%E4%B9%8B%E5%BC%82%E6%AD%A5%E5%BA%94%E7%AD%94%E8%81%94%E7%BB%9C.png" alt="I/O设备写流程"></p>
<ol>
<li>在写过程中,I/O设备发送数据到I/O接口中,并发送信号<code>Strobe</code></li>
<li>I/O接口收到信号后,通知CPU中断处理,然后返回<code>READY</code>信号给I/O设备</li>
</ol>
<h3 id="同步工作采用同步时钟"><a href="#同步工作采用同步时钟" class="headerlink" title="同步工作采用同步时钟"></a>同步工作采用同步时钟</h3><h1 id="I-O设备的控制方式"><a href="#I-O设备的控制方式" class="headerlink" title="I/O设备的控制方式"></a>I/O设备的控制方式</h1><h2 id="程序查询控制"><a href="#程序查询控制" class="headerlink" title="程序查询控制"></a>程序查询控制</h2><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%A8%8B%E5%BA%8F%E6%9F%A5%E8%AF%A2%E6%96%B9%E5%BC%8F.png" alt="程序查询方式"></p>
<p>该方式主要在于CPU读I/O设备状态,然后检查状态是否就绪,如果没有就绪就循环查询。该查询方式会消耗很多时间在状态检查上(一直检查),同时还会花费部分时间在数据交互上。</p>
<h2 id="程序中断控制"><a href="#程序中断控制" class="headerlink" title="程序中断控制"></a>程序中断控制</h2><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%A8%8B%E5%BA%8F%E4%B8%AD%E6%96%AD%E6%96%B9%E5%BC%8F.png" alt="程序中断"></p>
<p>当CPU执行原程序时,如果遇到了I/O指令启动了I/O设备后,等待I/O接口数据缓冲区满了后,再通知CPU中断,执行中断服务程序</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%A8%8B%E5%BA%8F%E4%B8%AD%E6%96%AD%E6%96%B9%E5%BC%8F%E6%B5%81%E7%A8%8B%E5%9B%BE.png" alt="程序中断流程图"></p>
<p>这个控制方式相比中断方式,CPU不需要长时间轮询状态,只需要启动I/O设备后,就能转头去做其他事情。直到I/O设备发回中断请求,CPU停止做事情,转去执行中断服务程序(交换信息)。</p>
<h2 id="DMA控制"><a href="#DMA控制" class="headerlink" title="DMA控制"></a>DMA控制</h2><p>由于程序中断在数据交换过程仍然要CPU参与,会在一定程度上影响CPU速度。</p>
<p>DMA控制主要通过以下三个手段实现:</p>
<ol>
<li>主存和I/O之间架一条通路</li>
<li>不中断现行程序</li>
<li>周期挪用</li>
</ol>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/07/25/输入输出系统概述/" data-id="cjkgaepo6003slcvjrz43fq1i" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-IO接口" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/07/25/IO接口/" class="article-date">
<time datetime="2018-07-25T05:38:56.000Z" itemprop="datePublished">2018-07-25</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/07/25/IO接口/">IO接口</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>接口可以是两个部件之间连接的电路,IO接口通常是指CPU和IO设备间的硬件电路和控制软件,而不同的IO设备通常都通过IO接口和CPU取得联系的。</p>
<p>IO接口的功能有如下几个:</p>
<ol>
<li>实现IO设备的选择</li>
<li>匹配IO设备和CPU的速度</li>
<li>实现数据的串-并转换</li>
<li>实现电平的转换</li>
<li>发送控制信号</li>
<li>保存状态信息,供CPU进行查询</li>
</ol>
<p>这里举个四功能的接口,看看它的功能和组成</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%8A%9F%E8%83%BD%E5%92%8C%E7%BB%84%E6%88%90.png" alt="接口的功能和组成"></p>
<h1 id="组成介绍"><a href="#组成介绍" class="headerlink" title="组成介绍"></a>组成介绍</h1><p>设备选择线:<br>该线是用于传输设备码的,它的根数取决于I/O指令设备码的位数。</p>
<p>命令线:<br>该线是单项的,通常由CPU发出,用于传输CPU的I/O指令</p>
<p>状态线:<br>反馈IO设备的状态,该线也是单项的,通常</p>
<p>数据线:<br>双向的,用于传输数据,其根数一般等于存储字长的长度</p>
<h1 id="功能介绍"><a href="#功能介绍" class="headerlink" title="功能介绍"></a>功能介绍</h1><h2 id="选址功能"><a href="#选址功能" class="headerlink" title="选址功能"></a>选址功能</h2><p>IO总线与所有设备的接口相连,但CPU究竟选择哪台设备,还得通过设备选择线上的设备码来确定。</p>
<p>每个接口会带有选址功能,当设备线上的设备码与I/O设备的设备码一致时,应发出设备选中信号SEL。</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E8%AE%BE%E5%A4%87%E9%80%89%E6%8B%A9.png" alt="设备选择"></p>
<h2 id="传送命令功能"><a href="#传送命令功能" class="headerlink" title="传送命令功能"></a>传送命令功能</h2><p>当CPU向IO设备发出命令时,要求IO设备能做出响应。如果设备不具备传送命令的信息功能,设备将无法响应。所以通常在IO接口中设有存放命令的寄存器以及命令译码器。</p>
<p>命令寄存器用来存放IO指令中的指令码,只有当设备选择电路有效,SEL信号有效,才能存放命令道寄存器中。</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E5%91%BD%E4%BB%A4%E5%AF%84%E5%AD%98%E5%99%A8%E5%92%8C%E8%AF%91%E7%A0%81%E5%99%A8.png" alt="命令寄存器"></p>
<h2 id="传送数据的功能"><a href="#传送数据的功能" class="headerlink" title="传送数据的功能"></a>传送数据的功能</h2><p>IO设备和CPU之间通过接口的数据通路进行数据交互,接口还会有数据缓冲的功能,它用来暂存IO设备和CPU间要交换的数据。数据通路的位数取决于设备的种类。</p>
<h2 id="IO设备工作状态"><a href="#IO设备工作状态" class="headerlink" title="IO设备工作状态"></a>IO设备工作状态</h2><p>为了使CPU能时刻了解IO设备的运行状况,接口通常会设置一些反映设备状态的触发器。<br>比如工作触发器B、完成触发器D、中断触发器INTR,屏蔽触发器MASK</p>
<ul>
<li>D = 0, B = 0时,表示设备处于暂停</li>
<li>D = 1, B = 0时,表示设备已准备就绪</li>
<li>D = 0, B = 1时,表示设备处于准备状态</li>
</ul>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/IO%E6%8E%A5%E5%8F%A3%E7%9A%84%E5%9F%BA%E6%9C%AC%E7%BB%84%E6%88%90.png" alt="IO接口的基本组成"></p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/07/25/IO接口/" data-id="cjkgaepk9000alcvjwp33cyz8" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-存储器(九)之高速缓冲器(下)" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/07/22/存储器(九)之高速缓冲器(下)/" class="article-date">
<time datetime="2018-07-22T07:15:42.000Z" itemprop="datePublished">2018-07-22</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/07/22/存储器(九)之高速缓冲器(下)/">存储器之高速缓冲器(Cache)(下)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h1><p>CPU将主存地址 转换成 Cache地址时,需要经过 <strong>地址映射变换机构</strong>。不同层级的Cache 对 <strong>地址映射的算法方案</strong> 有不同的要求。</p>
<h1 id="主存Cache地址映射变换机构"><a href="#主存Cache地址映射变换机构" class="headerlink" title="主存Cache地址映射变换机构"></a>主存Cache地址映射变换机构</h1><p>该机构主要是 将主存地址 按一定算法映射到 Cache地址</p>
<h2 id="直接映射"><a href="#直接映射" class="headerlink" title="直接映射"></a>直接映射</h2><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/Cache%E7%9B%B4%E6%8E%A5%E6%98%A0%E5%B0%84.png" alt="直接映射"></p>
<h3 id="映射算法"><a href="#映射算法" class="headerlink" title="映射算法"></a>映射算法</h3><p>假设主存的块数为 <code>m</code>,Cache的块数为 <code>c</code>。那么通常会按 <code>c</code>个块为一组划分主存,每组的块编号依次按 <code>0,1,2,...,c</code>进行编号。假设m为16,c为2,其示意图如下所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%9B%B4%E6%8E%A5%E6%98%A0%E5%B0%84%E5%9B%BE%E4%B8%80.png" alt="直接映射图一"></p>
<p>每组的编号(每组都从0开始计算),组0的块0、组1的块0(整体来看即块2)都对应着Cache的块0。也就是说,<strong>每组的第一个块</strong> 都必须存放在 <strong>Cache的第一个块里</strong>,同理 <strong>每组的第二块</strong> 都必须存放在 <strong>Cache第二个块里</strong>。示意图如下所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%9B%B4%E6%8E%A5%E6%98%A0%E5%B0%84%E5%9B%BE%E4%BA%8C.png" alt="直接映射图二"></p>
<p>用数学来表示映射算法即 <code>z = i % c</code>,i表示块编号,c表示cache的总块数(每组的块数)</p>
<p>比如:</p>
<ol>
<li>3 % 2 = 1</li>
<li>2 % 2 = 0</li>
</ol>
<h3 id="映射流程"><a href="#映射流程" class="headerlink" title="映射流程"></a>映射流程</h3><p>CPU发送来一个主存地址,该地址会分为三部分,一部分是主存组号,一部分是组内编号,一部分是块内地址,主存组号和组内编号共同组成块号。 如下图所示</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/CPU%E5%8F%91%E9%80%81%E7%9A%84%E4%B8%BB%E5%AD%98%E5%9C%B0%E5%9D%80.png" alt="CPU发送的主存地址格式"></p>
<ol>
<li>当Cache收到CPU发送的主存地址时,提取组内编号</li>
<li>查找Cache中 组内编号指示 的块</li>
<li>将块中的标识提取出来(此处的标识保存着主存的组号)</li>
<li>比较Cache块提供的标识 和 主存提供的组号</li>
<li>如果组号一致,则进入下个判断;否则不命中</li>
<li>判断该块的有效位是否为“1”,如果为“1”,则命中;反之不命中</li>
<li>如果判断为不命中,Cache就从主存里取出新的字块,并替换旧字块,最后将该块的有效位置为“1”</li>
</ol>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>直接映射的缺点就是不够灵活,因为每个主存块都对应着唯一的Cache块,如果CPU频繁访问 <strong>不同组的同编号</strong>块,那么Cache就经常会发生替换,影响速度,空间利用率不高</p>
<h2 id="全相联映射"><a href="#全相联映射" class="headerlink" title="全相联映射"></a>全相联映射</h2><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E5%85%A8%E7%9B%B8%E8%81%94%E6%98%A0%E5%B0%84.png" alt="全相联映射"></p>
<h3 id="映射算法-1"><a href="#映射算法-1" class="headerlink" title="映射算法"></a>映射算法</h3><p>主存内每个字块都可以被存储到Cache的任意字块内。</p>
<h3 id="映射流程-1"><a href="#映射流程-1" class="headerlink" title="映射流程"></a>映射流程</h3><p>当CPU发出如下图所示的地址时,</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E5%85%A8%E7%9B%B8%E8%81%94%E6%98%A0%E5%B0%84CPU%E5%8F%91%E9%80%81%E7%9A%84%E5%9C%B0%E5%9D%80.png" alt="全相联映射地址"></p>
<ol>
<li>CPU通过主存块编号遍历Cache,查找Cache的标记是否和该主存编号相同</li>
<li>如果找到则命中,未找到则未命中</li>
</ol>
<h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><p>全相联映射更加灵活,主存块可以保存到Cache的任意块中。Cache的利用率就会高,但是由于遍历,速度往往较低。</p>
<h2 id="组相联映射"><a href="#组相联映射" class="headerlink" title="组相联映射"></a>组相联映射</h2><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%BB%84%E7%9B%B8%E8%81%94%E6%98%A0%E5%B0%84.png" alt="组相联映射"></p>
<h3 id="映射算法-2"><a href="#映射算法-2" class="headerlink" title="映射算法"></a>映射算法</h3><p>主存划分为多个组,缓存也划分为多个组,每个组里的块编号都对应着缓存里的组编号。简而言之,原来的直接映射只能一个缓存块对应多个主存块,为了提高空间利用率,允许一个多个同组的缓存块对应多个主存块。示意图如下所示<br><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/%E7%BB%84%E7%9B%B8%E8%81%94%E6%98%A0%E5%B0%84%E5%9B%BE%E4%B8%80.png" alt="组相联映射一"></p>
<p>每个组的第一个块都对应着缓存的第一个组。同理,每个组的第二块都对应着缓存的第二块。</p>
<h3 id="映射流程-2"><a href="#映射流程-2" class="headerlink" title="映射流程"></a>映射流程</h3><p>每个组的第一块 都能放到缓存的第一个组内的任意位置。一方面,主存的块可以放在对应组内的任意一个位置,体现出了全相联的思想;另一方面,主存的每个块都对应着唯一一个组,这是直接映射的思想。</p>
<p>该方案是直接映射和全相联映射的折中方案。</p>
<h1 id="替换机构"><a href="#替换机构" class="headerlink" title="替换机构"></a>替换机构</h1><p>替换就是指 <strong>Cache满了之后,如果要存放新的主存块,就需要将某个Cache块取出,并替换新的主存块进入</strong></p>
<blockquote>
<p>主存块就像是放在书架上的书,缓存块就像是放在床头的书,如果床头的书满了,就需要找个方案取出一本书,然后将想看的书加进去</p>
</blockquote>
<p>替换机构就是做了 <strong>如何取书</strong> 这件事</p>
<p>目前有两种方案:</p>
<ul>
<li>先进先出FIFO</li>
<li>近期最少使用(Least Recently Used)<br> LRU算法比较好的利用访存局部性原理,替换出近期用的最少的字块。通常是只记录每个快最近一次使用的时间。</li>
<li>随机法<br> 取一个随机数,没有根据访存的局部性原理。</li>
</ul>
<p>注意,这里讲到的访存局部性原理是指 <strong>只访问某个块的内容,而不是全部的内容,即局部性</strong></p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/07/22/存储器(九)之高速缓冲器(下)/" data-id="cjkgaepnb002rlcvj63cr1859" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<article id="post-存储器(八)之高速缓冲器(上)" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/codeleven.github.io/2018/07/20/存储器(八)之高速缓冲器(上)/" class="article-date">
<time datetime="2018-07-20T07:46:19.000Z" itemprop="datePublished">2018-07-20</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/codeleven.github.io/2018/07/20/存储器(八)之高速缓冲器(上)/">存储器之高速缓冲器(Cache)(上篇)</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="概要"><a href="#概要" class="headerlink" title="概要"></a>概要</h1><p>目前CPU遇到以下两个问题:</p>
<ol>
<li>当 <strong>I/O设备向主存请求</strong>时,<strong>CPU无法访问主存</strong>,只能空等着</li>
<li><strong>主存速度的提高</strong> 始终跟不上 <strong>CPU的发展</strong></li>
</ol>
<p>为了避免CPU和I/O设备争抢方寸,可在CPU与主存之间加一级Cache,此时CPU只需要跟Cache交互即可;另一方面,添加Cache还能解决主存与CPU速度的不匹配。</p>
<h1 id="问题的提出"><a href="#问题的提出" class="headerlink" title="问题的提出"></a>问题的提出</h1><ol>
<li>为什么添加一个Cache可以解决主存与CPU速度的不匹配问题?</li>
<li>既然Cache速度这么快,为什么不用 Cache 替换 主存?</li>
<li>Cache的工作原理?</li>
<li>如何判断Cache的性能?</li>
<li>Cache的性能由哪些方面决定?</li>
<li>CPU、Cache、主存间是如何进行交互的?</li>
</ol>
<h2 id="为什么添加一个Cache可以解决主存与CPU速度的不匹配问题"><a href="#为什么添加一个Cache可以解决主存与CPU速度的不匹配问题" class="headerlink" title="为什么添加一个Cache可以解决主存与CPU速度的不匹配问题"></a>为什么添加一个Cache可以解决主存与CPU速度的不匹配问题</h2><p>因为经过数据分析,发现代码执行时是存在 <strong>局部性</strong>的。<br>局部性主要分成两部分:<strong>时间局部性</strong>、<strong>空间局部性</strong>。</p>
<ul>
<li>时间局部性:CPU从主存取指令或取数据时,在一定时间内,可能会对同个块重复访问。</li>
<li>空间局部性:由于数据在主存内都是连续存放的,所以很可能下一个即将访问的指令也在同一个区域内。</li>
</ul>
<p>根据以上两点,只要将CPU近期要用到的程序和数据提前存到Cache中,那么CPU在一定时间内,只需要访问Cache即可。</p>
<h2 id="既然Cache速度这么快,为什么不用-Cache-替换-主存"><a href="#既然Cache速度这么快,为什么不用-Cache-替换-主存" class="headerlink" title="既然Cache速度这么快,为什么不用 Cache 替换 主存"></a>既然Cache速度这么快,为什么不用 Cache 替换 主存</h2><p>因为Cache一般采用SRAM制作,其价格比主存昂贵。</p>
<h2 id="Cache的工作原理"><a href="#Cache的工作原理" class="headerlink" title="Cache的工作原理"></a>Cache的工作原理</h2><p>主存由2<sup>n</sup>个可编址的字组成,每个字有惟一的n位地址。<br>为了与Cache映射,将主存与缓存都分成若干 <strong>块</strong>,每个块内又包含多个 <strong>字</strong>,并使他们的块大小相同(即块内字数相同)。</p>
<p>这就将主存的地址分成了两端:<strong>主存块号</strong>、<strong>块内地址</strong>。<strong>主存块号的位数 + 块内地址的位数 = 一个字的位数</strong>。</p>
<p>除此之外,Cache中有一个“标记”。标记是用来表示当前存放的是 <strong>哪一块主存块</strong>。设置这个标记是因为 Cache块 <strong>远比</strong> 主存块 <strong>少</strong>,所以Cache里停留的内容不会是固定的,需要一个东西标志 某个缓存块对应的主存块。</p>
<blockquote>
<p>不是主存地址可以通过转换获取缓存地址吗,为什么还需要标志位?因为Map需要的时间是最少的。</p>
</blockquote>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/Cache-%E4%B8%BB%E5%AD%98%E5%AD%98%E5%82%A8%E7%A9%BA%E9%97%B4.png" alt="Cache-主存原理"></p>
<h2 id="Cache的性能指标"><a href="#Cache的性能指标" class="headerlink" title="Cache的性能指标"></a>Cache的性能指标</h2><p>判断Cache好坏的指标有三个:</p>
<ul>
<li>Cache的命中率</li>
<li>Cache的平均访问时间</li>
<li>Cache的访问效率</li>
</ul>
<h3 id="Cache的命中率"><a href="#Cache的命中率" class="headerlink" title="Cache的命中率"></a>Cache的命中率</h3><p><code>Cache命中率 = 命中数 / (命中数 + 未命中数)</code></p>
<p>当CPU欲访问主存中的某块数据时,Cache中正好存在,则称为 <strong>缓存命中</strong><br>当CPU欲访问主存中的某块数据时,Cache中不存在,则称为 <strong>缓存不命中</strong></p>
<h3 id="平均访问时间"><a href="#平均访问时间" class="headerlink" title="平均访问时间"></a>平均访问时间</h3><p><code>平均访问时间 = 命中时访问时间 * 命中率 + (1 - 命中率) * 未命中时间</code></p>
<p><strong>平均访问时间</strong> 越接近 <strong>命中时访问时间</strong>越好</p>
<h3 id="访问效率"><a href="#访问效率" class="headerlink" title="访问效率"></a>访问效率</h3><p>在平均访问时间的基础上,扩充出访问效率,即<code>访问效率 = 平均访问时间 / 命中时访问时间</code></p>
<h2 id="Cache的性能由哪些方面决定"><a href="#Cache的性能由哪些方面决定" class="headerlink" title="Cache的性能由哪些方面决定"></a>Cache的性能由哪些方面决定</h2><p>通常来说,Cache 由 <strong>块的数量</strong>、<strong>块的长度</strong>决定的。</p>
<h3 id="Cache块的数量"><a href="#Cache块的数量" class="headerlink" title="Cache块的数量"></a>Cache块的数量</h3><p>因为Cache的块数量够多,达到主存的块数量,那么Cache除了第一次不命中,后面都是100%命中的。</p>
<h3 id="Cache块的长度"><a href="#Cache块的长度" class="headerlink" title="Cache块的长度"></a>Cache块的长度</h3><p>Cache的块长则需要具体情况具体分析,因为Cache块过短,保存的数据量不够多,可以被命中的字少了,那么命中率就下降了;而Cache块过长,缓存中块数减少,可装入的块就少了,很容易出现新块 覆盖 旧块,然后因为新块被使用次数不够多,最终导致命中率下讲;另外块过长,可能会导致一些不相关的数据保存进来,白白浪费空间。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>一般来说Cache块通常取4~8个字或字节,也可以取 一个主存周期所能获得的主存信息长度。</p>
<h2 id="CPU、Cache和主存间的交互"><a href="#CPU、Cache和主存间的交互" class="headerlink" title="CPU、Cache和主存间的交互"></a>CPU、Cache和主存间的交互</h2><p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/CPU%E3%80%81Cache%E3%80%81%E4%B8%BB%E5%AD%98%E9%97%B4%E7%9A%84%E4%BA%A4%E4%BA%92.png" alt="CPU、Cache和主存间的交互"></p>
<h3 id="Cache存储体"><a href="#Cache存储体" class="headerlink" title="Cache存储体"></a>Cache存储体</h3><p>Cache存储体以块为单位与主存交换信息,为了加速主存和Cache之间的调用速度,主存通常采用多体结构,且Cache访存的优先级最高。</p>
<h3 id="主存Cache地址映射变换机构"><a href="#主存Cache地址映射变换机构" class="headerlink" title="主存Cache地址映射变换机构"></a>主存Cache地址映射变换机构</h3><p>该机构是将CPU送来的主存地址转换为Cache地址。由于主存和Cache的块内地址都是一样的长度,因此地址映射变换机构主要是将 <strong>主存块号</strong> 转换成 <strong>Cache块号</strong>。如果Cache命中,则CPU可以直接访问Cache存储体;如果Cache未命中,CPU会直接访问主存,不仅将该字从主存中取出,同时将它(代指字)所在的主存块一并存入Cache内。如果Cache不可装进,就得采用替换策略;反正,可以直接装入Cache。</p>
<h3 id="替换机构"><a href="#替换机构" class="headerlink" title="替换机构"></a>替换机构</h3><p>当Cache内容已满时,需要根据一定的替换算法将Cache内的某个块移除,从而装入新的主存块。</p>
<p>注意:Cache对用户是透明得,用户编程时用到的地址是主存地址,用户根本不知道这些主存是否已调入Cache内。因为将主存块调入Cache的任务全由机器硬件自动完成。</p>
<h3 id="Cache的读写操作"><a href="#Cache的读写操作" class="headerlink" title="Cache的读写操作"></a>Cache的读写操作</h3><h4 id="Cache读"><a href="#Cache读" class="headerlink" title="Cache读"></a>Cache读</h4><p>Cache读如下图所示<br><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/Cache%E8%AF%BB%E6%B5%81%E7%A8%8B.png" alt="Cache的读操作"></p>
<p>当CPU发出主存地址后,先判断该字是否在Cache中。若命中,直接访问Cache,并将该字送至CPU;若未命中,一方面要访问主存,将该字传送给CPU,与此同时,要将该字所在的主存块装入Cache。</p>
<p>Cache写分为 <strong>写通</strong> 和 <strong>回写</strong>两种方法</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/Cache%E9%80%8F%E5%86%99.png" alt="Cache的写直达操作"><br>当CPU对某个Cache块内的数据进行修改时,也将对应的主存块的内容进行修改。</p>
<p><img src="https://blog-1252749790.file.myqcloud.com/ComputerOrganization/Cache%E5%86%99%E6%B5%81%E7%A8%8B.png" alt="Cache的回写操作"><br>当CPU对某个Cache块内的数据进行修改后,该块被标记为 <strong>浊</strong>。当Cache发生替换时,将浊的块写回主存中去。</p>
<p><em>注意,上述的写操作都只针对于单核处理去,对于多核处理器,需要考虑Cache一致性问题</em></p>
<h3 id="Cache的改进"><a href="#Cache的改进" class="headerlink" title="Cache的改进"></a>Cache的改进</h3><p>改进方法有以下两种:</p>
<ul>
<li>增加Cache级数</li>
<li>将统一的Cache变成分立的Cache</li>
</ul>
<h4 id="增加Cache级数"><a href="#增加Cache级数" class="headerlink" title="增加Cache级数"></a>增加Cache级数</h4><p>一开始CPU的Cache是直接集成在芯片内部的,这种Cache称为<strong>片内Cache(又或是L1 Cache)</strong>。片内Cache离CPU近,CPU也不需要占用系统总线就可以直接访问,速度极快。但是片内Cache没有相应数据块时,CPU被迫要访问主存内的信息,访问次数多了,速度相比直接访问Cache就慢下来很多。</p>
<p>所以为了解决CPU与主存间通讯占用总线的问题:在主存与片内缓存之间再加一级缓存,称为 <strong>片外缓存</strong>,<strong>片外缓存不使用系统总线</strong>而是 <strong>使用一个独立的数据路径</strong>。那么从片外缓存调入片内缓存的速度就得到了提高。</p>
<p>所以增加一个层次,即 <strong>片外Cache</strong>,该Cache置于L1与主存之间,当CPU要获取数据,先访问L1 Cache,如果L1 Cache没有,则访问L2 Cache。</p>
<h4 id="将统一的Cache变成分立的Cache"><a href="#将统一的Cache变成分立的Cache" class="headerlink" title="将统一的Cache变成分立的Cache"></a>将统一的Cache变成分立的Cache</h4><p>统一缓存是将数据、指令都存放在同一缓存内;分立缓存是将指令和数据分别存放在指令Cache、数据Cache内。</p>
<p>两种方案如何选择是由 <strong>存储结构</strong>、<strong>指令控制方式</strong>两个方面决定的。</p>
<p>如果存储结构是统一的(指令和数据存储在同一主存内),则相应的Cache采用统一缓存;如果主存采用指令、数据分开存储的方案,则相应的Cache采用分立缓存</p>
<p>如果指令控制方式是超前控制或流水控制,一般采用分立缓存。</p>
<blockquote>
<p>超前控制指在当前指令执行过程尚未结束时,就提前将下一条准备执行的指令取出,称为 <strong>超前取指</strong> 或 <strong>指令预取</strong>;流水线控制指多条指令同时执行,又可视为指令流水。</p>
</blockquote>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/GithubPages/2018/07/20/存储器(八)之高速缓冲器(上)/" data-id="cjkgaepno0033lcvj6fdoijz9" class="article-share-link">Share</a>
<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li></ul>
</footer>
</div>
</article>
<nav id="page-nav">
<span class="page-number current">1</span><a class="page-number" href="/codeleven.github.io/page/2/">2</a><a class="page-number" href="/codeleven.github.io/page/3/">3</a><span class="space">…</span><a class="page-number" href="/codeleven.github.io/page/6/">6</a><a class="extend next" rel="next" href="/codeleven.github.io/page/2/">__('next') »</a>
</nav>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">Tags</h3>
<div class="widget">
<ul class="tag-list"><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/JMM/">JMM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/JVM/">JVM</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/Java并发/">Java并发</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/Java集合/">Java集合</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/数据结构与算法/">数据结构与算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/数据结构和算法/">数据结构和算法</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/计算机组成/">计算机组成</a></li><li class="tag-list-item"><a class="tag-list-link" href="/codeleven.github.io/tags/设计模式/">设计模式</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Tag Cloud</h3>
<div class="widget tagcloud">
<a href="/codeleven.github.io/tags/JMM/" style="font-size: 16.67px;">JMM</a> <a href="/codeleven.github.io/tags/JVM/" style="font-size: 16.67px;">JVM</a> <a href="/codeleven.github.io/tags/Java并发/" style="font-size: 20px;">Java并发</a> <a href="/codeleven.github.io/tags/Java集合/" style="font-size: 15px;">Java集合</a> <a href="/codeleven.github.io/tags/数据结构与算法/" style="font-size: 10px;">数据结构与算法</a> <a href="/codeleven.github.io/tags/数据结构和算法/" style="font-size: 13.33px;">数据结构和算法</a> <a href="/codeleven.github.io/tags/计算机组成/" style="font-size: 18.33px;">计算机组成</a> <a href="/codeleven.github.io/tags/设计模式/" style="font-size: 11.67px;">设计模式</a>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Archives</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/codeleven.github.io/archives/2018/08/">August 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/codeleven.github.io/archives/2018/07/">July 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/codeleven.github.io/archives/2018/06/">June 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/codeleven.github.io/archives/2018/05/">May 2018</a></li><li class="archive-list-item"><a class="archive-list-link" href="/codeleven.github.io/archives/2018/04/">April 2018</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Recent Posts</h3>
<div class="widget">
<ul>
<li>
<a href="/codeleven.github.io/2018/08/05/(未埋)了解阻塞队列之PriorityBlockingQueue/">了解阻塞队列之PriorityBlockingQueue</a>
</li>
<li>
<a href="/codeleven.github.io/2018/08/05/《七周七并发模型》第一章第一天/">《七周七并发模型》第一章第一天</a>
</li>
<li>
<a href="/codeleven.github.io/2018/08/04/无符号数和有符号数/">处理器(一)</a>
</li>
<li>
<a href="/codeleven.github.io/2018/08/02/DMA方式/">DMA方式</a>
</li>
<li>
<a href="/codeleven.github.io/2018/07/27/程序中断方式/">程序中断方式</a>
</li>
</ul>
</div>