本次课内容:
计算机系统的状态机模型
对 “程序如何在计算机上运行”建立基本认识
S = {S1, S2, ...}
S0 ∈ S
考虑一个简单的计算机系统: 程序直接在CPU上运行(无操作系统)
这三个抽象层次(程序, 指令集, CPU)都可以用状态机来理解!
C语言的组成
一个例子
S = {<V, PC>}
V = {v1, v2, v3, ...}
= 程序中所有变量的取值
PC
= 程序计数器 = 当前执行的语句位置S0 = <V0, main函数的第一条语句>
/* 1 */ int main() {
/* 2 */ int x = 1;
/* 3 */ int y = 2;
/* 4 */ int z = x + y;
/* 5 */ printf("z = %d\n", z);
/* 6 */ return 0;
/* 7 */ }
main()
第一条语句开始执行吗?大家一定会觉得这是理所当然的, 至少很多C语言书籍都这么说
但怎么来动手验证一下呢?
5.1.2 Execution environments
Two execution environments are defined: freestanding and hosted. In both cases,
program startup occurs when a designated C function is called by the execution
environment...
有一个概念叫执行环境
, 原来是它来调用一个专门的C函数
5.1.2.1 Freestanding environment
1. In a freestanding environment (in which C program execution may take place
without any benefit of an operating system), the name and type of the function
called at program startup are implementation-defined.
在独立环境下, 这个专门的C函数由具体实现来决定
5.1.2.2 Hosted environments
5.1.2.2.1 Program startup
1. The function called at program startup is named main...
在宿主环境下, 这个专门的C函数名称为main
2. ...
— If the value of argc is greater than zero, the string pointed to by argv[0]
represents the program name; ...
If the value of argc is greater than one, the strings pointed to by argv[1]
through argv[argc-1] represent the program parameters.
上次课提到的命令行参数=main()函数的参数
,
原来是有手册依据的
5.1.2.3 Program execution
2. Accessing a volatile object, modifying an object, modifying a file, or calling
a function that does any of those operations are all side effects, which are
changes in the state of the execution environment...
一些语句的操作会引起副作用, 从而导致执行环境状态的变化
虽然main()
函数不是真正的程序入口,
但用来理解状态机模型是足够的
C语言标准手册精确定义了C语言的每一处细节
大部分C语言的材料都没有覆盖到所有细节, 因此不要迷信书籍和博客
正确的学习方法:
CPU如何设计是微结构层次的话题
但无论怎么设计, 总归是一个数字逻辑电路
S = {<时序逻辑元件的值>}
S0 = <复位时时序逻辑元件的值>
// S = <A, B, C, D>
S0 = <0, 0, 0, 0>
S1 = <1, 0, 0, 0>
S2 = <1, 1, 0, 0>
S3 = <1, 1, 1, 0>
S4 = <1, 1, 1, 1>
S5 = <0, 1, 1, 1>
S6 = <0, 0, 1, 1>
S7 = <0, 0, 0, 1>
S8 = <0, 0, 0, 0> = S0
从状态机的视角来说, CPU和这个计数器并没有本质上的区别
课本上通常会给一个抽象的定义, 例如
指令集是软件和硬件之间的接口
但你很可能还分不清软件和硬件的边界, 更别说它们之间的接口了 😂
我们来进行这样的比喻: 指令集是一本手册规范, 使得
指令集手册定义了CPU执行指令的行为
就好比
C语言标准手册定义了C程序执行语句的行为
随着开源芯片相关概念的普及, 总会有缺乏专业素质的自媒体哗众取宠
《俄数字发展部部长:将大力扶持国产 RISC-V 处理器发展》
包云岗老师在文章《点评RISC-V芯片出货量突破100亿》下的补充评论:
出自《从技术的角度来看,RISC-V 能对芯片发展、科技自主起到哪些作用?》
虚假的RISC-V手册 ❎
真正的RISC-V手册 ✅
《RISC-V Reader》是一本科普读物, 并不是官方手册(书的前言)
We intend this slim book to work as both an introduction and a reference to RISC-V
for students and embedded systems programmers interested in writing RISC-V code.
我们打算将这本⼩书作为RISC-V的介绍和参考资料,供有兴趣编写RISC-V代码的学⽣和嵌⼊式系统程序员使⽤
翻译团队把书名翻译成《RISC-V手册》, 太误导新人了 😂
《RISC-V Reader》的作者(David Patterson & Andrew Waterman)也来自RISC-V团队, 书的质量并不低
正确做法:
S = {<R, M>}
R = {PC, x0, x1, x2, ...}
PC
= 程序计数器 = 当前执行的指令位置M
= 内存
S0 = <R0, M0>
Q: RISC-V处理器的复位PC值是多少?
A: RTFM (《RISC-V Reader》中找不到答案)
计算1+2+...+100
的指令序列
// PC: instruction | label: statement
0: li x1, 0 | pc0: x1 = 0;
1: li x2, 0 | pc1: x2 = 0;
2: li x3, 100 | pc2: x3 = 100;
3: addi x2, x2, 1 | pc3: x2 = x2 + 1;
4: add x1, x1, x2 | pc4: x1 = x1 + x2;
5: blt x2, x3, 3 | pc5: if (x2 < x3) goto pc3; // branch if less than
6: j 6 | pc6: goto pc6;
指令并没有想象中的那么神秘
指令的两种表示: 符号化表示(面向程序员)和编码表示(面向电路设计)
全称: 指令集体系结构(Instruction Set Architecture, ISA), 还有
指令集手册通过定义状态机进行状态转移的规则, 来描述一台抽象计算机所具备的, 程序可以使用的功能
编译器的工作:
将C程序的状态机S_c
翻译成指令集的状态机S_isa
fstate: {PC_c, v1, v2, v3, ...} -> {R, M}
(R
中包含PC_isa
)fcompile: {语句} -> {指令序列}
使得
说人话: C程序执行一条语句后的状态, 与抽象计算机执行编译后的指令序列后的状态,语义上是等价的
汇编课就这样上完了 😂
微结构设计的工作:
将指令集的状态机S_isa
用电路来实现成CPU的状态机S_cpu
fstate: {R, M} -> {时序逻辑电路}
fuarch: {指令} -> {组合逻辑电路}
使得
说人话: 抽象计算机执行一条指令后的状态, 与CPU在根据指令语义设计出的组合逻辑电路控制下的次态,语义上是等价的
程序和指令集都没有实体, 计算机的实体是电路, 如何联系它们?
S_c
~ S_isa
~
S_cpu
机器永远是对的
计算机系统的行为是按照官方手册的描述精确发生的
程序 | 抽象计算机 | CPU | |
---|---|---|---|
状态 | {<V, PC>} |
{<R, M>} |
{时序逻辑电路} |
状态转移规则 | C语言语句的语义 | 指令的语义 | 组合逻辑电路 |
FM | C语言标准手册 | 指令集手册 | 架构设计文档 |