-
Notifications
You must be signed in to change notification settings - Fork 154
/
Copy path2.3.html
1292 lines (776 loc) · 99.2 KB
/
2.3.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 lang="" >
<head>
<meta charset="UTF-8">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>程序, 运行时环境与AM · GitBook</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="description" content="">
<meta name="generator" content="GitBook 3.2.3">
<link rel="stylesheet" href="gitbook/style.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-intopic-toc/style.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-page-footer-ex/style/plugin.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-callouts/plugin.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-highlight/website.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-search/search.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-fontsettings/website.css">
<link rel="stylesheet" href="gitbook/gitbook-plugin-theme-comscore/test.css">
<link rel="stylesheet" href="styles.css">
<meta name="HandheldFriendly" content="true"/>
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="gitbook/images/apple-touch-icon-precomposed-152.png">
<link rel="shortcut icon" href="gitbook/images/favicon.ico" type="image/x-icon">
<link rel="next" href="2.4.html" />
<link rel="prev" href="2.2.html" />
</head>
<body>
<div class="book">
<div class="book-summary">
<div id="book-search-input" role="search">
<input type="text" placeholder="Type to search" />
</div>
<nav role="navigation">
<ul class="summary">
<li class="chapter " data-level="1.1" data-path="index.html">
<a href="index.html">
Introduction
</a>
</li>
<li class="chapter " data-level="1.2" data-path="PA0.html">
<a href="PA0.html">
PA0 - 世界诞生的前夜: 开发环境配置
</a>
<ul class="articles">
<li class="chapter " data-level="1.2.1" data-path="0.1.html">
<a href="0.1.html">
Installing GNU/Linux
</a>
</li>
<li class="chapter " data-level="1.2.2" data-path="0.2.html">
<a href="0.2.html">
First Exploration with GNU/Linux
</a>
</li>
<li class="chapter " data-level="1.2.3" data-path="0.3.html">
<a href="0.3.html">
Installing Tools
</a>
</li>
<li class="chapter " data-level="1.2.4" data-path="0.4.html">
<a href="0.4.html">
Configuring vim
</a>
</li>
<li class="chapter " data-level="1.2.5" data-path="0.5.html">
<a href="0.5.html">
More Exploration
</a>
</li>
<li class="chapter " data-level="1.2.6" data-path="0.6.html">
<a href="0.6.html">
Getting Source Code for PAs
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.3" data-path="PA1.html">
<a href="PA1.html">
PA1 - 开天辟地的篇章: 最简单的计算机
</a>
<ul class="articles">
<li class="chapter " data-level="1.3.1" data-path="1.1.html">
<a href="1.1.html">
在开始愉快的PA之旅之前
</a>
</li>
<li class="chapter " data-level="1.3.2" data-path="1.2.html">
<a href="1.2.html">
开天辟地的篇章
</a>
</li>
<li class="chapter " data-level="1.3.3" data-path="1.3.html">
<a href="1.3.html">
RTFSC
</a>
</li>
<li class="chapter " data-level="1.3.4" data-path="1.4.html">
<a href="1.4.html">
基础设施
</a>
</li>
<li class="chapter " data-level="1.3.5" data-path="1.5.html">
<a href="1.5.html">
表达式求值
</a>
</li>
<li class="chapter " data-level="1.3.6" data-path="1.6.html">
<a href="1.6.html">
监视点
</a>
</li>
<li class="chapter " data-level="1.3.7" data-path="1.7.html">
<a href="1.7.html">
如何阅读手册
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.4" data-path="PA2.html">
<a href="PA2.html">
PA2 - 简单复杂的机器: 冯诺依曼计算机系统
</a>
<ul class="articles">
<li class="chapter " data-level="1.4.1" data-path="2.1.html">
<a href="2.1.html">
不停计算的机器
</a>
</li>
<li class="chapter " data-level="1.4.2" data-path="2.2.html">
<a href="2.2.html">
RTFSC(2)
</a>
</li>
<li class="chapter active" data-level="1.4.3" data-path="2.3.html">
<a href="2.3.html">
程序, 运行时环境与AM
</a>
</li>
<li class="chapter " data-level="1.4.4" data-path="2.4.html">
<a href="2.4.html">
基础设施(2)
</a>
</li>
<li class="chapter " data-level="1.4.5" data-path="2.5.html">
<a href="2.5.html">
输入输出
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.5" data-path="PA3.html">
<a href="PA3.html">
PA3 - 穿越时空的旅程: 批处理系统
</a>
<ul class="articles">
<li class="chapter " data-level="1.5.1" data-path="3.1.html">
<a href="3.1.html">
最简单的操作系统
</a>
</li>
<li class="chapter " data-level="1.5.2" data-path="3.2.html">
<a href="3.2.html">
穿越时空的旅程
</a>
</li>
<li class="chapter " data-level="1.5.3" data-path="3.3.html">
<a href="3.3.html">
用户程序和系统调用
</a>
</li>
<li class="chapter " data-level="1.5.4" data-path="3.4.html">
<a href="3.4.html">
文件系统
</a>
</li>
<li class="chapter " data-level="1.5.5" data-path="3.5.html">
<a href="3.5.html">
精彩纷呈的应用程序
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.6" data-path="PA4.html">
<a href="PA4.html">
PA4 - 虚实交错的魔法: 分时多任务
</a>
<ul class="articles">
<li class="chapter " data-level="1.6.1" data-path="4.1.html">
<a href="4.1.html">
多道程序
</a>
</li>
<li class="chapter " data-level="1.6.2" data-path="4.2.html">
<a href="4.2.html">
虚实交错的魔法
</a>
</li>
<li class="chapter " data-level="1.6.3" data-path="4.3.html">
<a href="4.3.html">
超越容量的界限
</a>
</li>
<li class="chapter " data-level="1.6.4" data-path="4.4.html">
<a href="4.4.html">
来自外部的声音
</a>
</li>
<li class="chapter " data-level="1.6.5" data-path="4.5.html">
<a href="4.5.html">
编写不朽的传奇
</a>
</li>
</ul>
</li>
<li class="chapter " data-level="1.7" data-path="blank.html">
<a href="blank.html">
杂项
</a>
<ul class="articles">
<li class="chapter " data-level="1.7.1" data-path="FAQ.html">
<a href="FAQ.html">
常见问题(FAQ)
</a>
</li>
<li class="chapter " data-level="1.7.2" data-path="why.html">
<a href="why.html">
为什么要学习计算机系统基础
</a>
</li>
<li class="chapter " data-level="1.7.3" data-path="linux.html">
<a href="linux.html">
Linux入门教程
</a>
</li>
<li class="chapter " data-level="1.7.4" data-path="man.html">
<a href="man.html">
man入门教程
</a>
</li>
<li class="chapter " data-level="1.7.5" data-path="git.html">
<a href="git.html">
git入门教程
</a>
</li>
<li class="chapter " data-level="1.7.6" data-path="nemu-isa-api.html">
<a href="nemu-isa-api.html">
NEMU ISA相关API说明文档
</a>
</li>
<li class="chapter " data-level="1.7.7" data-path="changelog.html">
<a href="changelog.html">
更新日志
</a>
</li>
<li class="chapter " data-level="1.7.8" data-path="i386-intro.html">
<a href="i386-intro.html">
i386手册指令集阅读指南
</a>
</li>
<li class="chapter " data-level="1.7.9" data-path="exec.html">
<a href="exec.html">
指令执行例子
</a>
</li>
</ul>
</li>
<li class="divider"></li>
<li>
<a href="https://www.gitbook.com" target="blank" class="gitbook-link">
Published with GitBook
</a>
</li>
</ul>
</nav>
</div>
<div class="book-body">
<div class="body-inner">
<div class="book-header" role="navigation">
<!-- Title -->
<h1>
<i class="fa fa-circle-o-notch fa-spin"></i>
<a href="." >程序, 运行时环境与AM</a>
</h1>
</div>
<div class="page-wrapper" tabindex="-1" role="main">
<div class="page-inner">
<div id="book-search-results">
<div class="search-noresults">
<section class="normal markdown-section">
<h2 id="程序-运行时环境与am">程序, 运行时环境与AM</h2>
<h3 id="运行时环境">运行时环境</h3>
<p>为了让NEMU支持大部分程序的运行, 你已经实现了不少指令.
但并不是有了足够的指令就能运行更多的程序.
我们之前提到"并不是每一个程序都可以在NEMU中运行", 现在我们来解释一下背后的缘由.</p>
<p>从直觉上来看, 让仅仅只会"计算"的TRM来支撑一个功能齐全的操作系统的运行还是不太现实的.
这给我们的感觉就是, 计算机也有一定的"功能强弱"之分, 计算机越"强大", 就能跑越复杂的程序.
换句话说, 程序的运行其实是对计算机的功能有需求的.
在你运行Hello World程序时, 你敲入一条命令(或者点击一下鼠标), 程序就成功运行了,
但这背后其实隐藏着操作系统开发者和库函数开发者的无数汗水.
一个事实是, 应用程序的运行都需要<a href="http://en.wikipedia.org/wiki/Runtime_system" target="_blank">运行时环境</a>的支持,
包括加载, 销毁程序, 以及提供程序运行时的各种动态链接库(你经常使用的库函数就是运行时环境提供的)等.
为了让客户程序在NEMU中运行, 现在轮到你来提供相应的运行时环境的支持了.</p>
<p>根据KISS法则, 我们先来考虑最简单的运行时环境是什么样的.
换句话说, 为了运行最简单的程序, 我们需要提供什么呢?
其实答案已经在PA1中了: 只要把程序放在正确的内存位置,
然后让PC指向第一条指令, 计算机就会自动执行这个程序, 永不停止.</p>
<p>不过, 虽然计算机可以永不停止地执行指令, 但一般的程序都是会结束的,
所以运行时环境需要向程序提供一种结束运行的方法.
聪明的你已经能想到, 我们在PA1中提到的那条人工添加的<code>nemu_trap</code>指令,
就是让程序来结束运行的.</p>
<p>所以, 只要有内存, 有结束运行的方式, 加上实现正确的指令,
就可以支撑最简单程序的运行了.
而这, 也可以算是最简单的运行时环境了.</p>
<h3 id="将运行时环境封装成库函数">将运行时环境封装成库函数</h3>
<p>我们刚才讨论的运行时环境是直接位于计算机硬件之上的,
因此运行时环境的具体实现, 也是和架构相关的.
我们以"ISA-平台"的二元组来表示一个架构, 例如<code>mips32-nemu</code>.
以程序结束为例, NEMU中是使用特殊的<code>nemu_trap</code>指令,
而不同ISA的<code>nemu_trap</code>指令的格式肯定不同;
但如果我们自己用verilog设计了一个riscv32 CPU, 这个<code>riscv32-mycpu</code>的架构,
有可能是通过一条<code>mycpu_trap</code>指令来结束程序, 它和<code>nemu_trap</code>指令可能是不一样的.
而结束运行是程序共有的需求, 为了让<code>n</code>个程序运行在<code>m</code>个架构上, 难道我们要维护<code>n*m</code>份代码?
有没有更好的方法呢?</p>
<p>对于同一个程序, 如果能把<code>m</code>个版本不同的部分都转换成相同的代码,
我们就只需要维护一个版本就可以了.
而实现这个目标的杀手锏, 就是你在程序设计课上学过的抽象!
我们只需要定义一个结束程序的API, 比如<code>void halt()</code>,
它对不同架构上程序的不同结束方式进行了抽象:
程序只要调用<code>halt()</code>就可以结束运行, 而不需要关心自己运行在哪一个架构上.
经过抽象之后, 之前<code>m</code>个版本的程序, 现在都统一通过<code>halt()</code>来结束运行,
我们就只需要维护这一个通过<code>halt()</code>来结束运行的版本就可以了.
然后, 不同的架构分别实现自己的<code>halt()</code>, 就可以支撑<code>n</code>个程序的运行!
这样以后, 我们就可以把程序和架构解耦了:
我们只需要维护<code>n+m</code>份代码(<code>n</code>个程序和<code>m</code>个架构相关的<code>halt()</code>), 而不是之前的<code>n*m</code>.</p>
<p>这个例子也展示了运行时环境的一种普遍的存在方式: 库.
通过库, 运行程序所需要的公共要素被抽象成API,
不同的架构只需要实现这些API, 也就相当于实现了支撑程序运行的运行时环境,
这提升了程序开发的效率: 需要的时候只要调用这些API, 就能使用运行时环境提供的相应功能.</p>
<div class="panel panel-info"><div class="panel-heading"><h5 class="panel-title" id="这又能怎么样呢"><i class="fa fa-question-circle"></i> 这又能怎么样呢</h5></div><div class="panel-body"><p>思考一下, 这样的抽象还会带来哪些好处呢? 你很快就会体会到这些好处了.</p></div></div>
<h3 id="am---裸机bare-metal运行时环境">AM - 裸机(bare-metal)运行时环境</h3>
<p>一方面, 正如上文提到, 应用程序的运行都需要运行时环境的支持;
另一方面, 只进行纯粹计算任务的程序在TRM上就可以运行,
更复杂的应用程序对运行时环境必定还有其它的需求:
例如你之前玩的超级玛丽需要和用户进行交互, 至少需要运行时环境提供输入输出的支持.
要运行一个现代操作系统, 还要在此基础上加入更高级的功能.</p>
<p>如果我们把这些需求都收集起来, 将它们抽象成统一的API提供给程序,
这样我们就得到了一个可以支撑各种程序运行在各种架构上的库了!
具体地, 每个架构都按照它们的特性实现这组API;
应用程序只需要直接调用这组API即可, 无需关心自己将来运行在哪个架构上.
由于这组统一抽象的API代表了程序运行对计算机的需求, 所以我们把这组API称为抽象计算机.</p>
<p>AM(Abstract machine)项目就是这样诞生的.
作为一个向程序提供运行时环境的库, AM根据程序的需求把库划分成以下模块</p>
<pre><code>AM = TRM + IOE + CTE + VME + MPE
</code></pre><ul>
<li>TRM(Turing Machine) - 图灵机, 最简单的运行时环境, 为程序提供基本的计算能力</li>
<li>IOE(I/O Extension) - 输入输出扩展, 为程序提供输出输入的能力</li>
<li>CTE(Context Extension) - 上下文扩展, 为程序提供上下文管理的能力</li>
<li>VME(Virtual Memory Extension) - 虚存扩展, 为程序提供虚存管理的能力</li>
<li>MPE(Multi-Processor Extension) - 多处理器扩展, 为程序提供多处理器通信的能力
(MPE超出了ICS课程的范围, 在PA中不会涉及)</li>
</ul>
<p>AM给我们展示了程序与计算机的关系:
利用计算机硬件的功能实现AM, 为程序的运行提供它们所需要的运行时环境.
感谢AM项目的诞生, 让NEMU和程序的界线更加泾渭分明, 同时使得PA的流程更加明确:</p>
<pre><code>(在NEMU中)实现硬件功能 -> (在AM中)提供运行时环境 -> (在APP层)运行程序
(在NEMU中)实现更强大的硬件功能 -> (在AM中)提供更丰富的运行时环境 -> (在APP层)运行更复杂的程序
</code></pre><p>这个流程其实与PA1中开天辟地的故事遥相呼应:
先驱希望创造一个计算机的世界, 并赋予它执行程序的使命.
亲自搭建NEMU(硬件)和AM(软件)之间的桥梁来支撑程序的运行,
是"理解程序如何在计算机上运行"这一终极目标的不二选择.</p>
<div class="panel panel-danger"><div class="panel-heading"><h5 class="panel-title" id="am的诞生和project-n的故事"><i class="fa fa-bullhorn"></i> AM的诞生和Project-N的故事</h5></div><div class="panel-body"><p>在AM诞生之前, Project-N的各个主要部件就已经存在了:</p><ul>
<li>NEMU - NJU EMUlator (系统基础实验)</li>
<li>Nanos - Nanjing U OS (操作系统实验)</li>
<li>NOOP - NJU Out-of-Order Processor (组成原理实验)</li>
<li>NCC - NJU C Compiler (编译原理实验)</li>
</ul><p>但我们一直没想好, 如何把这些部件集成到一个完整的教学生态系统中.</p><p>在2017年春季的计算机系统综合实验课程中, <a href="http://moon.nju.edu.cn/~jyy/" target="_blank">jyy</a>首先提出AM的思想, 把程序和架构解耦.
解耦之后, AM就成了Project-N的一把关键的钥匙:
只要实现了AM, 我们就可以在NEMU和NOOP上运行各种AM程序;
只要在AM上实现Nanos, 我们就可以把Nanos运行在NEMU和NOOP上;
只要NCC把程序编译到AM上, 我们就可以在NOOP上运行NCC编译的程序.</p><p>经过几个月的尝试, 我们很快就相信, 这条路是对的.
于是临时决定将2017年秋季的PA进行大改版, 借鉴AM的思想来设计开发NEMU,
期望大家能更好地理解"程序如何在计算机上运行".
因此2017年秋季版本的NEMU, 也算是第一次正式作为一个子项目收录到Project-N教学生态系统中.</p><p>我们已经连续两年组队参加计算机系统设计大赛"龙芯杯",
在大赛上展示我们独有的Project-N生态系统, 均获得第二名的好成绩.
我们在大赛中探索出来的好方法, 也会反馈到PA中.
这些离你其实并不遥远, 我们在PA中传递出来的做事方法和原则, 都是大赛得奖的黄金经验.</p><p>如果你对AM和Project-N感兴趣, 欢迎联系jyy或yzh.</p></div></div>
<div class="panel panel-success"><div class="panel-heading"><h5 class="panel-title" id="穿越时空的羁绊"><i class="fa fa-lightbulb-o"></i> 穿越时空的羁绊</h5></div><div class="panel-body"><p>有了AM, 我们就可以把课程之间的实验打通, 做一些以前做不到的有趣的事情了.
比如今年春季的操作系统课上, 你的学长学姐在AM上编写了他们自己的小游戏.
在今年PA的后期, 你将有机会把学长学姐们编写的游戏无缝地移植到NEMU上,
作为最终系统展示的一部分, 想想都是一件激动人心的事情.</p></div></div>
<!-- -->
<div class="panel panel-info"><div class="panel-heading"><h5 class="panel-title" id="为什么要有am-建议二周目思考"><i class="fa fa-question-circle"></i> 为什么要有AM? (建议二周目思考)</h5></div><div class="panel-body"><p>操作系统也有自己的运行时环境.
AM和操作系统提供的运行时环境有什么不同呢? 为什么会有这些不同?</p></div></div>
<h3 id="rtfsc3">RTFSC(3)</h3>
<p>你已经在PA0的最后获得了AM的子项目<code>abstract-machine</code>, 下面我们来简单介绍一下AM项目的代码.
代码中<code>abstract-machine/</code>目录下的源文件组织如下(部分目录下的文件并未列出):</p>
<pre><code>abstract-machine
├── am # AM相关
│ ├── include
│ │ ├── amdev.h
│ │ ├── am.h
│ │ └── arch # 架构相关的头文件定义
│ ├── Makefile
│ └── src
│ ├── mips
│ │ ├── mips32.h
│ │ └── nemu # mips32-nemu相关的实现
│ ├── native
│ ├── platform
│ │ └── nemu # 以NEMU为平台的AM实现
│ │ ├── include
│ │ │ └── nemu.h
│ │ ├── ioe # IOE
│ │ │ ├── audio.c
│ │ │ ├── disk.c
│ │ │ ├── gpu.c
│ │ │ ├── input.c
│ │ │ ├── ioe.c
│ │ │ └── timer.c
│ │ ├── mpe.c # MPE, 当前为空
│ │ └── trm.c # TRM
│ ├── riscv
│ │ ├── nemu # riscv32(64)相关的实现
│ │ │ ├── cte.c # CTE
│ │ │ ├── start.S # 程序入口
│ │ │ ├── trap.S
│ │ │ └── vme.c # VME
│ │ └── riscv.h
│ └── x86
│ ├── nemu # x86-nemu相关的实现
│ └── x86.h
├── klib # 常用函数库
├── Makefile # 公用的Makefile规则
└── scripts # 构建/运行二进制文件/镜像的Makefile
├── isa
│ ├── mips32.mk
│ ├── riscv32.mk
│ ├── riscv64.mk
│ └── x86.mk
├── linker.ld # 链接脚本
├── mips32-nemu.mk
├── native.mk
├── platform
│ └── nemu.mk
├── riscv32-nemu.mk
├── riscv64-nemu.mk
└── x86-nemu.mk
</code></pre><p>整个AM项目分为两大部分:</p>
<ul>
<li><code>abstract-machine/am/</code> - 不同架构的AM API实现, 目前我们只需要关注NEMU相关的内容即可.
此外, <code>abstract-machine/am/include/am.h</code>列出了AM中的所有API, 我们会在后续逐一介绍它们.</li>
<li><code>abstract-machine/klib/</code> - 一些架构无关的库函数, 方便应用程序的开发</li>
</ul>
<p>阅读<code>abstract-machine/am/src/platform/nemu/trm.c</code>中的代码,
你会发现只需要实现很少的API就可以支撑起程序在TRM上运行了:</p>
<ul>
<li><code>Area heap</code>结构用于指示堆区的起始和末尾</li>
<li><code>void putch(char ch)</code>用于输出一个字符</li>
<li><code>void halt(int code)</code>用于结束程序的运行</li>
<li><code>void _trm_init()</code>用于进行TRM相关的初始化工作</li>
</ul>
<p>堆区是给程序自由使用的一段内存区间, 为程序提供动态分配内存的功能.
TRM的API只提供堆区的起始和末尾, 而堆区的分配和管理需要程序自行维护.
当然, 程序也可以不使用堆区, 例如<code>dummy</code>.
把<code>putch()</code>作为TRM的API是一个很有趣的考虑, 我们在不久的将来再讨论它,
目前我们暂不打算运行需要调用<code>putch()</code>的程序.</p>
<p>最后来看看<code>halt()</code>. <code>halt()</code>里面调用了<code>nemu_trap()</code>宏
(在<code>abstract-machine/am/src/platform/nemu/include/nemu.h</code>中定义),
这个宏展开之后是一条<a href="http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html" target="_blank">内联汇编</a>语句.
内联汇编语句允许我们在C代码中嵌入汇编语句,
以riscv32为例, 宏展开之后将会得到:</p>
<pre><code class="lang-c">asm volatile("mv a0, %0; ebreak" : :"r"(code));
</code></pre>
<p>显然, 这个宏的定义是和ISA相关的, 如果你查看<code>nemu/src/isa/$ISA/inst.c</code>,
你会发现这条指令正是那条特殊的<code>nemu_trap</code>!
<code>nemu_trap()</code>宏还会把一个标识结束的结束码移动到通用寄存器中,
这样, 这段汇编代码的功能就和<code>nemu/src/isa/$ISA/inst.c</code>中<code>nemu_trap</code>的行为对应起来了:
通用寄存器中的值将会作为参数传给<code>set_nemu_state()</code>,
将<code>halt()</code>中的结束码设置到NEMU的monitor中, monitor将会根据结束码来报告程序结束的原因.
此外, <code>volatile</code>是C语言的一个关键字, 如果你想了解关于<code>volatile</code>的更多信息, 请查阅相关资料.</p>
<div class="panel panel-info"><div class="panel-heading"><h5 class="panel-title" id="am的api文档"><i class="fa fa-info-circle"></i> AM的API文档</h5></div><div class="panel-body"><p>我们在这里给出<a href="https://jyywiki.cn/AbstractMachine/AM_Spec.md" target="_blank">AM的API文档</a>, 如果你希望了解这些API的严格定义, 可以RTFM.</p></div></div>
<p><code>am-kernels</code>子项目用于收录一些可以在AM上运行的测试集和简单程序:</p>
<pre><code>am-kernels
├── benchmarks # 可用于衡量性能的基准测试程序
│ ├── coremark
│ ├── dhrystone
│ └── microbench
├── kernels # 可展示的应用程序
│ ├── hello
│ ├── litenes # 简单的NES模拟器
│ ├── nemu # NEMU
│ ├── slider # 简易图片浏览器
│ ├── thread-os # 内核线程操作系统
│ └── typing-game # 打字小游戏
└── tests # 一些具有针对性的测试集
├── am-tests # 针对AM API实现的测试集
└── cpu-tests # 针对CPU指令实现的测试集
</code></pre><p>在让NEMU运行客户程序之前, 我们需要将客户程序的代码编译成可执行文件.
需要说明的是, 我们不能使用gcc的默认选项直接编译,
因为默认选项会根据GNU/Linux的运行时环境将代码编译成运行在GNU/Linux下的可执行文件.
但此时的NEMU并不能为客户程序提供GNU/Linux的运行时环境, 在NEMU中无法正确运行上述可执行文件,
因此我们不能使用gcc的默认选项来编译用户程序.</p>
<p>解决这个问题的方法是<a href="http://en.wikipedia.org/wiki/Cross_compiler" target="_blank">交叉编译</a>.
我们需要在GNU/Linux下根据AM的运行时环境编译出能够在<code>$ISA-nemu</code>这个新环境中运行的可执行文件.
为了不让链接器ld使用默认的方式链接, 我们还需要提供描述<code>$ISA-nemu</code>的运行时环境的链接脚本.
AM的框架代码已经把相应的配置准备好了, 上述编译和链接选项主要位于<code>abstract-machine/Makefile</code>
以及<code>abstract-machine/scripts/</code>目录下的相关<code>.mk</code>文件中.
编译生成一个可以在NEMU的运行时环境上运行的程序的过程大致如下:</p>
<ul>
<li>gcc将<code>$ISA-nemu</code>的AM实现源文件编译成目标文件,
然后通过ar将这些目标文件作为一个库, 打包成一个归档文件<code>abstract-machine/am/build/am-$ISA-nemu.a</code></li>
<li>gcc把应用程序源文件(如<code>am-kernels/tests/cpu-tests/tests/dummy.c</code>)编译成目标文件</li>
<li>通过gcc和ar把程序依赖的运行库(如<code>abstract-machine/klib/</code>)也编译并打包成归档文件</li>
<li>根据Makefile文件<code>abstract-machine/scripts/$ISA-nemu.mk</code>中的指示,
让ld根据链接脚本<code>abstract-machine/scripts/linker.ld</code>,
将上述目标文件和归档文件链接成可执行文件</li>
</ul>
<p>根据上述链接脚本的指示, 可执行程序重定位后的节从<code>0x100000</code>或<code>0x80000000</code>开始
(取决于<code>_pmem_start</code>和<code>_entry_offset</code>的值), 首先是<code>.text</code>节,
其中又以<code>abstract-machine/am/src/$ISA/nemu/start.S</code>中自定义的<code>entry</code>节开始,
然后接下来是其它目标文件的<code>.text</code>节. 这样, 可执行程序起始处总是放置<code>start.S</code>的代码,
而不是其它代码, 保证客户程序总能从<code>start.S</code>开始正确执行.
链接脚本也定义了其它节(包括<code>.rodata</code>, <code>.data</code>, <code>.bss</code>)的链接顺序,
还定义了一些关于位置信息的符号, 包括每个节的末尾, 栈顶位置, 堆区的起始和末尾.</p>
<p>我们对编译得到的可执行文件的行为进行简单的梳理:</p>
<ol>
<li>第一条指令从<code>abstract-machine/am/src/$ISA/nemu/start.S</code>开始,
设置好栈顶之后就跳转到<code>abstract-machine/am/src/platform/nemu/trm.c</code>的<code>_trm_init()</code>函数处执行.</li>
<li>在<code>_trm_init()</code>中调用<code>main()</code>函数执行程序的主体功能,
<code>main()</code>函数还带一个参数, 目前我们暂时不会用到, 后面我们再介绍它.</li>
<li>从<code>main()</code>函数返回后, 调用<code>halt()</code>结束运行.</li>
</ol>
<p>有了TRM这个简单的运行时环境, 我们就可以很容易地在上面运行各种"简单"的程序了.
当然, 我们也可以运行"不简单"的程序:
我们可以实现任意复杂的算法, 甚至是各种理论上可计算的问题, 都可以在TRM上解决.</p>