This commit is contained in:
shzhxh 2019-01-27 10:38:16 +08:00
commit b3c9cd5715
323 changed files with 14570 additions and 0 deletions

View File

@ -0,0 +1,173 @@
### 什么是编译器?
个人认为,编译器是从一个指令集到另一个指令集的**映射工具**。为实现此功能任何编译器都需要做这3件事1是**分解**原指令集2是**分析**原指令集即将原指令集转化为等价于目标指令集的结构显然这个结构也是等价于目标指令集的3是将所得的结构**重组**成目标指令集。以v9.js为例它也做的是这三件事1构建词法分析器2构建语法分析器3构建虚拟机及指令集。
### 四个主要的函数
* next:获取下一个标记,用于词法分析
所有支持的标记都被放入一个枚举类型中词法分析器的框架换行符宏定义标识符与符号表数字字符串注释lookahead关键字与内置函数
* decl:语法分析,使用递归下降的方法
BNF语法左递归的消除
变量定义EBNF表示解析变量的定义
函数定义:函数定义的解析,解析参数,函数体的解析
* expr:解析一个表达式
stmt函数解析一个语句IFWHILERETURNBREAKCONTINUEFORDOSWITCHCASE,ASM,VA_START,DEFAULT,GOTO,ID,其它语句)
运算符的优先级:逆波兰式,一元运算符,二元运算符
### 一个简化的计算机
* 内存:text(代码段)data(数据段)stack(栈)
* 寄存器pc(程序计数器)sp(指针寄存器)bp(基址寄存器)ax(通用寄存器)
* 指令集一个字符型数组ops[]和一个枚举类型
1. IMM(将立即数放入ax)LC(将字符地址放入ax)LI(将整数地址放入ax),SC(将ax中的数据放入栈中),SI(将ax中的数据放入栈中)
2. PSHA(将ax的值入栈)
3. JMP(无条件跳转)
4. JZ/JNZ(ax为零/不为零跳转)
5. CALL调用RET返回ENT子函数调用ADJ清空栈中子函数的参数LEV返回LEA参数调用
6. 运算符指令要注意第一个参数在栈顶第二个参数在ax中
7. 内置函数exit,open,close,read,printf,malloc,memset,memcmp
### 全局变量
* file字符型指针用于指向预处理包含文件的文件名或源文件的文件名
* cmd字符型指针用于指向命令的名字
* pos字符型指针用于指示源文件中字符的位置
* incl字符型指针用于指示预处理包含文件的路径
* tk整型变量用于记录当前标记
* ts、ip整型变量分别指代码段和代码指针
* gs、data整型变量指数据段、当前偏移
* bss整型变量bss偏移
* loc整型变量本地帧偏移
* line整型变量用于记录行号
* ival整型变量当前标记的整数值
* errors整型变量错误的数量
* verbose整型变量c编译器的-v选项输出详细信息
* debug整型变量c编译器的-s选项打印源和生成代码
* ffun整型变量末解决的函数计数器
* va、vp整形变量分别指变量池和变量指针
* e整型变量的指针是表达式树的指针
* pdata整型变量的指针数据段修补指针
* pbss整型变量的指针BSS段修补指针
* fval双精度型变量当前标记的双精度型变量
* ty无符号整型变量当前解析的子表达式类型
* rt无符号整型变量当前解析的函数返回类型
* bigend无符号整型变量大端机器
* info_fd文件句柄
* idident_t结构体的指针当前解析标识符
### 标记与汇编码
- 标记
![标记](tokens.png)
- 汇编码
![汇编码](assembly.png)
### 静态变量
* iline静态整型变量预处理包含文件所在的行
* ifile静态字符型指针变量可能代表预处理包含文件的句柄
* ipos静态字符型指针变量意指预处理包含文件在文件中的位置
* iname静态字符型数组保存着预处理包含文件的路径的字符串
* brk静态整型变量
### main函数
* 输入整型变量argc是变量个数argv是字符型指针变量的数组输出整型变量
* 数据结构:
hdr结构体看起来像是在模拟硬盘
outfile字符型指针输出文件的文件名
tmain:ident_t结构体的指针
* 工作流程:获取编译参数,为目标代码申请内存空间,将源文件读入内存,将源代码转换为目标代码,将目标代码写入文件中。
### next函数
* 详见src/xvcc.c文件
* 无输入,无输出,操作的均为静态变量和公共变量。把源码输出为标记流。
* 所有标记都放在一个枚举型变量中,注意标记是有顺序的
* 私有变量p,hm
* b整型变量表明一个字符串或文件的长度
* 处理逻辑:
* 如果是空格,水平制表'\t',垂直制表'\v',回车'\r',换页'\f',则跳出本循环,读下一个字符。
* 如果是换行'\n'则行数加1,跳出本循环,读下一个字符。
* 如果是'#',则判断是否是预处理语句"#include",依次读取预处理包含文件。
* 如果是字母、下划线或美元符号"$"
* 如果是数字,
* 如果是斜杠“/”,可能是除号或注释。
* 如果是斜杠"\"或",判断是哪种转义字符。
* 如果是“=”可能是Eq或Assign。
* 如果是运算符,则为tk赋值相应的标记。
* 如果是其它符号,则为tk赋值相应的标记。
```
变量id(Id), Ptr, Notf, Nzf, Fun, FFun, 函数调用Fcall, 符号标记(Label, FLabel),
Cic, Cuc, Cis, Cus, Addaf, Subaf, Mulaf, Dvua,
Divaf, Mdua, Srua,
* 关键字标记(括号内为标记)
asm(Asm) auto(Auto) break(Break) case(Case) char(Char) const
continue(Continue) default(Default) do(Do) double(Double)
else(Else) enum(Enum) extern float(Float) for(For) goto(Goto)
if(If) int(Int) long(Long) register return(Return) short(Short)
signed sizeof(Sizeof) static(Static) struct(Struct)
switch(Switch) typedef(TypeDef) union(Union) unsigned(Unsigned)
void(Void) volatile while(While)
va_list(Va_list), va_start(Va_start), va_arg(Va_arg),
整数Num, 浮点数Numf, 多个点Dots,
汇编指令Lea, 汇编指令Leag,汇编指令Cid, 汇编指令Cud, 汇编指令Cdi, 汇编指令Cdu,
汇编指令Eqf, 汇编指令Nef, 汇编指令Ltu, 汇编指令Ltf, 汇编指令Geu, 汇编指令Gef, 汇编指令Sru, 汇编指令Addf, 汇编指令Subf, 汇编指令Mulf,汇编指令Dvu, 汇编指令Divf, 汇编指令Mdu,
```
```
* 运算符标记,从上到下优先级降低
()Paren []Brak ->Arrow .Dot
!Not ~ ++Inc --Dec + - * (type) sizeof
*Mul /Div %Mod
+Add -Sub
<<Shl >>Shr
<Lt <=Le >Gt >=Ge
==Eq !=Ne
&And
^Xor
|Or
&&Lan
||Lor
?:Cond
=Assign +=Adda -=Suba *=Mula /=Diva %=Moda &=Anda ^=Xora |=Ora <<=Shla >>=Shra
,Comma
```
### stmt函数
* 无输入,无输出,操作的均为静态变量和公共变量。把语句解析成汇编代码。
* 私有变量c, d, et, cmin, cmax
a看起来像是语句的某个跳转入口
b看起来像是语句的某个跳转入口
es用于存储表达树的指针
* 处理逻辑:
* IF语句判断表达式的值为真则顺序执行然后跳转到语句末尾为假则跳转到else语句。
* WHILE语句判断表达式的值为真则顺序执行然后跳转到语句前端为假则跳到语句末尾。
* RETURE语句使用LEV指令返回函数。
* Break语句
### expr函数
* 输入整型,无返回值。把表达式解析成汇编代码。
* 如果是数字标记为Num或Numf。
* 如果是字符串标记为Leag。
* 如果是Id,
* 如果是Va_arg,
* 如果是Sizeof
### decl函数
- 输入整型,无返回值。
### info_函数
* info_open输入文件名的指针无返回值。打开文件将文件句柄赋值给公共变量为ip赋值。
* mapfile输入文件名的指针输出字符型指针将文件内容载入到内存中。
### 指令发射器(introduction emitter)
* em,emi,emj,eml,emg,emf,patch
### 分析器
* next, imm, immf, tsize, tinc, talign, basetype, type, decl, member
* dline是个分析器将行中的某个位置到行末的距离输出到句柄为info_fd的文件中。
* skip输入整型变量c,无输出作用是跳过标记c。
### 表达式分析
* node, nodc, mul, add, flot, ind, addr, assign, trim, cast, expr,
### 表达式生成
* lmod, smod, lbf, opf, opaf, lbi, lb, opt, opi, op, opa, rv, stmt
* testnot输入表达式树指针和一个整数返回一个整数。
* test输入表达式树的指针和一个整数返回一个整数。
* opt输入表达式树指针无返回值。

View File

@ -0,0 +1,85 @@
### 实验pa1a
#### 源代码的结构
1. Lexer.l用以生成Lexer.java文件作用是把decaf文件的源代码转化为Token的形式供Parser.java使用完成的是词法分析的功能。是用正则表达式来实现此功能的。
2. Parser.y用以生成Parser.java文件作用是把从Lexer.java得到的Token转化为语法树完成的是语法分析的功能。是用文法规则实现此功能的。
3. 抽象类BaseLexer是作为类Lexer的父类而出现的。
4. 抽象类BasePaser是作为类Paser的父类而出现的。
5. 类SemValue是在为Token赋值。
6. 抽象类Tree代表抽象语法树的结点。我理解抽象语法树描述的是源代码的**逻辑结构**,我**猜测**每一个叶子结点都是一个**基本数据类型**,每一个非叶子结点都是对基本数据类型的**操作**。抽象类Tree为不同类型的结点设置了约80种属性比如属性NEWARRAY代表创建新数组RETURN代表返回状态IF代表条件语句。这些属性实际上是互不相同的整数。抽象类Tree内设计了近40个它的子类
#### 跟踪注释的处理过程
1. 依据正则表达式("//"[^\r\n]*{NEWLINE})将注释匹配为S_COMMENT
2. 依据注释的规则 { /* Just ignore */ },什么都不做,注释处理完毕
#### 跟踪关键字的处理过程
1. 是直接匹配和关键字对应的字符串,关键字对应的规则均为{ return keyword(Parser.XXX); }
2. Parser.XXX的XXX是在Parser.y文件中定义的标记(token)通过观察Parser.java文件可见它实际上是个序号唯一的数字
3. keyword方法的作用是为Parser对象的yylval属性赋值并返回Parser.XXX的值。
4. yylval是一个SemValue对象是为它的loc和code属性赋值code就是Parser.XXX的值loc是一个Location对象。
5. loc有两个属性lin代表行号col代表列号。
6. 到这里我觉得有点奇怪目前编译器应该是输入decaf源文件输出输出类似Testcase/S1/result/*.result之类的文件但从以上分析看不出有输出什么到文件。我想是不是应该看看总控函数public static void main做了些什么。
7. 通过grep命令找到main函数在Driver.java文件中。main函数只有4条指令它创建了一个Driver对象对Driver对象的option属性赋值调用init方法调用compile方法。
8. option属性是一个Option对象在赋值的时候是通过读取命令参数来指定Option对象的属性srcFileName,dstFileName,input,output。
9. init方法创建了Lexer对象Parser对象DecafError对象。
10. compile方法首先使用parser.parseFile方法解析输入并执行指示项然后使用checkPoint方法找到解析错误的地方最后使用条件判断做相应的处理。
11. 通过总控函数知道了大概的过程,但还是很迷惑,因为对象之间的关系也很复杂。
12. 应该怎么样继续理解编译器里这些对象呢从整体来看其实有三个对象decaf源文件编译器decaf目标文件。编译器里分了这么多对象其实目标只有一个就是把源文件转换成目标文件。我想从源文件和目标文件这个角度来理解编译器的工作原理是不是更容易一点呢
13. 源文件在Driver对象的option属性里option属性本身也是Opiton对象源文件具体对应着的是srcFileName属性。
14. srcFileName被转化成了InputStream对象input我猜测就是把源文件载入内存并把内存中的文件抽象成字节流。
15. input传递给了Lexer对象lexer,lexer和parser绑定对应关系。
16. parser.yyparse方法进行了具体的分析工作。依据Yacc的说明文档yyparse又调用了yylex进行词法分析的工作得到标记(tokens)。如果一切正常yyparse会返回0,否则返回1。
#### decaf-PA1A的处理过程是什么样的
1. decaf的总控函数在类Driver.java里理解了Driver.java里的main函数也就理解了decaf的执行逻辑。
2. 首先会打开源文件创建目标文件。这部分工作是由Option类的构造函数来完成的。最重要的两个属性是input和output。
3. 具体的分析工作是由Parser类的parseFile方法来完成的。它的作用是生成一颗语法分析树。是采用的自底向上的LALR(1)分析方法。
4. Parser类的parseFile方法就是yyparse()方法yyparse()方法是由Parser.y自动生成的语法分析工具。
5. yyparse()会调用yylex()方法来获得标记yylex()方法是由Lexer.l自动生成的词法分析工具。yylex()方法每次只返回一个标记。
6. 把生成的语法分析树写入目标文件中这是由Tree类的printTo()方法来完成的。至此decaf-PA1A的处理过程结束。
#### 相同名称的变量出现在源代码不同文件中,它们的含义有什么不同吗?
1. Lexer.l中的标记(NEWLINE、INTEGER等)只是正则表达式的记号它并未出现在lexer.java中由此可见lexer.l中的标记对源代码的其它部分无任何影响。lexer.l中用到的另一种标记Parser.XXX只是类Parser的属性。
2. Parser.y中的标记(由%token定义)是文法分析里的终结符是在文法规则里用的。在Parser.java中体现为类的属性。而在Parser.y中出现的Tree.XXX虽然名字和Parser.y中的标记相同但它们的数据类型、值和代表的意义都是不一样的。
3. Tree.java中的属性(public static final int TOPLEVEL等)虽然有一部分名字和Parser.y中的标记相同但表达的意义不同它表示的是抽象语法树里的某种结点。
4. SemValue.java中即用到了Tree.XXX也用到了Parser.XXX。

View File

@ -0,0 +1,45 @@
- install:
`sudo apt install flex bison`
- 作用:
flex: 生成一个确定有穷自动机(DFA),使用正则表达式描述正则文法。
bison:使用BNF范式描述上下文无关文法生成的应该是下推自动机。
- 结构flex和bison的程序均由三部分构成
```
声明
%%
规则
%%
C代码
```
> %{和%}之间的代码直接照抄到生成的C文件中
- flex的语法
- 正则表达式
- 消除二义性:最大匹配,最早匹配
- yyin: 读取输入的文件句柄(输入管理的第一个层次)
- YY_BUFFER_STATE:创建并使用输入缓冲区(输入管理的第二个层次)
- YY_INPUT:重定义输入到当前缓冲区的宏(输入管理的第三个层次)
- yyout:所有没有被匹配的输入都会复制到yyout可使用`%option nodefault`禁用它
- flex的库文件 -lfl定义了默认的main例程和yywrap然而基本上所有的flex程序都使用`%option noyywwap`和自己的主例程所以它们并不需要flex库。
- yytext: 字符串
- yylex(): 读取一个部分输入,返回下一个记号。记号由两部分组成:记号的编号和记号的值(yyval)。
- yyrestart(f):使词法分析器读取标准输入输出文件f。
- yyterminate():立即从词法分析器返回。
- %x IFILE:把IFILE定义为起始状态。
- INITIALflex本身定义的状态
- %s声明包含的起始状态
- BEGIN切换到另外一个起始状态
- `<<EOF>>`:匹配输入文件的结束
- bison的语法
- %token: 记号名称。
- 起始符号在第一条规则的左边。

View File

@ -0,0 +1,75 @@
- 形式语言与自动机本质上是一种数学模型。
- 形式语言和自动机的理论基础
- Chomsky对自然语言的研究
- 巴科斯-诺尔范式(BNF)
- Kleene在研究神经细胞时建立的自动机模型
- 先修知识:集合、关系、图论
- 分类
- 0型 短语文法 图灵机(Turing machine)
- 1型 上下文有关文法 线性有界自动机LBA(Linear Bounded Automaton)
- 2型 上下文无关文法 下推自动机PDA(PushDown Automaton)
- 3型 右线性文法 正则文法 有限自动机(Finite-state machine)
1. 形式语言概念
1. 字母表,字(**字母表**中的符号组成**字**)
2. 字和字母表的运算(字的**连接**运算,字母表的**幂**是字的集合,**星闭包**是所有字的集体,**正闭包**是除空字外所有字的集体)
3. 语言(**语言**是闭包的子集)
4. 语言的运算(**并****连接****闭包****幂**)
2. 正规语言
1. 基本概念
- **正规表达式**就是满足并,连接和闭包这三种运算的表达式。
- **正规语言**就是满足正规表达式的语言。
2. 有限自动机(计算能力等价于正规表达式)
0. 有限自动机=**状态集**+**输入符号集**+**转移函数**+**开始状态**+**终态集**
1. 确定有限自动机DFA(deterministic finite automaton)
DFA就是一些**状态**和这些状态转换的**规则**。DFA的语言就是满足DFA的所有**字的集合**。
2. 非确定有限自动机NFA
NFA的状态转换不唯一。
3. 带ε-转移的非确定有限自动机
4. 有限自动机的确定化
从NFA或ε-NFA构造等价的DFA的算法(**子集构造法**)。
5. DFA的最小化
使DFA的状态数最小的算法(**填表算法**)
6. 从正规表达式到有限自动机
从正规表达式构造ε-NFA的算法
3. 上下文无关文法
1. 上下文无关文法的基本概念
上下文无关文法=**终结符集**+**非终结符集**+**开始符号**+**产生式**
2. 归约与推导
**归约**是自下而上的过程,是将产生式的右部替换为产生式的左部。
**推导**是自上而下的过程,是将产生式的左部替换为产生式的右部。
3. 上下文无关语言
4. 句型,句子与分析树
**句型**是一个可以由开始符号推导出的单词序列
**句子**是全部由终结符组成的句型
把从开始符号到句子的推导过程用有向树的形式表现出来就叫**分析树**。
5. 归约、推导与分析树之间的关系
6. 文法的二义性
如果一个文法存在两棵及以上的分析树,则称它为**二义的**。如果存在一棵及以下的分析树,则称它为**无二义的**。

View File

@ -0,0 +1,21 @@
**关于编译原理课的文档**
formal-language.md: 以我的理解表述阅读材料《形式语言与自动机》
compiler-example.md: 一个简化的C语言编译器的说明文档。
decaf-pa1a.md: 关于课后实验pa1a的文档。
lecture1.md:简介
lecture2.md:实验引导一
lecture3-lexical.md: 词法分析
lecture4_5.md:语法分析
lecture6.md:符号表
lecture7.md:实验引导2
lecture8.md:语义计算基础

View File

@ -0,0 +1,25 @@
* 编译程序做的工作是把一种语言翻译成另一种语言。
个人认为,编译器面对的是两个事:**语言**和**自动机**。编译原理讲解的也是两个事:**设计**语言和**实现**自动机。**词法分析**讲解的是设计正则语言和有限自动机。**语法分析**讲解的是设计上下文无关语言和下推自动机。 **语法制导**和**中间代码生成**讲解的也是设计上下文无关方法和下推自动机。上下文无关语言从结构上看应该是树形结构,所以用在语法分析阶段用来推导抽象语法树,在中间代码阶段生成中间代码。正则语言从结构上看是线性结构,所以在词法分析阶段用来分析同样是线性结构的源代码。
* 编译过程分为分析和综合两个阶段。
分析就是把线性结构转换成非线性结构,结构上变复杂了,但对于处理复杂性来说反而变简单了。综合就是把非线性结构再转换成线性结构。对应到编译程序,前端(Front End)完成的是分析的工作,后端(Back End)完成的是综合的工作。中端(Middle End)完成的是中间代码的生成与优化,即有分析也有综合。
* 源代码的七十二变。
源代码刚开始是字符流,经过**词法分析**变成了单词流,经过**语法分析**变成了抽象语法树,经过**语义分析**变成了中间代码,中间代码经过**中间代码的生成与优化**变成了另一种中间代码,经过**目标代码生成**变成目标代码,目标代码还可以进行**目标代码优化**变成优化的目标代码。
* 符号表和错误处理
1. 符号表管理和错误处理遍布于编译的整个过程
2. 符号表看起来就是抽象语法树上添加了属性,每个结点都是一个符号表。
3. 错误处理要报告出错信息,而且期望能报告尽可能多的错误,而不是在找到一处错误就停止。
* 抽象语法树
源代码的逻辑结构是树形结构,把这个树形结构表示出来就是抽象语法树。
* 形形色色的编译程序
我更喜欢把它们都叫做编译程序,因为它们都完成的是把一种语言翻译成另一种语言的工作。它们是:解释器(Interpreter),预处理程序(Preprocessor),编译器(Compiler),汇编器(Assembler),装入和链接程序(Loader and Linker),调试程序(Debugger)。

View File

View File

@ -0,0 +1,16 @@
* 词法分析是把字符流翻译成单词流
所谓单词(Lexemes)就是对编译有意义的部分,对编译没有意义的部分(注释、空格、换行等)被直接丢弃掉了。单词由型(Token)和值(Attribute)两部分组成。
* 单词是怎么被识别出来的?
1. EBNF
2. 状态转换图
3. 正规表达式
4. 有限状态自动机
* 自动构造词法分析程序
词法分析在逻辑上是简单的,在实现上是复杂的,让简单回归简单,这就是**自动构造**存在的意义。

View File

@ -0,0 +1,26 @@
* 语法分析是把单词流翻译成抽象语法树
有两种构造抽象语法树的方法:从根结点到叶子结点构造(**自顶向下方法**),从叶子结点到根结点构造(**自底向上方法)**。语法分析总是从左到右依次扫描单词流这就是LL或LR文法里第一个字母L的含义。LL文法第二个L的意思是最左推导LR文法第二个R的意思是最右推导。
* 产生式、推导和归约
**产生式**是非终结符号的某种构造方法。**推导**是将产生式的左部替换为产生式的右部。**归约**是将产生式的右部替换为产生式的左部。**最左推导**总是选择每个句型的最左非终结符进行推导。**最右推导**总是选择每个句型最右的非终结符号进行推导。
* 自顶向下的语法分析
* 两个问题:一选择哪个终结符是不确定的,二选择哪个产生式是不确定的。可以通过最左推导的方法消除第一个不确定性。
* 消除左递归和提取左公因子
* LL(1)分析的1代表向前查看1个单词为什么要向前看因为要消除第二种不确定性。
* LL(1)文法要求对于任意非终结符其所有推导结果均不能相同。LL(1)文法是使LL(1)分析消除不确定性的文法。
* 怎么样才能使所有推导结果都不相同呢?预测集合不相交即可。
* first集是所有可能符号串的第一个终结符的集合。first集合的计算方法。
* follow集合是方法符号后面跟的可能的终结符的集合(不包括空字符)。follow集合的计算方法。
* LL(1)分析之递归下降
* LL(1)分析之表驱动
* 错误处理
* 自底向上的语法分析
* 两个问题:一选择哪个产生式是不确定的,二匹配哪个位置上的子串是不确定的。
* 移进归约分析,两种类型的冲突,表驱动方法
* LR分析

View File

View File

View File

View File

@ -0,0 +1,2 @@
* 表达式树(expression tree)以数据形式表示语言代码是一种抽象语法树或数据结构。表达式树与Lambda表达式相关联。表达式树也是一种简单的抽象语法树AST
表达式树的树叶是操作数(operand),结点是运算符(operator)。

View File

@ -0,0 +1,17 @@
正则表示法:
^表示行首匹配或补集:
^word //行首匹配
\[^xyz] //匹配不包含xyz的任意多个字符
word$ //行尾匹配
* //匹配前面的表达式零次以上
+ //匹配前面的表达式一次以上
//匹配前面的表达式零次或一次
. //任意一个字符,但不包括\n和\r
\ //转意特殊字符串
[xyz] //匹配xyz中意一个字符
{} //一个数字表示匹配次数,两个数字表示匹配范围,带有名字时表示以此名字命名的模式
| //选择操作符
"..." //引号中的字符将基于字面意思
//把一系列的正则表达式组成一个新的正则表达式
/ //尾部上下文

View File

@ -0,0 +1 @@
[](heap)

19
Data-Structure/heap.md Normal file
View File

@ -0,0 +1,19 @@
参考:[51cto博客_算法](http://363883844.blog.51cto.com/2977542/1544700)
#### 什么是堆
* 堆的关系满足k<sub>i</sub><=k<sub>2i</sub> ,k<sub>i</sub><=k<sub>2i+1</sub>或者k<sub>i</sub>>=k<sub>2i</sub> ,k<sub>i</sub>>=k<sub>2i+1</sub>)
* 堆的分类:顺序存储堆,链式存储堆(*个人认为顺序存储堆才是真正的堆,链式存储堆不就是二叉树么*
链式存储堆 = 左式堆 + 斜堆 + 二项堆
* 堆总是一颗完全二叉树,完全二叉树是其结点编号能与其所在的满二叉树完全对应的二叉树,满二叉树是每一层上的结点数都是该层的最大结点数的二叉树。
#### 顺序存储堆
* 顺序存储堆从物理结构上看是数组,从逻辑结构上看是完全二叉树。
#### 左式堆
* 每一个结点其左子树零路径长不小于右子树
* 零路径长:结点到一个叶子结点的最短路径长
#### 斜堆
* 与左式堆类似,不同之处在于合并后不管左右子树零路径大小都必须交换左右子树
#### 二项堆
* 二项堆的每个结点都是二叉树的根结点。合并时将相同度的二叉树合并。

8
Hardware/6845CGA.md Normal file
View File

@ -0,0 +1,8 @@
## CGA显示控制器68452个端口
```
显示控制索引端口0x3d4
存放光标位置的寄存器编号是14和15
数据端口0x3d5
显存范围0xb8000~0xbc000,每个字符对应2个字节第1个字节是ASCII码第2个字节是显示属性低4位是前景色高4位是背景色KRGBIRGB,K闪烁I亮度。
```

27
Hardware/8042kbd.md Normal file
View File

@ -0,0 +1,27 @@
## 8042键盘控制器(4个寄存器2个端口3个内部端口
```
寄存器Status Register, Control Register, Input Buffer, Output Buffer
读写Control Register:先向0x64写入命令(0x20读0x60写再通过0x60端口对Control Register进行相应的读或写的操作
0键盘中断1鼠标中断2设置状态寄存器的bit2,3设置状态寄存器的bit4,4禁止键盘5禁止鼠标6翻译扫描码7保留恒为0
读Output Buffer:读0x60端口
存储的是扫描码或命令响应
写Input Buffer:写0x60端口
存储的是命令及参数
读Status Register:读0x64端口
0输出缓冲器1输入缓冲器2系统标志3输入缓冲器中是命令还是数据,4键盘是否被禁止5发送超时6接收超时7奇偶校验
内部端口Input Port, Output Port, Test Port
读写Output Port:先向0x64写入命令(0xd0读0xd1写再通过0x60端口对Output Port进行相应的读或写的操作
读input port:先向0x64写入命令0xc0,再从0x60端口读出
命令和状态端口0x64
共有12种参数0x20, 0x60, 0xa4, 0xa5, 0xa6, 0xaa, 0xad禁止键盘, 0xae打开键盘, 0xc0, 0xd0, 0xd1, 0xd2)
0xa4:测试键盘密码是否被设置结果放在0x60端口
0xa5:设置键盘密码结果通过0x60端口放在Input Buffer中
0xa6:让通过0xa5命令设置的密码生效
0xaa:自检结果放在Output Buffer中可通过0x60端口读取
0xd2:写数据到Output Buffer通过0x60端口写入此功能用来模拟键盘发送的数据
数据端口0x60
movb $0xdf,%al
outb %al,0x60 打开A20地址线
```

6
Hardware/8251PCI.md Normal file
View File

@ -0,0 +1,6 @@
其实就是uart只是Intel把它叫做**Programmable Communication Interface**而已。
串口控制器3个端口
状态端口0x3f8+5
数据端口0x3f8

24
Hardware/8253timer.md Normal file
View File

@ -0,0 +1,24 @@
参考自[CSDN博客_Mr.Phoebe](http://blog.csdn.net/u013007900/article/details/50408903)
1. 8253芯片是可编程计数器/计时器(Counter/Timer)。因为计时和计数的本质是相同的,所以即可以叫计时器也可以叫计数器。
2. 内部有三个完全相同的计数器分别是计数器0计数器1,计数器2.
3. 端口号由引脚A1和A0确定。如果计数器0端口号为0x40则计数器1为0x41,计数器2为0x42,控制端口为0x43。
4. 8253编程的两条原则
* 必须先将控制字写入控制寄存器即0x43端口。
* 其次向计时器写入初值,初值必须符合控制字的规定。
5. 控制字有8位只能写入不能读出。
| D7 | D6 | D5 | D4 | D3 | D2 | D1 | D0 |
| --- | --- | --- | --- | -- | -- | -- | --- |
| SC1 | SC0 | RW1 | RW0 | M2 | M1 | M0 | BCD |
* D0计数码选择。决定计数器在减1过程中采用的进位制0表示二进制1表示BCD计数制。
* D3D2D1工作方式选择。
* 000:Interrupt on Terminal Count(计数结束产生中断)
* 001:Hardware Retriggerable one-shot(可编程单稳态触发器)
* 010:Rate Generator(分频器)
* 011:Square Ware Mode(方波发生器)
* 100:Software Triggered Strobe(软件触发选通脉冲)
* 101:Hardware Triggered Strobe(硬件触发选通脉冲)
* D5D4:读写格式控制。01,只读写低8位。10,只读写高8位。11,先读写低8位后读写高8位。00,把当前计数值存入输出锁存器,供以后读取。
* D7D6计数器选择。00,选择计数器0。01,选择计数器1。10,选择计数器2。11,在8253中为非法编码在8254中用于读回命令。
![8253](_img/8253timer.jpg)

37
Hardware/8259A_pic.md Normal file
View File

@ -0,0 +1,37 @@
#### 8259A概述
![8259A引脚](_img/8259A_pin.jpg "8259A引脚")
8259A芯片是一个中断管理芯片中断的来源除了来自于硬件自身的NMI中断和来自于软件的`INT n`指令造成的软件中断之外,还有来自于外部硬件设备的中断,这些中断是可屏蔽的。这些中断也都通过**PIC**(Programmable Interrupt Controller)进行控制并传递给CPU。
一个8259A芯片的可以接最多8个中断源但由于可以将2个或多个8259A芯片级连cascade并且最多可以级连到9个所以最多可以接64个中断源。如今绝大多数的PC都拥有两个8259A这样 最多可以接收15个中断源。
8259提供了两种屏蔽方式1,简单方式提供8位屏蔽字对应各个IR。2,特殊方式允许CPU让低优先级的外设去中断高优先级的服务程序。
#### 内部结构
![8259A内部结构](_img/8259A_structure.jpg)
- IRR:中断请求寄存器8位IR0IR7每一位对应一个设备共可接收8个设备的中断请求。
- IMR:中断屏蔽寄存器为8位设置需要屏蔽的中断请求
- ISR中断服务寄存器为8位保存当前正在处理的中断请求
- PR优先权判别器。当多个中断同时发生时将高优先级者优先传递给CPU。优先级选择方式有4种1,完全嵌套方式优先级从IR0到IR7依次降低。2,轮换方式A一个中断完成后立即把它放到最低优先级的位置上。3,轮换方式BCPU可以在任何时间规定最优优先级。4,查询方式CPU访问中断状态寄存器。
- 控制逻辑向CPU发出中断请求信号INT并接受CPU的中断响应信号INTA。
- 数据总线缓冲器:保存数据总线的数据,传输命令控制字、状态字和中断类型码。
- 读/写逻辑确定数据总线缓冲器中数据的传输方向选择内部的各命令字寄存器。RD为读WR为写AO为I/O端口识别CS为设备选择。
- 级联缓冲/比较器主从控制器的级联是由级联总线CAS0CAS1CAS2实现的。
#### 工作原理
![8259A级联](_img/8259A_link.jpg)
#### 实例:两个中断控制器连接
![8259A级联](_img/8259A_link2.gif)
Master 8259A:
0x20: ICW1,OCW2,OCW3,IRR,ISR
0x21: ICW2,ICW3,ICW4,IMR, address registers
Slave 8259A:
0xA0: ICW1,OCW2,OCW3,IRR,ISR
0xA1: ICW2,ICW3,ICW4,IMR, address registers

8
Hardware/A20.md Normal file
View File

@ -0,0 +1,8 @@
## fast gate A20: 1个端口0x92
```
inb $0x92,%al
orb $0x2,%al
outb %al,$0x92
```

27
Hardware/ACPI.md Normal file
View File

@ -0,0 +1,27 @@
#### 简介
*Advanced Configuration and Power Interface*,操作系统用它来查找和配置计算机硬件,让未使用的硬件休眠来进行电源管理,并进行状态监控。
##### RSDP
RSDP(Root System Description Pointer)是ACPI编程接口用到的数据结构。如下所示
```c
struct acpi_table_rsdp {
uint8_t signature[8]; /* "RSD PTR " */
uint8_t checksum; /* first 20 bytes must add up to 0 */
uint8_t oem_id[6];
uint8_t revision; // ACPI的修订号大的修定号向后兼容低的修订号。
uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */
uint32_t length; /* table length */
uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */
uint8_t extended_checksum; /* the entire table must add up to 0 */
uint8_t reserved[3];
} __packed;
```
RSDP有两个可能的位置一是在EBDA(Extended BIOS Data Area)的头1KBEBDA是16位的实模式段位于0x40e一是在内存区域从0x000e0000到0x000fffff。
RSDT(Root System Description Table)是ACPI编程接口用到的数据结构。
XSDT(eXtended System Descriptor Table)是RSDT的64位版本。

56
Hardware/APIC.md Normal file
View File

@ -0,0 +1,56 @@
#### 简介
Advanced Programmable Interrupt Controller是一个与8259A兼容的高级中断处理器。APIC分为两层Local APIC和I/O APIC。
![APIC](_img/APIC.jpg)
- I/O APIC I/O APIC是用来与外部设备通讯的它安装在主板上它完成了APIC最主要的功能中断处理。 I/O APIC提供了两个模式普通模式和8259A兼容模式。xv6在单核环境下会选择使用8259A兼容模式而在多处理器环境下则会使用普通模式。
- Local APIC是APIC的顶层每个核都有一个对应的Local APICLAPIC内置在CPU内部。它负责进行多处理器之间的中断传输屏蔽中断还提供了一个可编程的Timer。由于I/O APIC已经提供了中断处理的功能Local APIC只是起辅助作用可以屏蔽不用。
#### APIC的中断映射
| IRQ | Using SERIRQ | Direct form Pin | Using PCI Message | Internal Modules |
| ------ | ------------ | --------------- | ----------------- | ----------------------------- |
| 0 | | | | |
| 1 | | | | |
| 2 | | | | |
| 3 | | | | |
| 4 | | | | |
| 5 | | | | |
| 6 | | | | |
| 7 | | | | |
| 8 | | | | |
| 9 | | | | |
| 10 | | | | |
| 11 | | | | |
| 12 | | | | |
| 13 | | | | |
| 14 | | | | |
| 15 | | | | |
| 16~19 | PIRQA~PIRQD | PIRQA~PIRQD | Yes | Internal devices are routable |
| 2023 | N/A | PIRQH | Yes | |
#### 控制方式
APIC通过修改该单元的寄存器来实现控制但APIC的相关寄存器被映射到了物理地址。
- Local APIC默认映射到物理地址`0xFEE0 0000`
- IO APIC 默认映射到物理地址`0xFEC0 0000`
#### PRT表的格式
Programmable Redirection Table(24*64bit)IOAPIC上有24个interrupt pin每一个pin都对应一个RTE,所以针对每一个interrupt pin都可以单独设定它的mask触发方式(level,edge trigger),中断管脚的极性,传送方式,传送状态,目的地,中断向量等。
| bit | 意义 |
| ------ | ------------------------------------------------------------ |
| 6356 | Destination Field(Physical Mode:APIC ID, Logical Mode:一组CPU) |
| 5517 | Reserved |
| 16 | Interrupt Mask(1:屏蔽中断0:中断发给LAPIC) |
| 15 | Trigger Mode(1:Level电平触发。0Edge边沿触发) |
| 14 | Remote IRR,只读只对Level触发有效。1LAPIC接收了该中断0LAPIC写EOI |
| 13 | Interrupt Input Pin Polarity(INTPOL)(1:低电平0高电平) |
| 12 | Delivery Status(0:IDEL,当前没有中断1:Send PendingIOAPIC已收到中断但由于某种原因还没有发给LAPIC) |
| 11 | Destination Mode(0:Physical Mode, 1: Logical Mode) |
| 10:8 | Delivery Mode(0:Fixed, 1:Lowest Priority, 2: SMI, 4: NMI, 5:INIT, 7:ExtINT ) |
| 7:0 | Interrupt Vector(0x10~0xFE, X86架构的前16个vector被系统预留) |

208
Hardware/FU540-C000.md Normal file
View File

@ -0,0 +1,208 @@
#### 参考资料
[FU540-C000-v1.0](https://static.dev.sifive.com/FU540-C000-v1.0.pdf)
[Getting-Started-Guide](https://static.dev.sifive.com/HiFive-Unleashed-Getting-Started-Guide-v1p1.pdf)
#### 简介
HiFive Unleashed是U540 Soc的Linux开发平台是一个多核RISC-V SoC。硬件配置如下
| 硬件 | 属性 | 手册对应章节 |
| ------------ | ------------------------------------------ | ------------ |
| 内存 | 8G DDR4 | 5, 12, 20 |
| 以太网端口 | 千兆 | 19 |
| JTAG接口 | 用于调试 | 23, 34 |
| ChipLink | TileLink协议的片外串行化 | 22 |
| SD卡槽 | | |
| PCIe | FMC + 低速I/O扩展 | |
| E51 Core 0 | RV64IMAC(M,U mode),监控核 | 3 |
| U54 Core 1-4 | RV64GC(M,S,U mode),应用核 | 4 |
| L2 cache | 2M | 11 |
| 中断 | 平台级中断控制器PLIC计时器中断软件中断 | 8, 9, 10 |
| 串口 | uart0, uart1 | 13 |
| QSPI 0-3 | 串行外部接口 | 16 |
| PWM 0-1 | 脉冲宽度调制器 | 14 |
| I2C 0 | 集成电路控制器 | 15 |
| GPIO | 通用I/O引脚 | 17 |
| OTP | 一次可编程存储器 | 18 |
需要开发板USB线电源适配器可选配网线或FMC扩展卡
#### 时钟和复位(手册第7章)
时钟和复位是通过电源复位时钟中断(PRCI)架构来管理的。
#### 启动过程(手册第6章)
FU540-C000支持从多种源来启动这是通过模式选择(MSEL[3:0])引脚来实现的。而3个QSPI接口则控制从哪里下载media(SPI flash或SD卡)。
| MSEL | FSBL的位置 | BBL的位置 | 作用 |
| ---- | -------------- | -------------- | --------------------------------- |
| 0000 | - | - | 无限循环等待调试 |
| 0001 | - | - | 跳至0x2000_0000(内存映射OSPI0) |
| 0010 | - | - | 跳至0x3000_0000(内存映射OSPI1) |
| 0011 | - | - | 跳至0x4000_0000(未缓存的ChipLink) |
| 0100 | - | - | 跳至0x6000_0000(缓存的ChipLink) |
| 0101 | QSPI0 x1 flash | QSPI0 x1 flash | - |
| 0110 | QSPI0 x4 flash | QSPI0 x4 flash | 从flash 获取镜像 |
| 0111 | QSPI1 x4 flash | QSPI1 x4 flash | - |
| 1000 | QSPI1 x1 SD | QSPI1 x1 SD | - |
| 1001 | QSPI2 x1 flash | QSPI2 x1 flash | - |
| 1010 | QSPI0 x4 flash | QSPI1 x1 SD | - |
| 1011 | QSPI2 x1 SD | QSPI2 x1 SD | 从SD卡获取镜像 |
| 1100 | QSPI1 x1 flash | QSPI2 x1 SD | - |
| 1101 | QSPI1 x4 flash | QSPI2 x1 SD | - |
| 1110 | QSPI0 x1 flash | QSPI2 x1 SD | - |
| 1111 | QSPI0 x4 flash | QSPI2 x1 SD | 默认启动模式 |
##### 1. 复位向量
一加电所有的核都跳到ROM 0x1004处执行ROM中的内容如下
```
0x1000 MSEL的值
0x1004 auipc t0, 0 # t0保存了当前指令的地址即0x1004
0x1008 lw t1, -4(t0) # t1保存的内存0x1000的值即MSEL的值
0x100c slli t1, t1, 0x3 # t1 = t1 << 3即8*t1
0x1010 add t0, t0, t1 # t0 += t1
0x1014 lw t0, 252(t0) # t0保存的内存地址0x1000+0x100+8*MSEL处的值
0x1018 jr t0 # 实际上最终跳转的位置由MSEL的值决定
```
当MSEL>4的时候均是跳转到ROM 0x1_0000处执行即ZSBL。
##### 2. ZSBL
ZSBL是零阶段的引导程序它负责从GPT下载FSBL。编号为0的核首先配置时钟分频器然后寻找GUID为`5B193300-FC78-40CD-8002-E86C45580B47`的分区。它首先下载GPT头(位置在512字节604字节),然后依次扫描分区表直到找到分区,接下来将该分区的内容(即FSBL)下载到内存0x0800_0000处最后跳转到该地址执行。
ZSBL是通过MSEL的值来决定FSBL所在的分区的。
##### 3. FSBL
FSBL是第一阶段的引导它负责将内核载入内存。
- 通过配置和运行片上PLL将频率提升至1GHz。
- 配置DDR PLL以太网PHY和控制器。
- 设置GEMGXLTXPLL为125MHz并重置之。
- 如果有外部PHY重置之。
- 从GUID号为`2E54B353-1271-4842-806F-E436D6AF69851`的分区下载BBL。
- Scan OTP for the chip serial number
- 将DTB复制到内存
- 跳转到`0x8000_0000`
FSBL通过MSEL的值来寻找BBL所在的分区。
##### 4. BBL
BBL在`0x8000_0000`它提供SBI并模拟RISC-V指令。Linux在`0xffff_ffe0_0000_0000`处。
##### 5. 启动方法
- Flash Bit-Banged x1
- Flash 内存映射x1
- Flash 内存映射x4
- SD卡Bit-Banged x1
#### 软件开发流程
1. 安装需要的包
```shell
sudo apt-get install autoconf automake autotools-dev bc bison \
build-essential curl flex gawk gdisk git gperf libgmp-dev \
libmpc-dev libmpfr-dev libncurses-dev libssl-dev libtool \
patchutils python screen texinfo unzip zlib1g-dev
```
2. 安装free-u-sdk
```shell
git clone https://github.com/sifive/freedom-u-sdk.git
cd freedom-u-sdk
git submodule update --init --recursive
unset RISCV
make
```
编译时问题解决:
- `Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/\${ <-- HERE (\[^ \t=:+{}]+)}/ at /data/szx/Code/freedom-u-sdk/work/buildroot_initramfs/host/usr/bin/automake line 3936.`
以上问题的解决方法:给大括号转义,变成如下形式`\{`
- `./../misc/create_inode.c:395:18: error: conflicting types for copy_file_range`
以上问题的解决方法这是由于自定义函数和库函数重名了解决方法就是给自定义函数换个名字。首先通过grep命令找到所有定义和使用了copy_file_range函数的地方然后把所有的copy_file_range函数更名为copy_file_chunk。2个文件共计8处需要修改。
> 编译完毕,显示如下信息:
>
> This image has been generated for an ISA of rv64imafdc and an ABI of lp64d
> Find the image in work/bbl.bin, which should be written to a boot partition
>
> To completely erase, reformat, and program a disk sdX, run:
> sudo make DISK=/dev/sdX format-boot-loader
> ... you will need gdisk and e2fsprogs installed
3. 把镜像复制到SD卡
```
sudo umount /dev/exb # 假设SD卡名叫exb,先要从文件系统中卸载掉
sudo gdisk /dev/exb # 为exb创建GPT
# p打印分区信息d删除分区o清除所有分区数据w写数据(即将更改作用于设备)
sudo make DISK=/dev/exb format-boot-loader # 将系统写入SD卡
```
写卡时的问题与修复:
- 写低速SD卡有可能会失败多重复几次即可成功。
4. 在开发板上运行镜像
1. 拔电源,关开关。(关开关并不能代替拔电源。)
2. 确保风扇已连接。
3. 将DIP开关拔到左边(左边为1,右边为0)使MSEL处于1111模式。这是默认的启动模式。
4. 插入安装了BBL+Linux的SD卡。
5. 连接网线和USB线。
6. 插上电源,风扇开始转动。
7. 打开开关30秒后LED灯开始闪烁。
有两种方式连到开发板ssh和串口。串口慢且不能接收文件应尽量选择ssh方式。
- ssh连接
电源开启后默认的freedom-u-sdk镜像会通过DHCP获取IP地址并开启ssh服务。
`ssh root@ip地址`登陆密码sifive
- USB连接
`sudo screen /dev/ttyUSB1 115200`
用户名root密码sifive
5. 在qemu中运行镜像
`make qemu`
编译时问题的解决:
- `Unescaped left brace in regex is illegal here in regex; marked by <-- HERE in m/\${ <-- HERE ([^ \t=:+{}]+)}/ at /data/szx/Code/freedom-u-sdk/work/buildroot_rootfs/host/usr/bin/automake line 3936.`
以上问题的解决方法依然是给大括号转义。
- `./../misc/create_inode.c:395:18: error: conflicting types for copy_file_range`
以上问题的解决方法依然是给本地函数重命名。
#### 连接器引脚分配
##### FMC
FPGA Mezzanine Card
##### 低速I/O扩展
##### MicroUSB
##### SD卡
##### JTAG

18
Hardware/IO.md Normal file
View File

@ -0,0 +1,18 @@
#### 两种I/O
1. CPU只能读取三个地方的数据寄存器内存端口
1. CPU与外设有两种交互方式内存映射IO(MMIO)与端口IO(PMIO)
##### 端口映射I/O
1. 端口读写指令只有2条in, out 。这两个指令的硬件实现本质上和读写内存是相同的。
1. 端口地址范围065535 (本机端口:/proc/ioports)
1. 只能用ax或al来存放与端口交换的数据
##### 内存映射I/O
很多计算机体系结构都没有单独的设备访问指令,取而代之的是让设备拥有固定的内存地址,然后通过内存读写实现设备读写。实际上现代x86体系结构就在大多数高速设备上(如网络、磁盘、显卡控制器)使用了该技术,叫做内存映射I/O。
#### DMA
内存映射(MMIO和PMIO)作为一种CPU对I/O设备(CPU-to-device)的通信方法并不影响DMA(直接内存访问), 因为DMA是一种绕过CPU的内存对设备(memory-to-device)的通信方法。

15
Hardware/NUMA.md Normal file
View File

@ -0,0 +1,15 @@
#### NUMA简介
numa把一台计算机分成多个节点(node),每个节点内部拥有多个CPU节点内部使用共有的内存控制器节点之间是通过互联模块进行连接和信息交互。
#### NUMA和SMP的区别CK老师的解释
NUMA和SMP的区别在性能上。SMP是对称多处理NUMA是非统一内存访问。
SMP的每一颗处理器到内存的距离都是一样的。传统上所有处理器都连接着北桥芯片(即内存控制器)这样的结构是对称的可以认为是SMP。
现在,内存控制器做在处理器里面了,这样每一个处理器都可以控制一部分内存。要访问另一颗处理器控制的内存,就必须通过处理器的通信线路,这样访问自己的内存就快,访问别人的内存就慢,这样就存在一个处理器亲和力的问题。解决方法就是内核线程要绑定到一个处理器的一个核心中,不要老跑来跑去。
有这样一个结论任何针对单核处理器所做的优化在多核上面都要创建一个新的Memory Model。
对内存有兴趣的同学可以看两个文档:**All Programmer should Know About Memory**和**Tutorial On Memory Model**。前提是要先清楚**Sequential Consistency**的概念。

1
Hardware/TLB.md Normal file
View File

@ -0,0 +1 @@
- TLB(Translation Lookaside Buffer)翻译后备缓冲器是在MMU中的一个对PTE的小的缓存。由于MMU比L1缓存快的多所以可以提高CPU执行的效率。但如果PTE更新了则需要刷新TLB。

26
Hardware/VT-x.md Normal file
View File

@ -0,0 +1,26 @@
#### 简介
Intel的虚拟化技术包含了三方面的虚拟化**CPU虚拟化**是VT-x或VT-i**内存虚拟化**是EPT(Extended Page Table)**I/O虚拟化**是VT-d(Intel Virtualization Technology for Directed I/O)。
VT-x是Intel虚拟化技术的指令集在x86平台上的VT技术被称为VT-x在itanium平台上的VT技术被称为VT-i
![intel-vmx](_img/intel_vmx.png)
它引入了VMX操作模式有两种VMX操作模式**VMX root operation**是为VMM/Hypervisor使用的其行为与传统的IA32一样**VMX not-root operation**则是为Guest使用的是在VMM控制下的IA32环境。Guest是运行在Ring0下的Guest运行的指令一般可以直接控制硬件只有需要执行特殊指令才会切换到VMM。
VMX操作流程
1. 执行`vmxon`指令进入VMX操作模式。
2. 执行`vmlaunch`或`vmresume`指令产生VMEntry操作进入到Guest OS。
3. Guest OS执行特权指令导致VMExit操作陷入到VMM。
4. VMM可以通过`vmxoff`指令退出VMX操作。
![intel-vmcs](_img/intel_vmcs.png)
VT-x设计了一个Virtual-Machine Control Structure(VMCS)的数据结构CPU在发生VMEntry或VMExit时都会查询和更新VMCS。
- 03字节VMCS版本标识
- 47字节VMX中止的原因
- 8字节end数据区。由3个部分组成控制区域(虚拟执行控制域VMExit控制域VMEntry控制域VMExit信息域)Guest区(寄存器状态,非寄存器状态)Host区(主要是VMM运行时的状态)。
[Intel Virtualization Technology for Directed I/O: Architecture Specification](https://software.intel.com/sites/default/files/managed/c5/15/vt-directed-io-spec.pdf)

BIN
Hardware/_img/80386CPU.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

BIN
Hardware/_img/80386_pin.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
Hardware/_img/8253timer.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

BIN
Hardware/_img/8259A_pin.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

BIN
Hardware/_img/APIC.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
Hardware/_img/bus_isa.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
Hardware/_img/bus_pci.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
Hardware/_img/intel_vmx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

11
Hardware/bus.md Normal file
View File

@ -0,0 +1,11 @@
* 总线是电路的通路它连接了CPU芯片显卡芯片内存芯片和I/O控制芯片等。
* 总线标准有XTAT(ISA)MCAEISAPCIUSB用以兼容不同厂家的设备。
* ISA总线结构
![ISA总线](_img/bus_isa.jpeg)
* PCI总线
![PCI总线](_img/bus_pci.jpeg)

10
Hardware/cpu_rocket.md Normal file
View File

@ -0,0 +1,10 @@
#### 概览
下图是对Rocket_chip的概览。
![rocket_chip](_img/rocket_chip.png)
- Rocket Tile一个Rocket core和对应的L1缓存。
- htifHost/Target interfaceHost指ARM PSTarget指RISC-V。
- coherence manager一致性管理器管理L1缓存和L2缓存的一致性。
- TileLinkRocet Tile与一致性管理器之间的通信协议。

24
Hardware/cpu_x86.md Normal file
View File

@ -0,0 +1,24 @@
- CPU(中央处理单元,或处理器)其实只是在执行一个非常简单的循环:从一个被称为『程序计数器』的寄存器中获取一个内存地址,从个地址读出机器指令,增加程序计数器的值,执行机器指令,不断反复。
- 速度最快的数据存储器是处理器的寄存器组。
| X86寄存器类型 | 数量 | 寄存器名称 |
| ------------- | ------- | ------------------------------------------- |
| 通用寄存器 | 8(32位) | %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp, %esp |
| 程序计数器 | 1(32位) | %eip(instruction pointer) |
| 浮点寄存器 | 8(80位) | 名称未知 |
| 控制寄存器 | 4 | %cr0,%cr2,%cr3,%cr4 |
| 调试寄存器 | 4 | %dr0,%dr1,%dr2,%dr3 |
| 段寄存器 | 6 | %cs,%ds,%es,%fs, %gs,%ss |
| 伪寄存器 | 2 | %gdtr,%ldtr |
- 缓存是主存和寄存器在速度和大小上的折衷。
- 80386CPU
![80386](_img/80386CPU.jpg)
- 80386引脚
![80386_pin](_img/80386_pin.jpg)

11
Hardware/ide.md Normal file
View File

@ -0,0 +1,11 @@
### 硬盘控制器ide控制器最多支持4个硬盘
0x1f0:读数据应该是4字节大的端口
0x1f2:需要读写的扇区数量
0x1f3:LBA参数的07位 //LBA参数即要读写扇区的起始扇区号
0x1f4:LBA参数的815位
0x1f5:LBA参数的1623位
0x1f6:03LBA2427位40主盘1从盘61LBA模式0CHS模式5和7固定为1
0x1f7:状态和命令寄存器
0x20命令读取硬盘
第7位为1表示硬盘忙
第7位为0,第3位为1表示硬盘空闲可以读取指令

9
Hardware/parallel.md Normal file
View File

@ -0,0 +1,9 @@
## 并口控制器3个端口
```
命令端口0x37a
状态端口0x379
数据端口0x378
```
##

99
Hardware/zedboard.md Normal file
View File

@ -0,0 +1,99 @@
### 功能测试
在ZedBoard自带的SD卡上就有一个简单的Linux镜像用于展示Zedboard的基本功能。
#### 1. 连接跳线
确保跳线按如下形式连接:
| 标识符 | 连接形式 | 意义 |
| ------ | -------- | -------- |
| JP11 | GND | 使用PLL |
| JP10 | 3V3 | SD卡启动 |
| JP9 | 3V3 | SD卡启动 |
| JP8 | GND | SD卡启动 |
| JP7 | GND | JTAG级联 |
| JP6 | Shorted | SD卡启动 |
| J18 | 1.8V | 文档要求 |
#### 2. 启动系统
插入SD卡开发板上Uart接口与电脑相连连接电源拨动开关命令窗口中输入`sudo minicom -D /dev/ttyACM0`。看到提示符`zed-boot>`即表示成功进入U-BOOT。
输入`printenv`可以看到环境变量。
#### 3. 启动Linux
输入`run sdboot`开开始启动Linux当看到提示符`zynq>`即表示成功进入linux。可以看到开发板上的显示屏显示出了图案。
```
read_sw # 读取开关的状态
write_led 0xF0 # 使左边4个LED灯开右边4个关
write_led 0xF # 使左边4个关右边4个关
write_led 0 # 使8个LED灯关闭
poweroff # 关闭系统
```
### 运行Rocket
#### 1. 制作SD卡
```
git clone https://github.com/ucb-bar/fpga-zynq.git
cd fpga-zynq/zedboard
make fetch-images
# 电脑上插入SD卡假定挂载在/mnt/abc
make load-sd SD=/mnt/abc
# SD卡再插入开发板
```
> boot.binsystem.bit + u-boot.elf + zynq_fsbl.elf
>
> uImageARM Linux
>
> uramdisk.image.gz包含根文件系统的RAMDisk
>
> devicetree.dtb设备树
>
> riscv/vmlinuxRISC-V Linux内核
>
> riscv/root.bin包含根文件系统的RAMDisk(for RISC-V Linux)
#### 2. 启动Linux
在U-BOOT界面输入`run sdboot`启动Linux用户名和密码均为root。
然而这有个问题就是无法启动riscv核。我推测是riscv核根本就没有烧写到开发板里。
### 硬件结构
#### 跳线
| 标识符 | 描述 | 默认设置 | 功能 |
| -------- | --------------------------- | --------------------- | --------------------------------------- |
| JP1 | 卖克风输入bias | Open-无Electret卖克风 | 短接使能偏压 |
| JP2 | Vbus 5V 使能 | Open-5V无连接 | 短接使能5V输出到OTG连接器 |
| JP3 | Vbus电容器设置 | Open-设备模式 | 短接用于Host模式Open用于设备或OTG模式 |
| JP4 | CFGBVS选择 | 无填充 | |
| JP5 | PUDC选择 | 无填充 | |
| JP6 | MIO[0] | Short | SD卡启动 |
| JP7 | Boot_Mode[3]/MIO[2] | GND-级联JTAG | JTAG模式。 |
| JP8,9,10 | Boot_Mode[0,1,2]/MIO[3,4,5] | 110-SD卡 | 选择启动设备 |
| JP11 | Boot_Mode[4]/MIO[6] | GND-PLL用 | PLL选择。GND使用PS PLLVCC绕过PS PLL. |
| JP12 | XADC | Open | |
| JP13 | JTAG PS-RST | Open | 短接将JTAG PROG-RST连接到PS Reset. |
| JP18 | Vadj选择 | 1.8V | |
#### 启动模式
| | MIO6/JP11 | MIO5/JP10 | MIO4/JP9 | MIO3/JP8 | MIO2/JP7 |
| -------------- | --------- | --------- | -------- | -------- | -------- |
| JTAG模式(级联) | | | | | 0 |
| JTAG模式(独立) | | | | | 1 |
| 启动设备(JTAG) | | 0 | 0 | 0 | |
| 启动设备(QSPI) | | 1 | 0 | 0 | |
| 启动设备(SD卡) | | 1 | 1 | 0 | |
| PLL(使用) | 0 | | | | |
| PLL(绕过) | 1 | | | | |

View File

@ -0,0 +1 @@
LDR

View File

@ -0,0 +1,846 @@
RISC-V ISA手册
本手册分为两卷,上卷讲用户级指令,下卷讲特权级指令。
### 一些待解决的问题
#### 1. riscv里的ecall指令与x86里的int指令的异同。
ecall是用来触发异常的而int是用来触发中断的感觉它们用的地方也很相似那么它们之间有什么异同呢
#### 2. 为什么即有LW指令也有LWU指令而只有SW指令没有SWU指令呢
如果无符号数的宽度小于寄存器的宽度执行普通的LW指令就会有问题因为它会进行符号扩展这就需要LWU指令把高位全都填充为0。而把数保存到寄存器中就不存在这样的问题因为数的大小和占用的内存大小是完全一致的。
#### 3. 为什么有算术右移而没有算术左移呢?
### RISC-V 手册上卷(riscv-spec-v2.2)
#### 1. 介绍
设计目标。不使用商业ISA的原因。不使用OpenRISC的原因。
1. RISC-V ISA概述
共有3种类型的指令基本指令标准扩展指令和特殊扩展指令。基本指令被命名为I是不可更改必须被包含的指令它们都是整数指令。标准扩展指令包括M(扩展乘除指令)A(扩展原子指令)F(单精度浮点指令扩展)D(双精度浮点指令扩展)G(IMAFD五种指令的总和提供了完整的通用标量指令集是默认工具链支持的指令集)。特殊扩展指令只用于特定领域,不适用于全部通用领域。
2. 指令长度编码
基本指令固定是32位的然而扩展指令可以是变长的但必须是16的倍数。当低2位不是11时指令长度为16位当低2位是11时指令长度为32位当低5位为11111时指令长48位当低6位为111111时指令长64位当低7位全1则要计算[14:12]位的数才能得出指令长度。
基本指令集是小端系统,但扩展指令集即可做成小端也可做成大端。
3. 异常、陷阱和中断
异常是指令错误引起的,陷阱是线程自主调用的,中断是外部事件引起的。
#### 2. RV32I指令集
RV32I是基本指令集它被设计足以支持一个现代的操作系统而且除了A扩展外它基本上能模拟所有的其它扩展。RV32I共计47条指令。
##### 1. 程序员模型
用户可见的寄存器有3种1个恒为0的x031个通用寄存器x1-x31pc。
##### 2. 基本指令格式
在基本ISA里所有指令都是32位且必须4字节对齐。共有四种基本格式R-type是寄存器-寄存器操作I-type是寄存器-立即数操作S-typeU-type。
立即数字段里放的是立即数的位置,而不是它本身。
R-type: **funct7**[31:25] **rs2**[24:20] **rs1**[19:15] **funct3**[14:12] **rd**[11:7] **opcode**[6:0]
I-type: **imm[11:0]**[31:20] **rs1**[19:15] **funct3**[14:12] **rd**[11:7] **opcode**[6:0]
S-type: **imm[11:5]**[31:25] **rs2**[24:20] **rs1**[19:15] **funct3**[14:12] **imm[4:0]**[11:7] **opcode**[6:0]
U-type: **imm[31:12]**[31:12] **rd**[11:7] **opcode**[6:0]
##### 3. 立即数编码的变体
B是S的变体J是U的变体
B-type: **imm[12]imm[10:5]**[31:25] **rs2**[24:20] **rs1**[19:15] **funct3**[14:12] **imm[4:1]imm[11]**[11:7] **opcode**[6:0]
J-type:**imm[20]imm[10:1]imm[11]imm[19:12]**[31:12] **rd**[11:7] **opcode**[6:0]
##### 4. 整数计算指令(共计22条指令)
| imm+rs1+funct3+rd+opcode | funct3 | opcode | 意义 |
| ---------------------------------------- | ------ | ------ | ---------------------------------------- |
| ADDI rd, rs1, imm | ADDI | OP-IMM | rd = rs1 + imm |
| SLTI rd, rs1, imm (set less than immediate) | SLTI | OP-IMM | rd = (rs1<imm) ? 1 : 0(用于有符号数) |
| SLTIU rd, rs1, imm | SLTIU | OP-IMM | rd = (rs1<imm) ? 1 : 0(用于无符号数);**注sltiu rd,rs,1的意思是rd = (rs==0)?1:0;** |
| ANDI rd, rs1, imm | ANDI | OP-IMM | rd = rs1 & imm |
| ORI rd, rs1, imm | ORI | OP-IMM | rd = rs1 \| imm |
| XORI rd, rs1, imm | XORI | OP-IMM | rd = rs1 ^ imm |
| SLLI rd, rs1, imm | SLLI | OP-IMM | rd = rs1 << imm[4:0] |
| SRLI rd, rs1, imm | SRLI | OP-IMM | rd = (unsigned) rs1 >> imm[4:0] |
| SRAI rd, rs1, imm | SRAI | OP-IMM | rd = (signed) rs1 >> imm[4:0] |
| imm+rd+opcode | opcode | 意义 |
| ---------------------------------------- | ------ | --------------------- |
| LUI rd, imm (load upper immediate) | LUI | rd = (imm << 12) |
| AUIPC rd, imm (add upper immediate to pc) | AUIPC | rd = (imm << 12) + PC |
| funct7+rs2+rs1+funct3+rd+opcode | funct7 | funct3 | opcode | 意义 |
| ------------------------------- | ------- | ------ | ------ | ---------------------------------------- |
| ADD rd, rs1, rs2 | 0000000 | ADD | OP | rd = rs1 + rs2 |
| SLT rd, rs1, rs2 | 0000000 | STL | OP | rd = (rs1<rs2) ? 1 : 0 (用于有符号数) |
| SLTU rd, rs1, rs2 | 0000000 | STLU | OP | rd = (rs1<rs2) ? 1 : 0 (用于无符号数);**注sltu rd, x0, rs的意思是rd = (rs != 0) ? 1 : 0;** |
| AND rd, rs1, rs2 | 0000000 | AND | OP | rd = rs1 & rs2 |
| OR rd, rs1, rs2 | 0000000 | OR | OP | rd = rs1 \| rs2 |
| XOR rd, rs1, rs2 | 0000000 | XOR | OP | rd = rs1 ^ rs2 |
| SLL rd, rs1, rs2 | 0000000 | SLL | OP | rd = rs1 << (rs2 & 0x1F ) |
| SRL rd, rs1, rs2 | 0000000 | SRL | OP | rd = (unsigned) rs1 >> (rs2 & 0x1F) |
| SUB rd, rs1, rs2 | 0100000 | SUB | OP | rd = rs1 - rs2 |
| SRA rd, rs1, rs2 | 0100000 | SRA | OP | rd = (signed) rs1 >> (rs2 & 0x1F) |
NOP指令的编码ADDI x0, x0, 0
##### 5. 控制转移指令(8条指令)
- 无条件跳转(U-type or J-type)
| 指令 | 编码 | 意义 |
| ----------------------------------------- | --------------------------------- | ----------------------------- |
| JAL rd, imm(jump and link) | imm+rd+JAL(opcode) | rd = pc + 4; pc += imm; |
| JALR rd, rs1, imm(jump and link register) | imm+rs1+0(funct3)+rd+JALR(opcode) | rd = pc + 4; pc += rs1 + imm; |
- 条件分支(S-type or B-type)
| imm+rs2+rs1+funct3+imm+opcode | funct3 | opcode | 意义 |
| ----------------------------- | -------- | ------ | ------------ |
| BEQ rs1,rs2,offset | BEQ | BRANCH | if(rs1==rs2) |
| BNE rs1,rs2,offset | BNE | BRANCH | if(rs1!=rs2) |
| BLT/BLTU rs1, rs2, offset | BLT/BLTU | BRANCH | if(rs1<rs2) |
| BGE/BGEU rs1, rs2, offset | BGE/BGEU | BRANCH | if(rs1>=rs2) |
##### 6. LOAD和STORE指令(8条指令)
| imm+rs1+funct3+rd+opcode | funct3 | opcode | 意义 |
| ------------------------ | ------ | ------ | --------------- |
| LW rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
| LH rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
| LHU rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
| LB rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
| LBU rd, imm(rs1) | 宽度 | LOAD | rd=mem(rs1+imm) |
| imm+rs2+rs1+funct3+imm+opcode | funct3 | opcode | 意义 |
| ----------------------------- | ------ | ------ | ---------------- |
| SW(32位) | 宽度 | STORE | mem(rs1+imm)=rs2 |
| SH(16位) | 宽度 | STORE | mem(rs1+imm)=rs2 |
| SB(8位) | 宽度 | STORE | mem(rs1+imm)=rs2 |
##### 7. 内存模型(2条指令)
本节已 **过时**,新的内存模型正在修订中。
| imm[11:0] | rs1 | funct3 | rd | opcode | 意义 |
| --------------------------------- | ------- | ------- | ------- | -------- | --------- |
| imm[11:8]保留imm[7:4]前续imm[3:0]后续 | 保留应置为0 | FENCE | 保留应置为0 | MISC-MEM | 顺序化IORW访问 |
| imm[11:0]保留应置为0 | 保留应置为0 | FENCE.I | 保留应置为0 | MISC-MEM | 同步指令和数据流 |
##### 8. 控制和状态寄存器指令(6条指令)
- CSR指令
| csr+rs1+funct3+rd+opcode | rs1 | funct3 | opcode | 意义 |
| ------------------------ | --------- | ------ | ------ | ---------------------------- |
| csrrw rd, csr, rs1 | source | CSRRW | SYSTEM | tmp=rs1; rd=csr; csr=tmp |
| csrrs rd, csr, rs1 | source | CSRRS | SYSTEM | tmp=rs1; rd=csr; csr\|=tmp |
| csrrc rd, csr, rs1 | source | CSRRC | SYSTEM | tmp=rs1; rd=csr; csr &= !tmp |
| csrrwi rd, csr, rs1 | uimm[4:0] | CSRRWI | SYSTEM | rd=csr; csr=uimm[4:0] |
| csrrsi rd, csr, rs1 | uimm[4:0] | CSRRSI | SYSTEM | rd=csr; csr\|=uimm[4:0] |
| csrrci rd, csr, rs1 | uimm[4:0] | CSRRCI | SYSTEM | rd=csr; csr\|=uimm[4:0] |
- 计时器和计数器(伪指令)
| csr+rs1+funct3+rd+opcode | csr | rs1 | funct3 | opcode | 意义 |
| ------------------------ | ------------- | ---- | ------ | ------ | -------------- |
| rdcycle[h] rd | RDCYCLE[H] | 0 | CSRRS | SYSTEM | 读取cycle CSR |
| rdtime[h] rd | RDTIME[H] | 0 | CSRRS | SYSTEM | 读取time CSR |
| rdinstrent[h] rd | RDINSTRENT[H] | 0 | CSRRS | SYSTEM | 读取instrent CSR |
##### 9. 环境调用和断点(2条指令)
| funct12 | rs1 | funct3 | rd | opcode | 意义 |
| ------- | ---- | ------ | ---- | ------ | ------------ |
| ECALL | 0 | PRIV | 0 | SYSTEM | 用于产生对执行环境的请求 |
| EBREAK | 0 | PRIV | 0 | SYSTEM | 调试器用它返回调试环境 |
ECALL指令只是触发一个当前特权级下的exception并不执行其它操作。
ECALL可以在各自的特权级下产生不同的exception,所以可以选择性地委派call exception。对于Unix-like操作系统来说一个典型的用法是通过U-mode下的调用切换到S-mode。
#### 3. RV32ERV32I的精简版为嵌入式系统而设计
1. 程序员模型通用寄存器从31个(x1~x31)减少到15个(x1~x15)
2. 指令集与RV32I相同但计时器和计数器伪指令不是必须的。
3. 扩展可进行M,A,C扩展只有两种特权级(用户模式和机器模式)
#### 4. RV64IRV32I的变体只描述与RV32I的不同之处。
1. 寄存器状态寄存器扩展为64位且支持64位用户地址空间。
2. 整数计算指令(增加了9条特有指令)
RV64I的指令长度也是32位的操作的是64位数值。但也提供了变种指令来操作32位数值这些指令的操作码后面都增加了后缀“W"是RV64I特有的指令。
| imm | rs1 | funct3 | rd | opcode |
| --------- | ---- | ------ | ---- | --------- |
| imm[11:0] | src | ADDIW | dest | OP-IMM-32 |
| imm[4:0] | src | SLLIW | dest | OP-IMM-32 |
| imm[4:0] | src | SRLIW | dest | OP-IMM-32 |
| imm[4:0] | src | SRAIW | dest | OP-IMM-32 |
| funct7 | rs2 | rs1 | funct3 | rd | opcode |
| ------- | ---- | ---- | --------- | ---- | ------ |
| 0000000 | src2 | src1 | ADDW | dest | OP-32 |
| 0000000 | src2 | src1 | SLLW/SRLW | dest | OP-32 |
| 0100000 | src2 | src1 | SUBW/SRAW | dest | OP-32 |
3. Load和Store指令(增加了3条指令)
增加LD(64位)LWU(32位无符号)SD(64位三条指令。
4. 系统指令与RV32I完全相同只是操作的对象换成了64位的。需要注意的是伪指令rdcycle,rdtime,rdinstret分别读取的是相应寄存器的全部64位(cycle,time,instret计数器)所以rdcycleh,rdtimeh,rdinstreth在RV64I里就不需要了是非法的。
#### 5. RV128I
#### 6. M整数乘除扩展
1. 乘法操作
| funct7+rs2+rs1+funct3+rd+opcode | funct7 | funct3 | opcode | 意义 |
| ------------------------------- | ------ | --------------- | ------ | ----------------------------------- |
| MUL/MULH\[[S]U] rd, rs1, rs2 | MULDIV | MUL/MULH\[[S]U] | OP | MUL将乘法的低位放入目标寄存器MULH则是放高位 |
| MULW rd, rs1, rs2 | MULDIV | MULW | OP-32 | 仅用于RV64计算源寄存器的低32位再用MUL指令获取高32位的值 |
2. 除法操作
| funct7+rs2+rs1+funct3+rd+opcode | funct7 | funct3 | opcode | 意义 |
| ------------------------------- | ------ | --------------- | ------ | ---------------------- |
| DIV[U]/REM[U] rd, rs1, rs2 | MULDIV | DIV[U]/REM[U] | OP | rs1/rs2。DIV提供商REM提供余数 |
| DIV[U]W/REM[U]W rd, rs1, rs2 | MULDIV | DIV[U]W/REM[U]W | OP-32 | 仅用于RV64,源寄存器的低32位相除。 |
#### 7. A原子扩展
原子指令是原子性地“读-改-写”内存以支持同一内存空间中多个硬件线程之间的同步。共有两种形式的原子指令load-reserved/store-conditional指令和原子“获取-操作”内存指令。它们都支持各种内存一致性排序都支持RCsc内存一致性模型。
1. 原子指令的顺序
基本RISCV ISA使用的是宽松的内存模型用FENCE指令来施加额外的次序约束。
为了更有效地支持一致性每个原子指令都有两个位aq和rl用于指定其他的RISC-V harts所观察到的额外的内存排序约束。
如果两个比特都清空了那么就不会对原子内存操作施加额外的排序约束。如果只设置了aq位则将原子内存操作视为**获取访问**,即:在**获取访问**操作之前在这个RISC-V hart中没有任何后续的内存操作可以被观察到。如果只设置rl位则将原子内存操作视为一个**释放访问**在这个RISC-V hart中任何早期内存操作之前释放内存操作都不能观察到。如果aq和rl位都设置了则在所有早期的内存操作之前或后期的内存操作之后在相同的RISCV hart上原子内存操作是顺序一致的且不能被观察到而只能被全局次序中的其它hart观察到。
2. load-reserved/store-conditional指令
| funct5+aq+rl+rs2+rs1+funct3+rd+opcode | funct5 | rs2 | opcode | 意义 |
| ------------------------------------- | ------ | ---- | ------ | ------------------------------------------------------------ |
| lr rd, (rs1) | LR | 0 | AMO | 原子交换的前半部分load且将内存地址注册为reservation |
| sc rd, rs2, (rs1) | SC | src | AMO | 原子交换的后半部分:(rs1)=rs2如果内存地址的reservation有效,成功则rd为0,失败则rd非0. |
在完成上一个LR之前,一个SC指令不能被另一个RISC-V hart观察到。由于LR/SC序列的原子性在LR和一个成功的SC之间任何hart的内存操作都不能被观测到。LR/SC序列可以通过在SC指令上设置aq位来赋予**获取语义**。LR/SC序列可以通过在LR指令上设置rl位来赋予**释放语义**。在LR指令上设置aq和rl位并在SC指令上设置aq位使得LR/SC序列与其他顺序一致的原子操作顺序一致。
如果LR/SC的位均没有设置则LR/SC序列是可以观测到的。这适用于并行下降操作。
3. 原子内存操作atomic memory operation(AMO)
| funct5+aq+rl+rs2+rs1+funct3+rd+opcode | funct5 | opcode | 意义 |
| ------------------------------------- | ------------- | ------ | -------------------------- |
| amoswap.w/d rd, rs2, (rs1) | AMOSWAP.W/D | AMO | rd=(rs1); |
| amoadd.w/d rd, rs2, (rs1) | AMOADD.W/D | AMO | rd=(rs1);(rs1)=rd+rs2 |
| amoand.w/d rd, rs2, (rs1) | AMOAND.W/D | AMO | rd=(rs1);(rs1)=rd&rs2 |
| amoor.w/d rd, rs2, (rs1) | AMOOR.W/D | AMO | rd=(rs1);(rs1)=rd\|rs2 |
| amoxor.w/d rd, rs2, (rs1) | AMOXOR.W/D | AMO | rd=(rs1);(rs1)=rd^rs2 |
| amomax[u].w/d rd, rs2, (rs1) | AMOMAX[U].W/D | AMO | rd=(rs1);(rs1)=max(rd,rs2) |
| amomin[u].w/d rd, rs2, (rs1) | AMOMIN[U].W/D | AMO | rd=(rs1);(rs1)=min(rd,rs2) |
原子内存操作(AMO)指令为多处理器同步执行"读-修改-写"操作并使用r类型指令格式进行编码。这些AMO指令从rs1代表的内存地址原子地加载数据值将该值放入rd对rd的值和rs2的值进行操作然后将结果存储回rs1的内存地址。AMOs可以在内存中操作64位(仅限RV64)或32位的数据。对于RV64AMOs总是对rd中的值进行符号扩展。rs1中的地址必须与操作数的大小自然对齐(即8字节对齐64位字4字节对齐32位字),否则就会造成未对齐地址异常。
支持的操作有swap, add, and, or, xor, 符号和非符号整数的maximum和minimum。除了排序约束这些AMOs也可以实现并行下降操作一般会通过写入x0的方式来丢弃返回值。
为了帮助实现多处理器同步AMOs可选地提供发布一致性语义。如果设置了aq位那么在这个riscv - v hart中就不能观察到在AMO之前发生的内存操作。相反如果设置了rl位那么其他的RISC-V harts将不会在这个riscv - v hart的内存访问之前观察AMO。
#### 8. F浮点扩展
1. F寄存器
F扩展增加了32个浮点寄存器和1个浮点控制和状态寄存器fcsr。浮点寄存器是f0-f31每个32位宽。浮点控制和状态寄存器则包含了浮点单元的操作模式和异常状态。
2. 浮点控制和状态寄存器
3. NaN的生成与传播
4. 低能的算术
5. 单精度Load和Store指令
6. 单精度浮点计算指令
7. 单精度浮点转换和移动指令
8. 单精度浮点比较指令
9. 单精度浮点类型指令
#### 9. D双精度扩展
1. D寄存器
D将32个浮点寄存器扩展到64位F寄存器现在可以保存32位或64位浮点值了。
2. NaN Boxing of Narrower Values
3. 双精度Load和Store指令
4. 双精度浮点计算指令
5. 双精度浮点转换和移动指令
6. 双精度浮点比较指令
7. 双精度浮点类型指令
#### 10. Q四精度扩展
#### 11. L十进制浮点扩展
#### 12. C压缩指令扩展
#### 13. B位操作扩展
#### 14. J动态翻译语言扩展
#### 15. T事物内存扩展
#### 16. P封装的单指令多数据流扩展
#### 17. V向量操作扩展
#### 18. N用户级中断扩展
#### 19. GRV32/64G 指令集列表
#### 20. RISC-V汇编程序员手册
- 寄存器在标准调用规约中的角色
| 寄存器 | ABI名字 | 描述 | 由谁保存 |
| ------ | ----- | ------------- | ---- |
| x0 | zero | 固定为0 | 无 |
| x1 | ra | 返回地址 | 调用者 |
| x2 | sp | 栈指针 | 被调用者 |
| x3 | gp | 全局指针 | 无 |
| x4 | tp | 线程指针 | 无 |
| x5 | t0 | 临时寄存器/备用连接寄存器 | 调用者 |
| x6-7 | t1-2 | 临时寄存器 | 调用者 |
| x8 | s0/fp | 保存寄存器/桢指针 | 被调用者 |
| x9 | s1 | 保存寄存器 | 被调用者 |
| x10-11 | a0-1 | 函数参数/返回值 | 调用者 |
| x12-17 | a2-7 | 函数参数 | 调用者 |
| x18-27 | s2-11 | 保存寄存器 | 被调用者 |
| x28-31 | t3-6 | 临时寄存器 | 调用者 |
- 普通伪指令
| 伪指令 | 对应的基本指令 | 意义 |
| ---------------------------- | ---------------------------------------- | -------------------------- |
| la rd, symbol | auipc rd, symbol[31:12] ; addi rd, rd, symbol[11:0] | rd = pc + symbol载入地址 |
| l{b\|h\|w\|d} rd, symbol | auipc rd, symbol[31:12]; l{b\|h\|w\|d} rd, symbol[11:0]\(rd) | 载入全局地址 |
| s{b\|h\|w\|d} rd, symbol, rt | auipc rt, symbol[31:12]; s{b\|h\|w\|d} rd, symbol[11:0]\(rt) | 存储全局地址 |
| nop | addi x0, x0, 0 | 空操作 |
| li rd, immediate | 无穷序列 | rd = immediate存储立即数 |
| mv rd, rs | addi rd, rs, 0 | rd = rs + 0,复制寄存器 |
| not rd, rs | xori rd, rs, -1 | rd = rs ^ -1, rs的补码 |
| neg rd, rs | sub rd, x0, rs | rd = 0 - rs, 补码 |
| negw rd, rs | subw rd, x0, rs | rd = 0 - rs, 补码 |
| sext.w rd, rs | addiw rd, rs, 0 | rd = rs + 0, 符号扩展 |
| seqz rd, rs | sltiu rd, rs, 1 | rd = (rs == 0) ? 1 : 0; |
| snez rd, rs | sltu rd, x0, rs | rd = (rs != 0) ? 1 : 0; |
| sltz rd, rs | slt rd, rs, x0 | rd = (rs < 0) ? 1 : 0; |
| sgtz rd, rs | slt rd, x0, rs | rd = (rs > 0) ? 1 : 0; |
| beqz rs, offset | beq rs, x0, offset | if (rs == 0), branch |
| bnez rs, offset | bne rs, 0x, offset | if( rs != 0), branch |
| blez rs, offset | bge x0, rs, offset | if(rs <= 0), branch |
| bgez rs, offset | bge rs, x0, offset | if(rs >= 0), branch |
| bltz rs, offset | blt rs, x0, offset | if(rs < 0), branch |
| bgtz rs, offset | blt x0, rs, offset | if(rs > 0), branch |
| bgt rs, rt, offset | blt rt, rs, offset | if(rs > rt), branch |
| ble rs, rt, offset | bge rt,rs, offset | if(rs <= rt), branch |
| bgtu rs, rt, offset | bltu rt, rs, offset | if( rs > rt), branch |
| bleu rs, rt, offset | bgeu rt, rs, offset | if(rs <= rt), branch |
| j offset | jal x0, offset | pc += offset |
| jal offset | jal x1, offset | pc += offset; x1 = pc + 4; |
| jr rs | jalr x0, rs, 0 | pc = rs + 0 |
| jalr rs | jalr x1, rs, 0 | pc = rs + 0; x1 = pc + 4; |
| ret | jalr x0, x1, 0 | pc = x1 + 0 |
| call offset | auipc x6, offset[31:12]; jalr x1, x6, offset[11:0] | pc += offset; x1 = pc + 4; |
| tail offset | auipc x6, offset[31:12]; jalr x0, x6, offset[11:0] | pc += offset |
| fence | fence iorw, iorw | 隔离内存和IO |
- 访问CSR的伪指令
| 伪指令 | 对应的基本指令 | 意义 |
| --------------- | ------------------------ | ------------------------- |
| rdinstret[h] rd | csrrs rd, instret[h], x0 | rd = instret[h],读取指令退休计数器 |
| rdcycle[h] rd | csrrs rd, cycle[h], x0 | rd = cycle[h],读取循环计数器 |
| rdtime[h] rd | csrrs rd, time[h], x0 | rd = time[h], 读取实时时钟 |
| csrr rd, csr | csrrs rd, csr, x0 | rd = csr, 读取csr |
| csrw csr, rs | csrrw x0, csr, rs | csr = rs, 写入csr |
| csrs csr, rs | csrrs x0, csr, rs | csr \|= rs, csr置位 |
| csrc csr, rs | csrrc x0, csr, rs | csr &= !rs, csr清位 |
| csrwi csr, imm | csrrwi x0, csr, imm | csr = imm, 立即数写入csr |
| csrsi csr, imm | csrrsi x0, csr, imm | csr \|= imm, 立即数置位csr |
| csrci csr, imm | csrrci x0, csr, imm | csr &= !imm, 立即数清位csr |
#### 21. 扩展RISC-V
#### 22. ISA子集命名约定
#### 23. 历史与致谢
### RISC-V手册下卷(riscv-privileged-v1.10)
#### 1. 介绍
1. 硬件平台术语
hart硬件线程hardware thread。
核心(core):包含独立取指单元的组件。
协处理器(coprocessor)是与RISC-V核心相连的一个单元且主要被RISC-V指令流序列化但包含了一些额外的体系结构状态和指令集扩展相对于主RISC-V指令流来说可能会有些有限的自治。
加速器(accelerator):一个非可编程的固定功能单元,或可以自治工作、但专门用于某项任务的核心。
2. 特权软件栈术语
AEE应用程序执行环境
ABI应用程序二进制接口
SBI管理员二进制接口
SEE管理员执行环境
HBI管理程序二进制接口
HEE管理程序执行环境
HAL硬件抽象层
3. 特权级
| 级别 | 编码 | 名字 | 缩写 | 特点 |
| ---- | ---- | -------- | ---- | ----------- |
| 0 | 00 | 用户/应用 | U | 应用程序 |
| 1 | 01 | 管理员 | S | 操作系统 |
| 2 | 10 | Reserved | | |
| 3 | 11 | 机器 | M | 必需的,可信的,最高级 |
4. 调试模式:用以支持片外调试或制造测试,可以认为是一种比机器级更高的特权级。
#### 2. 控制和状态寄存器(CSR)
1. CSR地址映射约定
2. CSR列表
**用户级**:Trap Setup, Trap Handling, Floating-Point, Counter/Timers
**管理员级**:Trap Setup, Trap Handling, Protection & Translation
**机器级**:Information, Trap Setup, Trap Handling, Protection & Translation, Counter/Timers, Counter Setup
**调试级**:Debug/Trace(与机器级共享), Debug Mode
3. CSR域的说明
**WIRI**:写忽略读忽略,用于保留的只读域。
**WPRI**:写保留读忽略,用于保留的读写域。
**WLRL**:写合法读合法,用于部分位有用的保留域。
**WARL**:写任意读合法,用于部分位有用的保留域。
#### 3. 机器级ISA
##### 3.1机器级CSR
- **misa**是一个WARL寄存器它会报告hart支持的ISA。
| XLEN-1 ~ XLEN-2 | XLEN-3 ~ 26 | 25 ~ 0 |
| --------------- | -------------- | ----------------------- |
| MXL[1:0] (WARL) | WIRI | Extensions[25:0] (WARL) |
| 2 bits | XLEN-28 (bits) | 26 bits |
**MXL**字段编码本机I指令的宽度1代表32位2代表64位3代表128位。**Extensions**字段用单个字母占据1位的方式表明是否存在相应的标准扩展比如A在0位B在1位Z在25位。**I**表明RV32I或RV64I或RV128I**E**表明RV32E如果**I**和**E**同时存在应选择**I**。由于**G**和**IMAFD**重复为避免冗余G表示除IMAFD外的额外扩展。**U**表示支持用户模式,**S**表示支持Supervisor模式。**X**表示存在非标准扩展。
- mvendorid机器供应商ID寄存器。
- martid机器架构ID寄存器。提供了hard的基本微架构的编码。
- mimpid机器实现ID寄存器。提供了处理器实现版本的唯一编码。
- mhartid包含了运行代码的硬件线程的id值这个寄存器是只读的。在多处理器系统中HardID的编号不一定的连续的但一定有一个是0。
- **mstatus**:追踪和控制硬件线程的当前操作状态。
| 名称 | RV32 | RV64,RV128 | 意义 |
| -------- | ------- | ----------- | ---------------------- |
| UIE | 0 | 0 | 1开启用户中断 |
| SIE | 1 | 1 | 1开启管理员中断 |
| WPRI | 2 | 2 | 保留 |
| MIE | 3 | 3 | 1开启机器中断 |
| UPIE | 4 | 4 | 进入自陷前UIE的值 |
| SPIE | 5 | 5 | 进入自陷前SIE的值 |
| WPRI | 6 | 6 | 保留 |
| MPIE | 7 | 7 | 进入自陷前MIE的值 |
| SPP | 8 | 8 | 进入S-mode之前的特权级 |
| WPRI | 9 ~ 10 | 9 ~ 10 | 保留 |
| MPP[1:0] | 11 ~ 12 | 11 ~ 12 | 进入M-mode之前的特权级 |
| FS[1:0] | 13 ~ 14 | 13 ~ 14 | |
| XS[1:0] | 15 ~ 16 | 15 ~ 16 | |
| MPRV | 17 | 17 | |
| SUM | 18 | 18 | |
| MXR | 19 | 19 | |
| TVM | 20 | 20 | |
| TW | 21 | 21 | |
| TSR | 22 | 22 | |
| WPRI | 23 ~ 30 | 23 ~ 31 | |
| UXL[1:0] | | 32 ~ 33 | 同misa寄存器的MXL域 |
| SXL[1:0] | | 34 ~ 35 | 同misa寄存器的MXL域 |
| WPRI | | 36 ~ XLEN-2 | |
| SD | 31 | XLEN -1 | |
**TSR,TW,TVW用来支持虚拟化** 。**TSR** (Trap SRET)支持拦截Supervisor异常返回指令SRET。**TW** (Timeout Wait) 支持拦截WFI指令。**TVM**(Trap Virtual Memory) 支持拦截supervisor虚拟内存管理操作。当TVM=1时在S模式下尝试读取或写入satp CSR或执行SFENCE.VMA指令会引发非法指令异常。当TVM=0时这些操作在s模式下是允许的。**MPRV,MXR,SUM用来支持内存权限** 。**MXR** (Make eXecutable Readable)修改了加载访问虚拟内存的特权当其为0时只有标记为可读的页面才可以加载当其为1时从标记为可读或可执行的页面均可加载 。**SUM**(permit Supervisor User Memory access)修改S模式加载、存储和取指访问虚拟内存的特权。当其为0时S模式不可以访问U模式的页面当其为1时这样的访问是允许的。**MPRV** (Modify PRiVilege)修改在所有特权模式下加载和存储执行的权限级别当其置位时加载和存储内存地址就好像当前特权模式为MPP一样指令地址的转换和保护不受影响。如果不支持用户模式则MPRV会被硬连接为1。**FS、XS和SD用来扩展上下文状态** 。**FS**字段编码浮点单元的状态包括CSR fcsr和浮点数据寄存器f0-f31**XS**字段编码额外的用户模式扩展和关联状态的状态。**SD**位是一个只读的位它总结了FS字段或XS字段是否表示存在某个脏状态那种状态需要将扩展的用户上下文保存到内存中。如果XS和FS都被固定为零那么SD也总是为零。
- mtvec机器自陷向量基址寄存器记录自陷向量的配置由向量基址和向量模式组成。
| 位 | 名称 | 作用 |
| ---------- | -------------- | ------------------------------------------------------------ |
| 2 ~ XLEN-1 | BASE[XLEN-1:2] | 中断例程的高XLEN-2位要4字节对齐 |
| 0 ~ 1 | MODE | 0中断入口仅由BASE决定1异步中断的地址为BASE+4*cause2和3为保留位 |
- medeleg和mideleg机器自陷授权寄存器(机器异常授权寄存器和机器中断授权寄存器)
默认情况下所有特权级的内陷都是在机器级处理的。为提高性能使用medeleg和mideleg寄存器来表明某些异常和中断可以由较低的权限级别直接处理。在三种特权级都具备的系统中在medeleg或mideleg中设置相应的位会将内陷(S模式或U模式)授权给S模式内陷处理程序。
- mip,miemip的意思是有未处理的中断mie的意思是中断使能。
sip/sie, uip/uie寄存器是mip/mie寄存器的受限视图。
| 位 | mip | mie | 意义 |
| ------------ | ---- | ---- | ------------------------------------------------------------ |
| 12 XLEN-1 | WIRI | WPRI | 保留区 |
| 11 | MEIP | MEIE | 外中断挂起或使能(机器级) |
| 10 | WIRI | WPRI | 保留区 |
| 9 | SEIP | SEIE | 外中断挂起或使能(管理员级) |
| 8 | UEIP | UEIE | 外中断挂起或使能(用户级) |
| 7 | MTIP | MTIE | 计时器中断挂起或使能(机器级只读只能通过写mtimecmp寄存器来清零) |
| 6 | WIRI | WPRI | 保留区 |
| 5 | STIP | STIE | 计时器中断挂起或使能(管理员级) |
| 4 | UTIP | UTIE | 计时器中断挂起或使能(用户级) |
| 3 | MSIP | MSIE | 软件中断挂起或使能(机器级) |
| 2 | WIRI | WPRI | 保留区 |
| 1 | SSIP | SSIE | 软件中断挂起或使能(管理员级) |
| 0 | USIP | USIE | 软件中断挂起或使能(用户级) |
指令手册没有明确解释,但我**推测**外中断是外部设备引发的中断,软件中断是处理器和内存引发的中断,而计时器中断应属于外中断,它被单独分出来应该是基于计时器在进程调度中的重要作用。
如果一个中断是全局使能的且mip和mie相应位被设置则那个中断将会被获取。默认情况下如果hart的当前特权级小于M或者当前特权级是M且mstatus寄存器的MIE位被设置则M模式下的中断是全局使能的。当mideleg相应位被设置如果hart的当前特权级等于授权的特权级(U或S)且当前特权级的中断使能位被设置(mstatus寄存器的SIE或UIE位),或者当前特权级低于授权特权级,则中断被认为是全局使能的。
- mtime, mtimecmp
mtime是一个实时计数器表现为内存映射的寄存器。不论是RV32, RV64还是RV128mtime都是64位的。
mtimecmp是一个64位内存映射的计时器比较寄存器。当mtime中的值大于或等于mtimecmp时将触发计时器中断。
- 硬件性能管理器
- **[m|h|s]counteren**:寄数器使能。将硬件性能监控计数器的可用性控制到下一个最低特权模式。
| 31 | ... | 3 | 2 | 1 | 0 |
| ----- | ---- | ----- | ----- | ----- | ----- |
| HPM31 | ... | HPM3 | IR | TM | CY |
| 1 bit | ... | 1 bit | 1 bit | 1 bit | 1 bit |
**CY**代表cycle寄存器**TM**代表time寄存器**IR**代表instret寄存器(即指令消耗寄存器)**HPM**代表hpmcounter寄存器(即性能监控计数器)。当相应位清零后,在更低特权级下访问相应的寄存器会非法指令异常。
- mscratch记录机器模式硬件线程上下文的地址当进入机器模式下的中断例程时可以与用户寄存器切换
- mepc机器异常程序计数器
- mcause机器原因计数器
| 位 | 名称 | 作用 |
| ----------- | -------------- | -------------------------------- |
| XLEN-1 | Interrupt | 由中断引发则置1由异常引发则置0 |
| 0 XLEN-2 | Exception Code | 引发中断的编码,详见下表 |
| 编码 | Interrupt位为1 | Interrupt位为0 |
| ----- | ---------------- | ------------------- |
| 0 | 用户软件中断 | 指令地址未对齐 |
| 1 | 管理员软件中断 | 指令访问错误 |
| 2 | 保留 | 非法指令 |
| 3 | 机器软件中断 | 断点 |
| 4 | 用户计时器中断 | Load地址未对齐 |
| 5 | 管理员计时器中断 | Load访问错误 |
| 6 | 保留 | Store/AMO地址未对齐 |
| 7 | 机器计时器中断 | Store/AMO访问错误 |
| 8 | 用户外中断 | U-mode下的环境调用 |
| 9 | 管理员外中断 | S-mode下的环境调用 |
| 10 | 保留 | 保留 |
| 11 | 机器外中断 | M-mode下的环境调用 |
| 12 | \>=12 保留 | 指令页错误 |
| 13 | | Load页错误 |
| 14 | | 保留 |
| 15 | | Store/AMO页错误 |
| \>=16 | | 保留 |
- mtval机器陷阱值寄存器当自陷发生在M-mode时mtval通过写入异常信息来辅助软件处理这个自陷。它用来取代之前版本的mbadaddr寄存器。
##### 3.2 机器模式特权指令
| funct12 | rs1 | funct3 | rd | opcode | 意义 |
| ------- | ---- | ------ | ---- | ------ | -------------------------------------- |
| ECALL | 0 | PRIV | 0 | SYSTEM | 向更高特权级发起请求 |
| EBREAK | 0 | PRIV | 0 | SYSTEM | 将控制转回调试环境 |
| xRET | 0 | PRIV | 0 | SYSTEM | 在处理完自陷后返回将xepc的值放到pc中 |
| WFI | 0 | PRIV | 0 | SYSTEM | 等待中断 |
##### 3.3 Reset
一经重置hart的特权模式就置为M。
##### 3.4 不可屏蔽中断Non-Maskable Interrupts(NMI)
不可屏蔽中断仅用于硬件错误的情况。
##### 3.5 物理内存特性 (**PMA**)
物理内存映射包含多种地址范围一些对应到内存区域一些对应到内存映射控制寄存器一些则对应到地直空间的空洞。一些内存区域可能不支持读、写或执行一些可能不支持子字或子块访问一些可能不支持原子操作一些可能不支持缓存一致性或可能有不同的内存模型。相似地内存映射控制寄存器在访问宽度、对原子操作的支持和读写访问是否有副作用上也各不相同。在RISCV系统里物理地址空间的这些属性和功能被称为物理内特性(PMA)。
PMAs是基础硬件的固有属性在系统运行时基本不会改变。一些内存区域的PMAs在芯片设计时就已经确定例如片上ROM。另一些是在电路板设计的时候确定的。芯片外总线也可能支持冷插拔或热插拔的设备。一些设备可能支持在运行的时候配置这意味着不同的PMAs。
##### 3.6 物理内存保护 (**PMP**)
为了支持安全运行和抑制错误,最好限制运行在某个核上的程序对物理地址的访问。物理内存保护(PMP)单元提供了单个hart上机器模式下的控制寄存器以设置在指定物理内存区域上的访问权限(read, write, execute)。PMP检查和PMA检查是并行的。
PMP访问控制的粒度是因平台而异的即使在一个平台内也可能因物理内存区域而不同但标准PMP编码支持小如4字节的区域。一些区域的特权级可以写死到硬件上比如一些区域可以只在机器模式下可见。
当hart运行在S或U模式mstatus寄存器的MPRV已置位且MPP域包含S或U对于loads或stores来说PMP检查会应用于所有访问。PMP检查也适用于虚拟地址转换的页表访问有效的特权模式为S。另外PMP检查还可应用于M模式的访问这种情况下PMP寄存器是锁定的以便M模式下的软件也不可以修改它们。实际上PMP可以将权限授予S或U模式还可以从M模式撤销权限。
PMP违规总是精确地限制在处理器上。
- PMP CSRs
PMP入口由一个8位配置寄存器和一个XLEN位地址寄存器描述。一共支持16个PMP入口。任意PHP入口被实现之后所有PMP CSRs都必须被实现。PMP CSRs只能在M模式下访问。
对于RV32,16个PMP入口的设置(pmp0cfg-pmp15cfg)保存在4个寄存器中pmpcfg0-pmpcfg3。对于RV64,16个入口保存在2个寄存器中pmpcfg0,pmpcfg2。
PMP地址寄存器是pmpaddr0-pmpaddr15。对于RV32PMP地址寄存器编码34位物理地址的33-2位。对于RV64,PMP地址寄存器编码56位物理地址的55-2位。
配置寄存器的RWX位设置后分别代表PMP入口允许读写和指令执行。如下所示
| L(WARL) | (WIRL) | A(WARL) | X(WARL) | W(WARL) | R(WARL) |
| ------- | ------ | ------- | ------- | ------- | ------- |
| 1 bit | 2 bits | 2 bits | 1 bit | 1 bit | 1 bit |
- 地址匹配
PMP配置寄存器的A字段编码了地址匹配的模式。如下表所示
| A | 名称 | 描述 |
| ---- | ----- | ------------------------------------------------ |
| 0 | OFF | 空区域(无效,不匹配任何地址) |
| 1 | TOR | Top Of Range |
| 2 | NA4 | Naturally Aligned 4-byte region |
| 3 | NAPOT | Naturally Aligned power-of-two region(>=8 bytes) |
NAPOT的对齐范围由对应地址寄存器的低位来决定。如下表所示
| pmpaddr | pmpcfg.A | 匹配大小 |
| ----------- | -------- | --------------------- |
| aaaa...aaaa | NA4 | 4 byte NAPOT |
| aaaa...aaa0 | NAPOT | 8 byte NAPOT |
| aaaa...aa01 | NAPOT | 16 byte NAPOT |
| ... | ... | ... |
| a011...1111 | NAPOT | 2^(XLEN+1) byte NAPOT |
| 0111...1111 | NAPOT | 2^(XLEN+2) byte NAPOT |
如果选择TOR相关联的地址寄存器即为地址范围的顶部前面的地址寄存器即为地址范围的底部。如果PMP入口i的A字段设置为TOR则入口匹配任意地址a(pmpaddr ~i-1~ <= a < pmpaddr~i~)当i为0时a的范围为(a < pmpaddr~0~)
- 锁定和特权模式
L位表示PMP入口被锁定。即写配置寄存器和相关联的地址寄存器的操作将无效。只能通过重置系统的方法来解除锁定。另外如果pmp*i*cfg.A被置为TOR写pmpaddr*i-1*的操作将无效。
除了锁定PMP入口L位还表示R/W/X权限是否是强制的。当L位为1时这些权限将强制用于所有特权模式。当L位为0时匹配PMP入口的所有M模式访问都被允许R/W/X权限仅适用于S和U模式。
- 优先权与匹配逻辑
PMP入口的优先级是静态的。最低编号的PMP入口匹配任意字节的访问它决定了访问是否成功。匹配的PMP入口必须匹配任意字节的访问否则访问失败这与L, R, W和X位无关。例如如果PMP入口被设置为匹配4字节范围0xC0xF则对范围0x8-0xF的8字节访问将失败假如那个PMP入口是匹配这些地址的最高优先级入口。
如果PMP入口匹配访问的所有字节L、R、W和X位决定访问是否成功。
如果PMP入口匹配一个M模式访问则此访问成功。
错误的访问将生成一个loadstore或指令访问异常。
在某些实现里未对齐的load, store和取指可能被分解为多个访问在访问异常发生前其中的某些访问可能成功。
#### 4. 管理员级ISA
1. 管理员CSR
- sstatus: 管理员状态寄存器追踪处理器当前的操作状态是XLEN位R/W寄存器。是mstatus寄存器的子集。
| 位 | 名称 | 意义 |
| ----------- | ---- | -------------------------------------- |
| XLEN-1 | SD | |
| 20 ~ XLEN-2 | WPRI | 保留 |
| 19 | MXR | |
| 18 | SUM | |
| 17 | WPRI | 保留 |
| 15 ~ 16 | XS | |
| 13 ~ 14 | FS | |
| 9 ~ 12 | WPRI | 保留 |
| 8 | SPP | 进入S-mode前hart的特权级。0:U, 1:S |
| 6 ~ 7 | WPRI | 保留 |
| 5 | SPIE | 进入S-mode前SIE的值。中断返回时写回SIE |
| 4 | UPIE | 进入U-mode前UIE的值。中断返回时写回UIE |
| 2 ~ 3 | WPRI | 保留 |
| 1 | SIE | S-mode下的中断。1使能0停止 |
| 0 | UIE | U-mode下的中断。1使能0停止 |
- UIE位开启或停止用户模式的中断。
- UPIE位的功能和SPIE位类似。
- stvec存放中断服务例程(ISR)的入口地址(BASE)和模式(MODE)
| XLEN-1 ~ 2 | 1 ~ 0 |
| --------------------- | ----------- |
| BASE[XLEN-1:2]\(WLRL) | MODE\(WARL) |
注意BASE不包含最低两位因为要求此地址4字节对齐即最低两位一定是0。
- sip, sie管理员中断寄存器sip挂起中断sie使能中断。它们是mip和mie的子集。
| 位 | sip | sie |
| ----------- | -------------------------------- | -------------------------- |
| 10 ~ XLeN-1 | WIRI(保留) | WPRI(保留) |
| 9 | SEIP | SEIE |
| 8 | UEIP用户特权级的外部中断被挂起 | UEIE使能用户级外部中断 |
| 6 ~ 7 | WIRI(保留) | WPRI(保留) |
| 5 | STIP | STIE |
| 4 | UTIP | UTIE |
| 2 ~ 3 | WIRI(保留) | WPRI(保留) |
| 1 | SSIP触发管理员级软件中断 | SSIE使能管理员级软件中断 |
| 0 | USIP | USIE |
这些位的名称都是4个字母的。第1个字母表示特权级S管理员级;U用户级。第2个字母表示中断类型E(External)外部中断T(Timer)定时器中断S(Software)软件中断。第4个字母表示挂起(P)还是使能(E)。
- time, cycle, instret :与用户级程序共用这些寄存器。
- scounteren详见机器级ISA对应的寄存器。控制硬件性能监控计数器在U-mode下的可用性。1则可用0则不可用。可控制的寄存器有CY(cycle)TM(time), IR(instret), HPMn(hpmcountern).
- sscratch记录管理员模式中断上下文的指针
- sepc记录引起异常的指令的地址
- scause各种中断的来源interrupt位为0表示异常为1表示中断
- stval陷阱值寄存器。引发错误的内存地址会保存在这个寄存器中。
- satp地址转换与内存保护寄存器。
| 字段 | RV32 | RV64 | 意义 |
| ---- | --------------------- | ----------------------------- | -------- |
| MODE | 1 bit (0:bare,1:Sv32) | 4 bits (0:bare,8:Sv39,9:Sv48) | 地址转换方案 |
| ASID | 9 bits | 16 bits | 地址空间标识符 |
| PPN | 22 bits | 44 bits | 根页表的物理页号 |
注意修改satp寄存器并不意味着给页表更新和地址转换之间加上了次序约束。所以在修改satp寄存器之前应该先执行sfence.vma指令。
2. 管理员指令
除了SRET指令还增加了一条新的管理员级的指令。
| funct7 | rs2 | rs1 | funct3 | rd | opcode | 意义 |
| ---------- | ---- | ----- | ------ | ---- | ------ | ---------- |
| SFENCE.VMA | asid | vaddr | PRIV | 0 | SYSTEM | 更新内存管理数据结构 |
3. Sv32:基于页的32位虚拟内存系统
当Sv32被写入satp寄存器的MODE域时就进入了32位的分页虚拟内存模式。Sv32基于RV32其被设计为足以支持类Unix操作系统。
- 寻址与内存保护
Sv32将虚拟地址映射到物理地址的过程与X86是相同的页表的基址放在satp寄存器内。所不同的是Sv32的虚拟地址是32位的而物理地址是34位的。虚拟地址可以寻址4G的空间而物理地址可寻址16G的空间。我猜测这样设计是为了支持虚拟化即虚拟机上的不同系统可以使用不同的物理内存互不干扰。
| PPN[1] | PPN[0] | RSW | D | A | G | U | X | W | R | V |
| ------- | ------- | ------ | ----- | ----- | ----- | ----- | ----- | ----- | ----- | ----- |
| 12 bits | 10 bits | 2 bits | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit | 1 bit |
| 位 | 意义 |
| ------- | ------------------------------------ |
| V | 1代表PTE有效0代表PTE无效 |
| X, W, R | 可执行可写可读。全为0代表是指向下级页表的指针否则就是叶子PTE |
| U | 1代表U-mode可访问0代表U-mode不可访问仅用于叶子PTE |
| G | 全局映射 |
| A | 虚拟页曾被访问过仅用于叶子PTE |
| D | 虚拟页曾被写入过仅用于叶子PTE |
| RSW | reserved |
- 虚拟地址转换过程
Sv32Sv39Sv48的转换算法都是一样的不同的只有LEVELS和PTESIZE的值。LEVELS指的是vpn有几级PTESIZE指的是PTE占几个字节。
4. Sv39:基于页的39位虚拟内存系统
Sv39支持39位的虚拟地址空间其设计遵循Sv32的总体方案本节仅介绍它们之间的不同点。Sv39基于RV64。
- 寻址与内存保护
Sv39的虚拟地址是64位的但只有低39位有效且高25位必须和第38位相等这里说的第38位指的是从0开始计数。从虚拟地址到物理地址共需3级页表的转换。
每一级的PTE都有可能是叶子PTE所以除了4K的页外还有可能存在2M或1G的页每种页都必须依照它自己的大小在虚拟层面和物理层面对齐。
5. Sv48:基于页的48位虚拟内存系统
Sv48支持48位的虚拟地址空间用以弥补Sv39的不足。Sv48也基于RV64。支持Sv48的实现也应当支持Sv39。
- 寻址与内存保护
Sv48的虚拟地址占用了64位但只有低48位是有效的且高16位都必须和第47位相等第47位的意思是从0开始计数如果从1开始计数则是第48位。从虚拟地址到物理地址共需4级页表的转换。
每一级的PTE都有可能是叶子PTE所以除了4K的页外还可能有2M1G512G的页每种页都必须依照它自己的大小在虚拟层面和物理层面对齐。
#### 5. Hypervisor扩展
#### 6. RISC-V特权指令列表
都是I-type类型的指令它们是**环境调用和断点指令**`ecall`和`ebreak`**陷阱返回指令**`uret`,`sret`,`mret`**中断管理指令**`wfi`**内存管理指令**`sfence.vma`。
#### 7. 平台级中断控制器(PLIC)
#### 8. 机器配置描述
#### 9. 历史

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,59 @@
- 指令类型
- R型register型从寄存器中取出源操作数运算结果再写入寄存器中
ADDU,ADD,CMP,MFIH,MFPC,MOVE
- I型immediate型 ,使用立即数作为源操作数
ADDIU,ADDIU3,ADDSP3,ADDSP,CMPI,INT,LI,LW,LW_SP
- B型branch型转移型
B,BEQZ,BNEZ,BTEQZ,BTNEZ
- J型jump型长跳转型
JALR,JR,JRRA
- 功能分类
- 算术/逻辑运算
1. `ADDIU rx immediate` (add immediate unsigned)
2. `ADDIU3 rx ry immediate`
3. `ADDSP3 rx immediate`
4. `ADDSP immediate`
5. `ADDU rx ry rz`
40. `SUBU rx ry rz`
26. `NEG rx ry` (negative)
12. `CMP rx ry`
13. `CMPI rx immediate`
6. `AND rx ry`
29. `OR rx ry`
27. `NOT rx ry`
44. `XOR rx ry`
30. `SLL rx ry immediate`
31. `SLLV rx ry`
32. `SLT rx ry`
33. `SLTI rx immediate`
34. `SLTU rx ry`
35. `SLTUI rx immediate`
36. `SRA rx ry immediate`
37. `SRAV rx ry`
38. `SRL rx ry immediate`
39. `SRLV rx ry`
- 分支操作
7. `B immediate`
8. `BEQZ rx immediate`
9. `BNEZ rx immediate`
10. `BTEQZ immediate`
11. `BTNEZ immediate`
14. `INT immediate`
15. `JALR rx` (jump and link)
16. `JR rx` (jump register)
17. `JRRA` (jump register RA)
- 加载和存储
18. `LI rx immediate` (load immediate)
19. `LW rx ry immediate`(load indirect addressing)
20. `LW_SP rx immediate`
41. `SW rx ry immediate`
42. `SW_RS immediate`
43. `SW_SP rx immediate`
- 数据传送
21. `MFIH rx` (move from IH)
22. `MFPC rx` (move from PC)
23. `MOVE rx ry`
24. `MTIH rx`
25. `MTSP rx`
- 空操作
28. `NOP` (空操作)

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 725 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

View File

@ -0,0 +1,7 @@
记录编译错误及解决方法
#### Error: Instruction csrw requires absolute expression
- 错误原因这是由于编译器版本不正确引起的由于riscv指令集尙处于发展阶段所以不同版本的指令集可能是不一样的
- 解决方法:重新编译工链,要使用指令集对应的编译器版本号

359
Languages/Assembly/i386.md Normal file
View File

@ -0,0 +1,359 @@
## 资源
本文是对Intel手册的总结但要注意在gcc下的汇编是使用的是gas syntax。
## 一、应用程序编程
### 1. 基本编程模型
#### 1.1 寄存器
- 通用寄存器(8个)
EAXEBXECXEDXEBPESPEBIEDI
- 段寄存器(6个)
CS: DS: ES:SS: FS: GS:
- 标志寄存器EFLAGS低16位叫FLAGSFLAGS是为8086和80286模式准备的。标志可以分为三种状态标志控制标志和系统标志
![FLAGS](_img/Flags.png)
- 寄存器与内存的对应关系
![实模式](/home/szx/Desktop/shzhxh.github.io/Languages/Assembly/_img/real_mode.jpeg)
###
### 2. 应用程序指令集
#### 2.0 AT&T汇编语法
* (%eax) //代表eax指向的内存地址
1(%eax) //指向eax指向的内存地址的下一个字节的地址
* 赋值方向是从左向右(Intel语法的赋值方向是从右向左)
#### 2.1 数据传送指令
- MOVmovq(moves a quadword from source to destination, in x86 a word is 16 bits), movl(moves a long from source to destination, long is 32 bits)
- XCHG(Exchange)交换两个操作数的内容。这两个操作数可以都是寄存器也可以一个寄存器一个内存。当有一个操作数是内存时LOCK信号会自动激活。
- stos将EAX的值保存到ES:DSI指向的字符串中。
#### 2.2 二进制运算指令
* bt %1,%2 //bit test
bts %1,%2 //把%2对应地址的%1位填入CF位然后把%2对应地址的%1位置1(bit test and set)
btr %1,%2 //把%2对应地址的%1位填入CF位然后把%2对应地址的%1位置0(bit test and reset)
btc %1,%2 //把%2对应地址的%1位填入CF位然后把%2对应地址的%1位反转(bit test and complement)
#### 2.3 十进制运算指令
#### 2.4 逻辑运算指令
#### 2.5 控制转移指令
##### rep
- repne: zf=0且cx>0则重复
* repe: zf=1且cx>0,则重复
* rep: cx>0,则重复
##### iret
iret指令的执行过程iret指令用于从int指令返回
1. 从内核栈出弹出相应寄存器的值
2. 如果存在特权级转换则会从内核栈中弹出用户态栈的ss:esp
3. 如果有错误码,需要软件完成错误码弹出的功能
##### int
- int指令的执行过程
1. 从IDT中获得描述符
2. 检查%cs的域CPL <= 描述符记录的特权级DPL(这使得内核可以禁止一些特权级系统调用). 如果目标段选择符的PL<CPL(即发生了特权级转换),就在CPU内部的寄存器中保存%esp和%ss的值
4. 从一个任务段描述符中加载%ss和%esp。(不能使用用户栈来保存值,因为用户可能还没有建立合适的栈,所以使用任务段中指定的栈,这个栈是在内核模式中建立的)
5. 将%ss压栈。
6. 将%esp压栈。
7. 将%eflags压栈。
8. 将%cs压栈。
9. 将%eip压栈。
10. 清除%eflags的一些位。
11. 设置%cs和%eip为描述符中的值。
* INT指令通过软件方法调用中断处理程序立即数(0~255)是IDT中要调用的中断例程的索引号。在保护模式下IDT是8字节描述符的数组描述符必须指明是中断、陷阱还是任务门。在实模式下IDT是4字节大小的指针的数组。不管是实模式还是保护模式IDT的基址的线性地址都由IDTR保存。
* INTO指令与INT指令相同只是它的立即数被隐式地指定为4,此中断只有在溢出标志位设置为1时才使用。
* INT指令表现的像是远程调用(far call)只是在压入返回地址之前标志寄存器也会被压入栈中。中断例程通过IRET指令返回IRET指令会把栈里的标志寄存器和返回地址弹出。
* 在实模式下INT指令会依次将标志寄存器CS寄存器返回地址压入栈中然后跳转到由中断号指定的长地址处。
#### 2.6 串与字符转移指令
* ins:串输入指令
* edx存放接口edi存放内存指针
insl:将edx指向的接口的数据放到edi指向的内存中
#### 2.7 块结构语言的操作指令
#### 2.8 标志控制指令
* cld:将标志寄存器flag的DF位方向标志位清零使变址寄存器SI,DI自动增1
* std将DF位置1,使SIDI自动减1
#### 2.9 协处理器接口指令
#### 2.10 段寄存器指令
* pushal 将所有通用寄存器的内容压入栈中入栈顺序eax,ecx,edx,ebx,esp,ebp,esi,edi
#### 2.11 其它指令
* invlpg m // 清空TLB
* SGDT/SIDT 将描述符表寄存器中的内容复制到由操作数定义的6字节的内存中。寄存器的LIMIT域赋值给有效地址的第一个字中。如果操作数是32位的则接下来的3个字节由寄存器的BASE域赋值第4个字节会写入0因为最后一个字节未定义。否则如果操作数是16位的接下来的4个字节由寄存器的32位BASE域赋值。
## 二、系统编程
### 1. 系统架构
### 2. 内存管理
### 3. 保护模式
保护模式提供了虚拟内存管理。
#### 3.1 寄存器
![保护模式下的寄存器](_img/protect_mode.jpeg)
* 保护模式下新增的寄存器:
* GDTR全局描述符表寄存器(48位=32位基址+16位限长)
* LDTR局部描述符表寄存器
* TR任务寄存器
TR用于任务切换存放了一个16位的选择符用以指示GDT中任务状态段描述符(TSS)的位置。TSS描述符定义了任务状态段的起始地址和大小。每个任务都有自己的TSS存放进程运行时所需的硬件上下文。
* CR0CR3四个控制寄存器
| 寄存器名 | 意义 |
| -------- | ------------------------------------------------------------ |
| CR3 | page directory base register(PDBR), store phisical address of the current page directory. |
| CR2 | 导致页错误的线性地址 |
| CR1 | 保留不用 |
| CR0 | 控制协处理器和保护控制 |
| CR0的位 | 意义 |
| ------- | ------------------------------------------------------------ |
| PG | 1开启分页0关闭分页。 |
| PE | 1开启保护模式0处于实模式。注进入保护模式后就无法再回到实模式。 |
| MP | 1系统中存在一个数学协处理器0系统中不存在数学协处理器。 |
| EM | 1系统使用软件模拟器执行数学运算。 |
| R | 1表示安装的是80387数学协处理器。 |
| TS | 任务切换位,由处理器自动设置,也可由软件清零。 |
* EIP的长度扩展到32位
* EFLAGS有32位比实模式下的16位FLAGS新增了5个位。**IOPL**输入输出特权级占2位指示执行IO操作时需要的特权级。**NT**嵌套任务标志,指明当前任务是否嵌套,由硬件自动设置,用软件清零。**RF**为恢复标志。**VM**为虚拟8086标志。
* CSDSESSSFSGS功能改变
实模式下的段寄存器在保护模式下被称为段选择符寄存器里面存放的值不再是段基址而是段选择符。选择符用来选择位于GDT或LDT中的一个段描述符。段选择符格式如下
| 153 | 2 | 10 |
| ----- | ---------------------- | --------------- |
| index | TI(1访问GDT0访问LDT) | RPL(请求特权级) |
* 全局描述符表GDT
GDT中放的是段描述符每个段描述符占8个字节。GDT最大尺寸为0x10000个字节最多可放0x2000个表。
* 局部描述符表LDT
LDT中放的是段描述符。
#### 3.2 段描述符
段描述符格式如下:
| 字节 | 意义 |
| :--: | :--------------------: |
| 7 | 段基址31~24 |
| 6 | G+X+O+AVL+段限长1916 |
| 5 | P+DPL+S+类型+A |
| 4 | 段基址23~16 |
| 3 | 段基址15~8 |
| 2 | 段基址7~0 |
| 1 | 段限长15~8 |
| 0 | 段限长7~0 |
- 第6个字节说明了描述符的属性
- G: GRANULARITY1段长度以页(4K)为单位0段长度以字节为单位。
- X1该段为32位代码段0该段为16位代码段。
- AVL: AVAILABLE FOR USE BY SYSTEMS PROGRAMMERS。
- 第5个字节说明了描述符的访问权限
- P: SEGMENT PRESENT1段不在内存中0段在内存中
- DPL: DESCRIPTOR PRIVILEGE LEVEL描述符特权级
- S: 代码段或数据段描述符0系统段或门描述符
- A: ACCESSED1访问过0未访问过
#### 3.3 地址映射
1. 寻址与内存保护
![addressing](_img/addressing.png)
2. 虚拟地址转换过程
![地址转换示意图](_img/address_mapping.jpeg)
#### 3.4 保护机制
四种特权级如下所示大多数操作系统只用0和3两个特权级
| 编号 | 特权级 |
| ------ | -------- |
| ring 0 | 内核 |
| ring 1 | 系统调用 |
| ring 2 | 共享库 |
| ring 3 | 应用程序 |
在硬件支持下,通过软件对存储器访问进行检查和限制的防护措施称为**保护**。
- 保护性检查
保护性检查是由MMU硬件完成的检查项目如下
- 类型检查:检查存放段选择符的段寄存器与要访问的段描述符类型是否一致。
- 限长检查:检查虚拟地址是否在段限长之内,以确保该段的访问没有越界。
- 指令集的限制有16条指令只能在特权级为0的代码段中执行。
- I/O限制I/O指令只能在小于等于IOPL的特权级上执行。
- LDT的限制每个任务的LDT都只能自己使用而拒绝其它任务使用。
- 越级调用限制CALL选择符-->门描述符-->段描述符
- 越级访问代码段和数据段
若要从低特权级访问高特权级内存段中的的代码和数据,需要使用门描述符。
### 4. 多任务
80386没有为多任务处理添加新的特殊指令而是使用了特殊的数据结构。支持多任务处理的数据结构和寄存器是任务状态段(Task state segment),任务状态段描述符(Task state segment descriptor),任务寄存器(Task register),任务门描述符(Task gate descriptor)。
### 5. 输入输出
#### 5.1 I/O地址映射
- 80386允许两种方式的I/OI/O地址空间(使用特殊I/O指令)I/O内存映射(无需特殊指令)
- I/O地址空间
![IO地址空间](/home/szx/Desktop/shzhxh.github.io/Languages/Assembly/_img/IOaddressing.jpeg)
- I/O地址空间的0页(0000H~00FFH)是主板上I/O芯片的接口地址共计256字节的寻址区。这些芯片是计时器/计数器、中断控制器、DMA控制器、并行接口和键盘控制器。
- 其它的I/O地址空间(0100H~FFFFH)用于主板扩展槽上的接口控制卡(也叫适配器)。如图形适配器、声卡、网卡等。
### 6. 异常与中断
中断来自于CPU外部的异步事件而异常来自于CPU自身在指令执行过程中检测到的条件。
- 有两种中断:可屏蔽,不可屏蔽。
- 有两种异常:由处理器检测的(包括faults, traps, aborts),可编程的(指令INTO, INT 3, INT n, BOUND都能触发异常这些异常也被叫做“软件中断”但处理器也把它们作为异常来处理)。
#### 6.1 未定义
#### 6.2 IDT与IDTR
- IDTR中断描述符表寄存器(48位=32位基址+16位限长)
LIDT指令将内存中IDT长度及起始地址加载到IDTR
SIDT指令将IDTR的内容加载到内存中
- 中断描述符表IDT
IDT中放的是中断门描述符(即中断处理程序的入口)IDT中有256个门描述符每个门描述符占8个字节。
有4种类型的门描述符如下表所示
| 门描述符类型 | 作用 |
| -------------- | ------------------------------------------ |
| 调用门 | 从当前特权级到更高特权级的间接转移 |
| 任务门 | 将控制传给请求特权级比当前特权级高的任务 |
| 中断门和陷阱门 | 将控制传给当前任务的高特权级的其它服务程序 |
门描述符如下表所示(5字节的0位1陷阱,0中断)
| 字节 | 任务门 | 中断门/陷阱门 | 调用门 |
| ---- | -------------- | -------------- | -------------- |
| 7 | 未使用 | offset[31:24] | offset[31:24] |
| 6 | 未使用 | offset[23:16] | offset[23:16] |
| 5 | P+DPL+TYPE | P+DPL+TYPE | P+DPL+TYPE |
| 4 | 未使用 | 000+未使用 | 000+双字计数 |
| 3 | selector[15:8] | selector[15:8] | selector[15:8] |
| 2 | selecotr[7:0] | selecotr[7:0] | selecotr[7:0] |
| 1 | 未使用 | offset[15:8] | offset[15:8] |
| 0 | 未使用 | offset[7:0] | offset[7:0] |
门描述符里各位的意义如下表所示:
| 位 | 意义 |
| --------------- | ------------------------------------------ |
| P(1 bit) | 1描述符有效0描述符无效 |
| DPL(2 bit) | 描述符特权级 |
| TYPE(5 bit) | 0xC调用门0x9任务门0xE中断门0xF陷阱门 |
| 双字计数(5 bit) | 只用在调用门中 |
#### 6.3 硬件中断的产生
```
1, CPU查询中断控制器8259A),读取中断向量
2, CPU从IDT中查中断向量对应的描述符读取段选择子
3, CPU通过段选择子从GDT中取得段描述符跳转到中断服务例程的地址
4, CPU通过CPL、DPL判断是否发生特权级转换并决定是否进行内核栈和用户栈之间的转换。
5, CPU将被打断程序的寄存器保存在内核栈中
6, CPU将中断例程的地址加载到cs:eip寄存器中开始执行中断例程
```
#### 6.4 硬件中断的结束
```
1, 从内核栈出弹出相应寄存器的值
2, 如果存在特权级转换则会从内核栈中弹出用户态栈的ss:esp
3, 如果有错误码,需要软件完成错误码弹出的功能
```
#### 6.5 中断汇总
前32个中断是由Intel保留由系统使用的即软件异常比如除0错误和访问非法的内存页。
| 中断号 | 意义 |
| --------- | -------------------- |
| 0 | 除0 |
| 1 | 调试 |
| 2 | 不可屏蔽中断 |
| 3 | 断点 |
| 4 | 溢出 |
| 5 | 边界检查 |
| 6 | 无效操作码,非法指令 |
| 7 | 协处理器不可用 |
| 8 | double fault |
| 9 | 保留(486后不再使用) |
| 10 | 无效的任务切换段 |
| 11 | 段不存在 |
| 12 | 栈异常 |
| 13 | 通用保护异常 |
| 14 | 页错误 |
| 15 | 保留 |
| 16 | 协处理器错误 |
| 17 31 | 保留 |
| 32 255 | `INTR pin`外部中断 |
### 7. 初始化
### 8. 协处理器和多处理器
### 9. 调试
## 三、兼容性
### 1. 执行80286保护模式的代码
### 2. 实模式
实模式是用16位的寄存器访问20位的物理地址这是由CPU中的MMU单元自动完成的。
### 3. 虚拟8086模式
### 4. 16位与32位代码的混合
## 四、指令集

View File

@ -0,0 +1,146 @@
#### long mode
长模式是指把寄存器扩展为64位添加了8个整数寄存器和8个SSE寄存器。线性地址扩展到64位物理地址扩展到52位。从本质上讲长模式是给CPU增加了一个新的模式。
长模式不支持硬件任务切换或虚拟8086模式。长模式下由CS决定代码运行状态64位(真正的长模式)32位或16位(兼容模式)。分页是强制使用的,由于性能原因分段已经被简化了。长模式下的分段是平直模型(a flat model)除了两个寄存器FS和GS。可以通过MSR寄存器里的FS.base和GS.base来设置这两个段寄存器的基址。
#### 寄存器
##### 通用寄存器
`rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp, r8~r15`
##### 指针寄存器
`rip`
##### 段寄存器
`cs, ds, ss, es, fs, gs `
##### 标志寄存器
`rflags`
##### 控制寄存器
`cr0, cr2, cr3, cr4, cr8`
- cr4寄存器各个位的意义。
| 位 | 标签 | 描述 |
| ---- | ---- | ------------------------------------------------------------ |
| ... | | |
| 4 | PSE | Page Size Extension1允许每个页为4M0每个页只能是4K |
| 5 | PAE | Physical Address Extension1允许使用32位以上物理地址0只能使用32位物理地址 |
| ... | | |
- cr3寄存器各个位的意义。cr3是页目录基址寄存器保存当前页目录的物理地址。
| 位 | 标签 | 描述 |
| ----- | ---- | ---------------- |
| | | |
| | | |
| 12-63 | | PML4的物理基地址 |
- cr0寄存器控制协处理器和保护控制。
其它的cr寄存器(cr1, cr5-7, cr9-15)是保留的,访问会产生异常。
##### model-specific register(MSR)
###### MTRR寄存器
MTRR(Memory type range registers)是一组处理器附属功能控制寄存器它为系统软件提供如何访问内存区域的控制。它使用一组MSRs(model-specific registers)。对内存区域的访问模式有uncached, write-through, write-combining, write-protect,和 write-back.在write-back模式下先写到cache并将cache标记为dirty这样cache的内容就会被写到内存。
- MTRRCAP是一个只读的MSR编号0xfe它的作用是获取其它MTRR的信息。其结构如下
| 位 | 名称 | 意义 |
| ----- | ---- | ------------------------------------------------------ |
| 0 - 7 | VCNT | 可变范围寄存器的数量 |
| 8 | FIX | 1:支持固定范围寄存器0:不支持固定范围寄存器 |
| 10 | WC | 1:支持write-combining内存0:不支持write-combining内存 |
| 其它 | 保留 | |
- DEF_TYPE编号0x2ff
- Variable-range Register Pairbase和mask两个寄存器成对出现编号从0x200开始。一共有8对这样的寄存器可映射8个可变的内存区域。
- Fixed-range由11个64位的寄存器组成共映射了88个固定内存范围。
- APIC-BASE编号0x1b。
###### IA32_EFER
Extended Feature Enable Register (EFER)使能SYSCALL和SYSRET指令用于长模式(long mode)的进入和返回。编号0xC0000080。
| 位 | 标签 | 描述 |
| ----- | ----- | ------------------------------ |
| 0 | SCE | 系统调用扩展 |
| 1-7 | 0 | 保留 |
| 8 | LME | 长模式使能 |
| 10 | LMA | 长模式active |
| 11 | NXE | 非执行使能 |
| 12 | SVME | Secure Virtual Machine Enable |
| 13 | LMSLE | Long Mode Segment Limit Enable |
| 14 | FFXSR | Fast FXSAVE/FXRSTOR |
| 15 | TCE | Translation Cache Extension |
| 16-63 | 0 | 保留 |
###### FS.base, GS.base
FS的编号0xC0000100GS的编号0xC0000101。
###### KernelGSBase
编号0xC0000102。
###### IA32_TSC_AUX
编号0xC0000103。是一个辅助的TSC寄存器与IA32_TSC结合使用。
##### 调试寄存器
`DR0-DR3, DR6, DR7`
##### 测试寄存器
`TR3-TR5, TR6, TR7`
##### 保护模式寄存器
`GDTR, LDTR, TR, IDTR`
#### cpuid指令
可以从[osdev](https://wiki.osdev.org/CPUID)获取关于cpuid的详细信息。
用于获取处理器信息其操作码为0x0fa2。从eax寄存器获得输入将信息输出到从eax到edx的四个寄存器。
| eax(写入) | eax | ebx | ecx | edx |
| ---------- | -------------------- | -------------- | -------------- | -------------- |
| 0 | 最大的基本功能号 | 厂商信息 | 厂商信息 | 厂商信息 |
| 1 | 处理器签名 | Misc Info | 属性标志 | 属性标志 |
| 2 | cache和TLB | cache和TLB | cache和TLB | cache和TLB |
| 3 | 保留 | 保留 | 处理器序列号 | 处理器序列号 |
| 0x80000000 | 最大的扩展功能号 | | | |
| 0x80000001 | | | 扩展功能标志 | 扩展功能标志 |
| 0x80000002 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 |
| 0x80000003 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 |
| 0x80000004 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 | 处理器名和商标 |
| 0x80000005 | | | | |
| 0x80000006 | | | L2cache信息 | |
| 0x80000007 | | | | 电源管理 |
| 0x80000008 | 物理和虚拟地址的位数 | | | |
当向eax写入1并执行cpuid指令可以从eax中得到处理器签名从edx和ecx得到的是cpu支持哪些功能。edx的第12位MTRR即Memory Type Range Registers。edx的第9位即APIC。
当向eax写入0x80000001并执行cpuid指令可以从edx和ecx得到扩展功能标志。edx的第29位即long mode。
#### rdmsr和wrmsr指令
rdmsr指令的作用是读取msr寄存器的内容。msr寄存器由ecx寄存器的值决定读取的内容高32位放在edx寄存器中低32位放在eax寄存器中。
wrmsr指令是把edx:eax的值写入到由ecx指定的msr寄存器中。

View File

@ -0,0 +1,46 @@
参考资料
https://sourceware.org/binutils/docs/as/CFI-directives.html
https://sourceware.org/binutils/docs/as/RISC_002dV_002dDirectives.html
#### 汇编指示(伪操作)
汇编程序中以"."开头的名称不是指令的助记符,不会被翻译成机器指令,而是给汇编器一些特殊的指示,称为汇编指示(Assembler Directive)或伪操作(Pseudo-operation)。
##### .section指示
.section指示把代码划分成若干个段(section),程序被操作系统加载时,每个段被加载到不同的地址,具有不同的读写执行权限。
- .data段保存程序的数据是可读写的C程序的全局变量也属于.data段。
- .text段保存代码是只读和可执行的。
- .bss段是未初始化的数据段
##### .globl指示
.globl指示告诉汇编器_start这个符号要被链接器用到所以要在目标文件的符号表中给它特殊标记。
##### .long指示
.long指示声明一组数每个数32位相当于C数组。
##### .byte指示
.byte指示也是声明一组数每个数8位
##### .ascii指示
声明一个字符串每个字符取值为相应的ASCII码。和C语言不同的是这样声明的字符串末尾是没有'\0'字符的。
##### .cfi_startproc和.cfi_closeproc
它们成对出现,分别在函数的开头和结尾。.cfi_startproc初始化一些内部数据结构并注入初始CFI指令。.cfi_closeproc关闭由.cfi_startproc打开的入口。
##### .cfi_undefined
指定寄存器的值将不能再恢复了。
##### .option
修改与汇编代码相关的汇编器选项。

23
Languages/CLang/CLang.md Normal file
View File

@ -0,0 +1,23 @@
[Home](../)->C语言
参考:[GCC用法](https://gcc.gnu.org/onlinedocs/gcc/)
《C程序设计语言》
---
[预处理指令](preprocessor)
[内联汇编](inline-assembly)
### 数据类型
[struct](struct)
### 关键字
[keyword](keyword)
### 函数库
[printf](printf)
[文件操作](file-operation)
[字符串操作](string-operation)
[builtin函数](builtin-function)
[poll](poll)
### 编译错误汇总
[编译错误](compiling-errors)

View File

@ -0,0 +1,7 @@
可以从[gcc onlinedocs](https://gcc.gnu.org/onlinedocs/gcc-4.4.5/gcc/Other-Builtins.html#Other-Builtins)获取更多信息。
```c
int __builtin_constant_p (exp); // 用来确定exp是否在编译时是常量是则返回1,否则返回0.
long __builtin_expect(long exp,long c); // 为编译器提供分支预测的信息,意为期望exp的值与c的值相等。也可以使用参数'-fprofile-arcs'来让gcc收集分支走向的信息然而对于很多程序来说这样的信息是很难收集的。exp为一个整型表达式c必须是一个编译期的常量返回值为第一个参数exp的值。
void __builtin_trap(void); // 此函数将使程序异常的退出。GCC通过宿主机的相关机制或调用abort实现此函数。
```

View File

@ -0,0 +1,75 @@
官方文档: [Inline-Assembly-HOWTO](http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html) & [Using-Assembly-Language-with-C](https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C)
#### 基本内联汇编的格式:
```c
_asm_ _volatile_("instruction list");
```
#### 带有C/C++表达式的内联汇编格式:
```c
_asm_ _volatile_("instruction list":output:input:modify);
// %0,%1代表占位符从%0到%9共有10个分别代表第0个到第9个C表达式。
// 匹配限制符从"0"到"9"共10个也代表的是从0个到9个的C表达式用于先读后写处理。
// r: auto select a,b,c,d
// q: auto select a,b,c,d
// m: memory
// g: r or m
// a: %eax or %ax or %al
// b: %ebx or %bx or %bl
// c: %ecx or %cx or %cl
// d: %edx or %dx or %dl
// D: %edi or %di
// S: %esi or %si
// i: 立即数
```
#### 一些例子
```c
/*
* 第1个例子
*/
asm volatile ("inb %1,%0" : "=a" (data) : "d" (port) : "memory");
// data是要输出的c表达式a是要赋值给data的寄存器=表示data是只写的什么都没有表示data是只读的+表示data是可读写的
// C表达式port通过寄存器d输入到当前内联汇编语句中
// "=a"(data)为第一个C表达式其指定的寄存器为a故%0代表了寄存器a
// "d"(port)为第二个C表达式其指定寄存器为d,故%1代表了寄存器d
// 内存被修改了,但在输出表达式中未指明"m"约束故需在modify部分指明"memory"被修改了。
// 如果寄存器在指令列表中被修改了,而输入/输出部分未进行约束需在modify部分指明这一点。
/*
* 第2个例子
*/
asm volatile ("outb %0,%1" :: "a" (data), "d" (port));
// 本例中没有输出
// 将data的值通过寄存器a输入将port的值通过寄存器d输入
// %0代表第一个寄存器a,%1代表第二个寄存器d
// 不存在未被约束过的寄存器或内存故modify部分无须指定
/*
* 第3个例子
*/
asm volatile (
"cld;"
"repne; insl;"
: "=D" (addr), "=c" (cnt) //通过D寄存器输出给addr,通过c寄存器输出给cnt
: "d" (port), "0" (addr), "1" (cnt) //port通过d寄存器输入addr通过D寄存器输入cnt通过c寄存器输入
: "memory", "cc"); //cc表示内联汇编语句会影响到eflags寄存器
/*
* 第4个例子
*/
asm volatile ("btsl %1, %0" :"=m" (* (volatile long * )addr) : "Ir" (nr));
//将 (* addr) 的第 nr 位设为 1。第一个占位符 %0 与 C 语言变量 addr 对应,第二个占位符 %1 与 C 语言变量 nr 对应。
//因此上面的汇编语句代码与下面的伪代码等价btsl nr, ADDR
//该指令的两个操作数不能全是内存变量因此将nr的限定字符串指定为“Ir”将nr与立即数或者寄存器相关联这样两个操作数中只有addr为内存变量。
/*
* 第5个例子
*/
asm volatile (
"int %1;"
: "=a" (ret)
: "i" (T_SYSCALL), "0" (SYS_exec), "d" (name), "c" (len), "b" (binary), "D" (size)
: "memory");
// 执行的是 int T_SYSCALL, 执行前先将SYS_exec传给eax执行完后将eax的值返回给ret。这实际上是系统调用的指令。
```

View File

@ -0,0 +1,44 @@
描述c语言中的关键字
#### attribute
官方文档https://gcc.gnu.org/onlinedocs/gcc/Attribute-Syntax.html
* align属性内存内按字节对齐
* always_inline属性GCC会根据代码逻辑判断是否使用inline代码但如果使用了always_inline,则一定会将函数内嵌进去。
* constructor:在main函数运行之前就可运行完毕。
* destructor:在进程exit之前执行。
* fastcall & regparm:函数传递参数通常是用堆栈,为了加快代码运行速度,使用这两个属性,会用寄存器来传递参数。
* packed属性按实际大小来计算struct,union,enum数据结构。
* section属性把代码放到特定的section中。
#### extern
参考自《The C programming language》1.10外部变量与作用域
作用: 声明外部变量
#### inline
官方文档https://gcc.gnu.org/onlinedocs/gcc/Inline.html#Inline
作用:表示此函数被调用时将直接插入,而不是像普通函数那样生成调用代码。
#### static
* static全局变量
和其他全局变量一样,都是存储在.data段已初始化或者.bss段未初始化但它只在定义它的源文件内有效其他源文件无法访问它。
* static局部变量
static局部变量分配在.data段中普通局部变量分配在栈空间中;只能在其作用域中被访问而不能被其他函数或源文件访问其值如果没有被用户初始化则会被系统初始化为0.
* static函数
相当于面向对象语言中的private函数只在定义他的源文件内有效其他源文件无法访问它。
#### volatile
参考:[volatile对象](https://gcc.gnu.org/onlinedocs/gcc/Volatiles.html#Volatiles)
描述volatile的意思是易变的此关键字的意思是告诉编译器不要对其所修饰的变量进行优化。
#### continue
* 用于循环体中continue语句会跳过循环体后续的代码再次进入循环体继续执行。
* 而break语句会直接跳出循环体不再执行循环体。另外在条件判断时break语句也用于跳出条件判断的语句块。

View File

@ -0,0 +1 @@
为程序增加诊断功能。

View File

@ -0,0 +1 @@
ctype.h声明了一些测试字符的函数。

View File

@ -0,0 +1,47 @@
getopt.h是用来获取函数参数的。函数getopt只支持短格式函数getopt_long长、短格式都支持函数getopt_long_only与getopt_long基本相同区别在于长选项字串是用"-"开始的,而不是"--"。
#### 变量
```
extern char *optarg; # 用于getopt和调用者之间通信当getopt找到带有参数的选项时参数值放在这里。
extern int optind, opterr, optopt;
# optindargv里下一个待处理参数的索引。用于对getopt的重复调用。
# opterr调用者在这里存储0以禁止getopt因未识别的选项而打印错误消息。
# optopt设置未识别的选项字符。
```
#### getopt
`int getopt(int argc, char * const argv[], const char *optstring);`
作用:解析命令行参数。当重复调用,它会依次返回各个参数里的字符串。当无参数可解析时,返回-1。
argc参数数量来自于main函数。
argv参数数组来自于main函数。以'-'开头的被认为是一个参数。
optstring
#### getopt_long
```
extern int getopt_long (int argc, char *const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
```
#### getopt_long_only
```
extern int getopt_long_only (int argc, char *const argv[],
const char *optstring,
const struct option *longopts, int *longindex);
```
argc, argv直接从main函数传递而来
shortopts短选项字符串。需要传递参数时要在后面加冒号。
longopts结构体的数组用于存放长选项参数。
longind长选项在longopts中的索引用于调试一般为NULL。

View File

@ -0,0 +1 @@
math.h中声明了一些数学函数和宏。

View File

@ -0,0 +1,4 @@
#### 作用:
- 等待一个文件描述符上的事件。和系统调用select()的作用相近它会等待一个文件描述符的集合是否准备好进行I/O。
#### 描述:
- 函数原型:`int poll(struct pollfd *fds, nfds_t nfds, int timeout)`

View File

@ -0,0 +1 @@
非局部跳转

View File

@ -0,0 +1 @@
处理异常

View File

@ -0,0 +1,37 @@
### 简介
关于可变参数的处理,详见`stdarg.h`
通过`man stdarg`获取帮助
### 数据结构
#### va_list
是一个变量类型,它将依次指向每个参数。
### 宏
#### va_start
`void va_start(va_list ap, last);`
在访问可变参数前必须用va_start初始化变量ap。last是可变参数之前最后一个参数的名称。
#### va_arg
`type va_arg(va_list ap, type);`
将返回下一个参数。
#### va_end
`void va_end(va_list ap);`
参数处理完毕后必须调用va_end一次。
#### va_copy
`void va_copy(va_list dest, va_list src);`
将可变参数表src复制到dest。

View File

@ -0,0 +1,118 @@
#### 基本概念
##### 流
流是数据的源或目的地它实际上对应着的是一个设备比如磁盘。程序开始执行时stdin, stdout, stderr这3个流已经打开。
#### 格式化输出的函数
```c
int fprintf(FILE *stream, const char *format, ...)
// fprintf函数将带有格式标记的字符串format写到stream流中。返回值是实际写入的字符数若出错则返回一个负值。
- printf(...)等价于fprintf(stdout, ...)
- dprintf是将输出流设定为文件描述符fd。
- sprintf(char *s,const char *format, ...)是将format写入到字符串s中。
- snprintf类似于sprintf但给定了字符串的长度。
- vprintf等价于printf,但用arg代替了可变参数表arg由宏va_start或va_arg初始化。
- vfprintf等价于fprintf但用arg代替了可变参数表arg由宏va_start或va_arg初始化。
- vdprintf等价于dprintf但用arg代替了可变参数表。
- vsprintf等价于sprintf,但用arg代替了可变参数表arg由宏va_start或va_arg初始化。
- vsnprintf等价于snprintf但用arg代替了可变参数表。
```
##### format的结构
格式化字符串format 普通字符串 转换说明
转换说明 % + 约束部分 + 转换字符
##### format约束部分
标志:
- // 左对齐
+ // 加正负号
空格 // 如果第一个字符不是正负号,则在其前面加空格
0 // 对于数值转换当输出长度小于字段宽度时则在其前面填充0
# // 指定另一种输出形式o转换则加前缀ox或X转换加前缀0x或0Xe、E、f、g、G转换结果有小数时才给出小数点。
数值(宽度) // 指定最小字段宽度
. // 分隔字段宽度和精度
数值(精度) //对于字符串指定最大长度e、E或f转换指定小数点后数位的长度g或G转换指定有效位长度对整型数则指定数位长度。
h // 按short或unsigned short输出。
l // 按long或unsigned long输出。
L // 按long double输出。
* // 宽度或精度的任何一个都可以用*指定
##### format转换字符
d,i // int,有符号十进制数
u // unsigned int,无符号十进制数
o // unsigned int,无符号八进制数
x,X // unsigned int,无符号十六进制无前导0x或0X)
c // int
s // char *,字符串
f // double,双精度型
e,E // double,双精度型(指数形式)
g,G // double,f或e,E类型取决于限定条件
p // void *,指针
n // int *,到当前为止输出的字符数
% // 不转换,打印%
#### 格式化输入函数
```c
int fscanf(FILE *stream, const char *format, ...); // 依据格式串format从*stream中读取输入把值赋给后续各个参数
int scanf(const char *format, ...); // 等价于fscanf(stdin, *format, ...)
int sscanf;
```
#### 字符输入输出函数
```c
int fgetc();
int fgets();
int fputc();
int fputs();
```
#### 文件操作函数
```c
FILE *fopen(const char *path, const char *mode);
// 打开文件名为path的文件并关联到一个流。参数mode指打开的模式。
FILE *fdopen(int fd, const char *mode);
FILE *freopen(const char *path, const char *mode, FILE *stream);
int fclose(FILE *stream); // 关闭流
```
##### 解读mode
- r以读的方式打开文件流指向文件开头。
- r+:以读写的方式打开,流指向文件开头。
- w将文件截断为0长度或创建可写的文件流指向文件开头。
- w+:以读写的方式打开,文件不存在则会创建,存在则截断。流指向文件开头。
- a以附加的方式打开文件不存在则会创建流指向文件末尾。
- a+:以附加且可读的方式打开,文件不存在则会创建,但读是从文件开头,写是从文件末尾。
- 还可以附加`b`表示是二进制但这对POSIX兼容系统来说没有效果而只是为了与C89兼容。
#### 直接输入输出函数
```c
size_t fread(void *ptr, size_t size, size_t nobj, FILE *stream); // 从流stream中最多读取nobj个长度为size的对象保存到ptr指向的数组中。返回读取的对象数此值可能小于nobj。
size_t fwrite(void *ptr, size_t size, size_t nobj, FILE *stream); // 从ptr指向的数组中读取nobj个长度为size的对象输出到流stream中。返回输出的对象数。
```
#### 文件定位函数
```c
int fseek(FILE *stream, long offset, int origin); // 设置流stream的位置为从origin开始的第offset个字符后续的读写操作将从新位置开始。origin的值可以是SEEK_SET(文件开始)SEEK_CUR(当前位置)SEEK_END(文件末尾)
long ftell(FILE *stream); // 返回stream流当前的位置
```
#### 错误处理函数
```c
void perror(const char *s); // 打印字符串s与错误信息
int feof(FILE *stream); // 测试流stream的文件结束指示符如果设置了则返回非0。
```

View File

@ -0,0 +1 @@
实用函数

View File

@ -0,0 +1,37 @@
1. 字符串操作
* strcpy(p, p1) 复制字符串
* strncpy(p, p1, n) 复制指定长度字符串
* strcat(p, p1) 附加字符串
* strncat(p, p1, n) 附加指定长度字符串
* strlen(p) 取字符串长度
* strcmp(p, p1) 比较字符串
* strcasecmp忽略大小写比较字符串
* strncmp(p, p1, n) 比较指定长度字符串
* strchr(p, c) 在字符串中查找指定字符
* strrchr(p, c) 在字符串中反向查找
* strstr(p, p1) 查找字符串
* strtok(p,p1) 依据p1中的字符分割字符串p
* strpbrk(p, p1) 以目标字符串的所有字符作为集合,在当前字符串查找该集合的任一元素
* strspn(p, p1) 以目标字符串的所有字符作为集合,在当前字符串查找不属于该集合的任一元素的偏移
* strcspn(p, p1) 以目标字符串的所有字符作为集合,在当前字符串查找属于该集合的任一元素的偏移
* 具有指定长度的字符串处理函数在已处理的字符串之后填补零结尾符
2. 字符串到数值类型的转换
* strtod(p, ppend) 从字符串 p 中转换 double 类型数值,并将后续的字符串指针存储到 ppend 指向的 char* 类型存储。
* strtol(p, ppend, base) 从字符串 p 中转换 long 类型整型数值base 显式设置转换的整型进制,设置为 0 以根据特定格式判断所用进制0x, 0X 前缀以解释为十六进制格式整型0 前缀以解释为八进制格式整型
* atoi(p) 字符串转换到 int 整型
* atof(p) 字符串转换到 double 符点数
* atol(p) 字符串转换到 long 整型
3. 字符检查
* isalpha() 检查是否为字母字符
* isupper() 检查是否为大写字母字符
* islower() 检查是否为小写字母字符
* isdigit() 检查是否为数字
* isxdigit() 检查是否为十六进制数字表示的有效字符
* isspace() 检查是否为空格类型字符
* iscntrl() 检查是否为控制字符
* ispunct() 检查是否为标点符号
* isalnum() 检查是否为字母和数字
* isprint() 检查是否是可打印字符
* isgraph() 检查是否是图形字符,等效于 isalnum() | ispunct()

View File

@ -0,0 +1,50 @@
string.h定义了两组函数一组以str开头一组以mem开头
#### 定位字符串中的字符
- char *strchr(const char *s, int c);
返回字符c在字符串s中第一次匹配时的指针若不能匹配则返回NULL。
- char *strrchr(const char *s, int c);
返回字符c在字符串s中最后一次匹配时的指针若不能匹配则返回NULL。
- char *strchrnul(const char *s, intc);
类似于strchr不同点在于如果没有在s里匹配到c不是返回的NULL而是返回在s后面那个null字节(应该指的是字符串后面那个`\0`字节)的指针。
#### 连接两个字符串
- char *strcat(char *dest, const char *src);
在字符串dest后面附加src字符串。要求两个字符串不可重叠且dest要有足够的空间。
- char *strncat(char *dest, const char *src, size_t n);
类似于strcat不同点在于最多只使用src里的n个字节如果src包含n个或更多的字符它也不必使用null作为终结符。
#### 复制字符串
```
char *strdup(const char *s); //复制字符串s并返回其指针。
char *strndup();
char *strdupa();
char *strndupa();
```
#### 复制内存区域
- void *memcpy(void *dest, cosnt void *src, size_t n);
从src向dest复制n个字节的内容。要求src和dest两个内存区域不可重叠。其返回值是对dest的指针。
- bcopy
- memccpy
- memmove
- mempcpy
- strcpy
- strncpy
- wmemcpy

View File

@ -0,0 +1 @@
日期与时间

19
Languages/CLang/macros.md Normal file
View File

@ -0,0 +1,19 @@
#### 可变参数宏
```c
#define dgbmsg(fmt, ...) printf(fmt, __VA_ARGS__)
// ...代表一个可变参数表, 保留名__VA_ARGS__把参数传递给宏
// ##的作用是:如果可变参数为空或被忽略,预处理器会去掉它前面的逗号
```
#### 查看GCC预定义的宏
`gcc -E -dM - </dev/null`
#### \__attribute__
\__attribute__关键字用于定义变量或结构体的属性。
- section子项的用法
`__attribute__((section("section_name")))`其作用是将函数或数据放入名为setion_name的段。

View File

@ -0,0 +1,13 @@
**参考自《C程序设计语言》B&D**
#### 文件包含
用法: `#include "文件名"``#include <文件名>`
#### 宏替换
用法: `#define 名字 替换文本`
作用: 用 *替换文本* 替换 *名字*
用法: `#undef 名字`
作用: 取消 *名字* 的宏定义
#### 条件包含
1. `#if`后包含的整型表达式若非0,则包含其后各行,直到`#endif`,`#elif`或`#else`语句为止。`#elif`的作用类似于`else if`。
2. 表达式 `defined(名字)` 的规则是若名字已定义其值为1,否则其值为0。
3. `#ifndef 名字` 的作用等同于 `#if !defined(名字)`
4. `#ifdef 名字` 的作用等同于 `#if defined(名字)`

View File

@ -0,0 +1,2 @@
结构体中冒号的作用:
* 位域:冒号后面的数字表示冒号前面的变量占的位数

View File

@ -0,0 +1,145 @@
1. 介绍
Chisel只是一些专门的**类定义**,预定义的**对象**和对Scala**使用约定**的集合。所以写Chisel程序实际上就是用Scala语言描述硬件。
Chisel生成的是可综合的Verilog代码。Chisel也可以生成一个用C++实现的快速的、周期精确的RTL模拟器。
Verilog和VHDL是作为硬件**模拟**语言而开发的,之后它们才成为硬件**综合**的基础。它们的大部分语义并不适合硬件综合事实上大部分是不可综合的。而Chisel描述的直接就是可综合的电路而且它更接近于Verilog这样低层次的硬件描述语言而不是高层的综合系统。
2. 硬件表达
本版本只支持二进制逻辑,不支持三态信号。
3. 数据类型
- Bits:比特的原始组合
- Sint:有符号整数
- Uint:无符号整数
- Bool:布尔值
- Bundles:带有命名域的值的组合类似于C语言里的结构体
- Vecs:值的可索引的组合
4. 组合电路
Chisel里的电路是结点组成的图。每个结点都是**硬件操作符**,由零或多个输入驱动一个输出。
- &:按位与
- |:按位或
- ~:按位非
简单表达式可以直接转换为电路树,在叶子和操作符上形成内部结点。表达式的最终输出是从树根上的操作符提取出来的。
- val:用来命名值不会再变化的变量
Chisel也支持**电线**作为结点,可用来赋值或连接到其它结点。
- Wire:电线
5. 内置操作符
- 按位操作符:~ & `(overflow)^
- 按位归约andR, orR, xorR
- 相等比较:===, =/=
- 移位:<<, >>
- 位域操作x(), Fill(), Cat()
- 逻辑运算:! && ` Mux()
- 算术运算:+ +% +& - -% -& * / %
- 算术比较:> >= < <=
6. 功能抽象
我们可以定义函数来提取出重复的逻辑,在一个设计里重复使用。
- 定义函数def
7. Bundles和Vecs
Bundles和Vecs是使用户可以扩展数据类型的类。Bundle相当于C语言里的结构体Vec相当于数组。
8. 端口
端口是硬件组件之间的接口。基本端口Input和Output可以合成更复杂的端口。
9. 模块
Chisel的模块非常类似于在生成的电路中定义层次结构的Verilog模块。
用户定义的模块被定义为如下的类:
- 继承自Module
- 包含封装在模块IO方法的接口
- 存储在一个名为io的端口字段中
- 在其构造函数中连接子电路
10. 黑箱
Chisel的黑盒用来表示外部定义的模块。
11. 状态元素
最简单的状态元素是正边沿触发寄存器,可表示如下:
`val reg = RgeNext(in)`
此电路的输出是延迟一个时钟周期后输入信号in的复制。
计数器是重要的时序电路。
12. 内存
- ROM
用户可通过Vec定义只读内存
- MEM
- Masks
13. 接口与bulk连接
- 端口:子类与嵌套
- Bundle Vectors
- Bulk Connections
14. 功能模块创建
15. Mux与输入选择
- Mux2-输入选择器
- MuxCase多路选择器
- MuxLookup可索引多路复用器
- Mux1H接收一系列选择器和值返回与被设置的选择器相关联的值。
16. 多态与参数化
此部分是高级的,可在第一次阅读时跳过。
17. 多时钟域
18. 注解扩展Chisel和Firrtl
使用import引用我们需要的组件。
Write a transform
创建一个注解工厂
创建一个注解器
使用注解器
19. 运行时发生了什么?
执行模式
运行用scala写的chisel代码
执行模式的层次
执行模式代码
所有的选项
20. Chisel3 vs Chisel2
21. 致谢

115
Languages/Chisel/rocket.md Normal file
View File

@ -0,0 +1,115 @@
#### 简介
Rocket是一个开源的System-on-Chip设计生成器用以生成可综合的RTL (所以Rocket不是单个的设计实例而是一个设计实例的生成器可以生成多种设计实例)。
Rocket是用的Chisel语言写的一种嵌入在Scala语言里的开源硬件构建语言。它构建了一个RISC-V的平台。它是由一些参数化的构建库组成的就是用这些构建库来生成各种各样的RTL实例的。
Rocket生成通用处理器的核它以RISC-V为ISA其中**Rocket**生成有序的核,**BOOM**生成无序的核。
#### 仓库内容
##### 子模块
- [chisel3](https://github.com/ucb-bar/chisel3)用来生成RTL(Register-Transfer Level)
- [firrtl](https://github.com/ucb-bar/firrtl)(Flexible Internal Representation for RTL)是RTL的中间表示由Chisel3来使用通过它来生成最终代码(Verilog, C等),详细介绍见[specification][6]
- [hardfloat](https://github.com/ucb-bar/berkeley-hardfloat)浮点单元的Chisel代码用于融合的乘加运算、整数浮点数转换、不同精度的浮点数转换
- [riscv-tools](https://github.com/riscv/riscv-tools)riscv软件生态环境与本仓库中的RTL一起工作
- [torture](https://github.com/ucb-bar/riscv-torture):生成和执行随机指令流,用于对本设计进行压力测试
##### scala包
在`src/main/scala`目录下
- amba这个RTL包是AMBA协议的总线实现包括AXI4, AHB-lite, APB
- config通过动态范围的参数化库来配置生成器
- coreplex将其它包的各种组件粘合在一起生成一个完整的coreplex
- devices外围设备的实现包括调试模块和各种TL从设备。
- diplomacy通过两阶段的硬件细化来扩展chisel两个阶段的交互详情见[paper][7]
- groundtest生成可综合的硬件测试器通过发出随机内存访问来对非核内存进行压力测试
- jtag生成JTAG总线接口的定义
- regmapper生成带有标准化接口的从设备用于访问其内存映射的寄存器
- rocket生成rocket核和L1缓存
- tile包含能与核心相结合来构建tiles的组件比如FPU和加速器。
- tilelink用于生成Tilelink协议的总线实现还包含了各种适配器和协议转换器。
- system用Chisel详述coreplex的特定配置
- unittest用于生成单个模块的硬件测试器
- util可供其它包重用的组件
##### 其它资源
- bootromBootROM中的first-stage bootloader源代码
- csrc用于Verilator模拟的C源代码
- emulator编译和运行Verilator
- project由sbt使用用于scala编译和构建
- regression持续集成和nightly regression套件
- scripts解析模拟输出或操作源文件
- vsim编译和运行Synopsys VCS
- vsrcverilog源文件包含接口、harnesses和VPI
##### 扩展Top-Level设计
详见[project-template](https://github.com/ucb-bar/project-template)
#### 使用FPGA
##### 编译rocket
- 约定:**ROCKET_DIR**为rocket-chip所在的目录
- 约定:**RISCV_DIR**为riscv所安装的目录
```shell
# 1. 下载rocket源码
git clone https://github.com/ucb-bar/rocket-chip.git
cd rocket-chip
git submodule update --init
# 2. 编译riscv环境(因为对应了特定的提交版本,所以即使有编译好的环境也需要重新编译)
export RISCV=$RISCV_DIR
cd $ROCKET_DIR/riscv-tools
git submodule update --init --recursive
export MAKEFLAGS="$MAKEFLAGS -j8"
./build.sh
# 3. 编译生成verilog(生成的代码在$ROCKET_DIR/vsim/generated-src)
cd $ROCKET_DIR/vsim
sudo apt install openjdk-8-jdk git device-tree-compiler # need to install first
make verilog CONFIG=DefaultFPGAConfig
# 在使用以上命令时会出现大量unable to get resource的错误提示
# this is a bug with ubuntu 18.04, when I use 16.04, it resolved.
# 4. 保持仓库更新
cd $ROCKET_DIR
git pull origin master
git submodule update --init --recursive
# 如果riscv-tools的版本改变了需要重新编译
cd riscv-tools
./build.sh
```
##### 使用FPGA
```
git clone https://github.com/ucb-bar/fpga-zynq
git cd fpga-zynq
cd zedboard
make init-submodules # 初始化子模块
make rocket # 生成新代码
make project # 建立vivado项目
```
##### 使用xxx
##### 使用VLSI工具
#### 参数化
#### 使用GDB调试
#### 参考资源
[1]:https://github.com/ucb-bar/fpga-zynq "在zynq FPGA开发板上跑RISC-V所需要的文件"
[2]:https://github.com/freechipsproject/rocket-chip "rocket源码"
[3]:https://www2.eecs.berkeley.edu/Pubs/TechRpts/2016/EECS-2016-17.pdf "rocket的详细说明文档"
[4]:http://www.lowrisc.org/docs/tagged-memory-v0.1/rocket-core/ "rocket core overview"
[5]:http://www.lowrisc.org/docs/tagged-memory-v0.1/rocket-chip/ "rocket chip overview"
[6]:https://www2.eecs.berkeley.edu/Pubs/TechRpts/2016/EECS-2016-9.pdf "Specification for the FIRRTL Language"
[7]:https://carrv.github.io/2017/papers/cook-diplomacy-carrv2017.pdf "cook-diplomacy-carrv2017"

27
Languages/Chisel/sodor.md Normal file
View File

@ -0,0 +1,27 @@
#### 简介
sodor实现了若干riscv核它们都实现了RV32b的2.0版本都不支持虚拟内存只实现了m-mode的priv 1.10版本。它们使用的verilog文件是用chisel3生成的它与C++一起生成和运行Sodor仿真器。
| 流水 | | |
| ---- | -------------------------------- | ---- |
| 1级 | ISA模拟器 | |
| 2级 | 演示流水 | |
| 3级 | 使用连续内存 | |
| 5级 | 可以在完全绕过和完全互锁之间切换 | |
| 5级 | 基于micro-coded实现 | |
#### 运行
##### 编译模拟器
```
git clone https://github.com/ucb-bar/riscv-sodor.git
cd riscv-sodor
git submodule update --init --recursive
```
#### 参考资料
[Learning journey](https://github.com/librecores/riscv-sodor/wiki/Learning-journey)

13
Languages/HTML/html.md Normal file
View File

@ -0,0 +1,13 @@
来源:[w3school](http://www.w3school.com.cn)
* meta标签提供页面元信息(必须出现在head内部必须以值/标签的形式成对传递)
content定义与 http-equiv 或 name 属性相关的元信息
http-equiv把content属性关联到http头部
name把content属性关联到一个名称
scheme定义翻译content属性值的格式
* link标签连接一个样式表(只能出现在head内部)
href规定被链接文档的位置
rel规定前文档与被链接文档的关系
* div标签把文档分区或分节
* ul标签定义无序列表
* li标签定义列表项目

View File

@ -0,0 +1,9 @@
#### Thread
程序执行时的线程。Java虚拟机允许一个程序有多个线程。
##### 方法
- `static void sleep(long millis)`
让当前正在执行的线程休眠指定的毫秒数。

View File

@ -0,0 +1,13 @@
描述包含collections框架legacy collection类事件模型日期与时间工具国际化和各种实用的类a string tokenizer, 一个随机数生成器和一个位数组)
Interfaces:
Classes:
StringTokenizer(这个类充许应用程序将字符串拆分成tokens)
继承java.lang.Object
构造方法:
StringTokenizer(String str) //为str构造一个字符串标记器
方法:
boolean hasMoreTokens() //测试此标记器里的字符串是否有更多的tokens
nextToken() //从此字符串标记器里返回下一个token
Enums:
Exceptions:
Errors:

View File

@ -0,0 +1,23 @@
#### 接口与抽象类的关系
1. 类之间的共同行为,即可以用**父类与子类**的方式来表示,也可以用**接口**来表示。
2. 接口与抽象类很相似,但它的目的是指明类的共同行为,这些类可能是相关的,也可能是不相关的。
3. 类与类之间的继承关系是**扩展**(extends),而类与接口之间的继承关系是**实现**(implements)。
4. 一个类只能继承**一个**父类,但可以实现**多个**接口。
5. 一个接口可以继承多个接口。
6. 抽象类的属性是**无限制的**,接口的属性必须是**public static final**。
7. 抽象类**有**构造方法,接口**没有**构造方法。
8. 抽象类对方法**无限制**,接口的所有方法必须是**公共的抽象实例方法**。
#### 抽象类
1. 在继承的层次结构中,父类总是越来越抽象,子类总是越来越具体。当父类设计的非常抽象以至于没有任何的实例的时候,就成为了**抽象类**。
2. 当父类的方法依赖于子类的具体属性,而无法在父类中实现时,这样的方法就叫**抽象方法**。
3. 为什么要有抽象方法呢?因为这样可以方便两个属性不同的子类之间进行运算。
4. 抽象类包含抽象方法,不可用于创建对象。这些抽象方法将在抽象类的子类中实现。抽象类和抽象方法用**abstract**关键字修饰。注意:抽象类也可以**不包含**抽象方法,抽象类的父类也可能是具体的。
5. 抽象类的**构造方法**为protected因为它只被子类使用。
####接口
1. **接口**是一种与**类**相似的结构,只包含常量和抽象方法。
2. 接口使用**interface**关键字修饰。

View File

@ -0,0 +1,23 @@
### 来源:
* [知乎](https://www.zhihu.com/question/25483589)
* [阮一峰_asm.js和Emscripten入门教程](http://www.ruanyifeng.com/blog/2017/09/asmjs_emscripten.html)
* 《JavaScript高级程序设计》
### 工具
* asm.js
是由Mozilla提出的一个基于JS的语法标准主要是为了解决JS引擎的执行效率问题
* Emscripten
是Mozilla的一个实验性项目目的是把C/C++开发的应用编译成JS或HTML5的应用
emcc是Emscripten的编译器前端
**注意:** 在ubuntu系统通过apt命令安装存在问题应选择从源码编译安装
* uglifyjs是用JavaScript写的压缩JavaScript代码的工具
**格式**`uglifyjs [选项] [文件名]`
**--compress**:启用压缩选项
**--mangle**:输出变量名替换后的文件
### JavaScript语法
* JavaScript = ECMAscript + DOM + BOM
* 数据类型 (5种基本数据类型和2种复杂数据类型)
1. 基本数据类型Undefined, Boolean, Number, String, Null
2. 复杂数据类型Object, Array
3. 变量:字母、数字、$、下划线的组合

View File

@ -0,0 +1,36 @@
1. `sudo apt update //更新软件包到最新状态`
2. `sudo apt install python2.7 //安装python2.7`
3. `sudo apt install nodejs //安装nodejs`
4. `sudo apt install build-essential 安装gcc`
`sudo apt install cmake //安装cmake`
5. `sudo apt install git-core //安装git-core`
6. `sudo apt install default-jre //安装JavaScript`
7. 编译安装Fastcomp(LLVM + Clang)
```
mkdir myfastcomp
cd myfastcomp
git clone https://github.com/kripken/emscripten-fastcomp
cd emscripten-fastcomp
git clone https://github.com/kripken/emscripten-fastcomp-clang tools/clang
mkdir duild
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86;JSBackend" -DLLVM_INCLUDE_EXAMPLES=OFF -DLLVM_INCLUDE_TESTS=OFF -DCLANG_INCLUDE_EXAMPLES=OFF -DCLANG_INCLUDE_TESTS=OFF // 使用cmake配置cmake或configure二选一
../configure --enable-optimized --disable-assertions --enable-targets=host,js // 使用configure配置cmake或configure二选一
cat /proc/cpuinfo | grep "^cpu cores" | uniq // 查询CPU核心数量充分利用多核可以加快编译速度
make -j2 // 上步查到是2核所以我用-j2参数慢慢等待吧。。。
```
8. `git clone https://github.com/kripken/emscripten.git //复制Emscripten`
9. 修改配置文件~/.emscripten将LLVM_ROOT设置为第7步的目录`.../myfastcomp/emscripten-fastcomp/build/bin`
10. `./emcc -v //在emscripten目录运行此命令如果没报错即为安装正确`
11. `./emcc tests/hello_world.c //如果没报错即为运行正确`

View File

@ -0,0 +1,65 @@
#### 特殊目录:
. //当前目录
.. //上一层目录
- //上一个目录
~ //当前用户的用户目录
~a //用户a的用户目录
#### 目录操作:
cd // change directory
pwd // print working directory
mkdir // make directory
rmdir // remove directory
#### 文件与目录管理:
ls, cp, rm, mv
#### 文件内容查阅:
cat, tac, nl, more, less, head, tail, od, file
change file timestamps:
touch
#### 修改系统隐藏权限:
lsattr, chattr
#### 默认权限:
umask
#### 文件特殊权限:
SUID
SGID
SBIT
#### 命令与文件搜寻:
which, type, whereis, locate, find
#### 重定向
##### 背景知识
0,1,2分别代表stdin,stdout,stderr
##### 基本用法
1. `<`重定向输入
2. `>`重定向输出
3. `>!`如果存在则覆盖
4. `>&`屏幕输出的所有信息都放在指定文件中
5. `>>`追加到文件中
6. `>>&`屏幕上的信息追加到文件中
##### 用法示例
```shell
# 将屏幕输出的所有信息都放在文件test.log中
test.sh >& test.log
```

View File

@ -0,0 +1,123 @@
#### 基本格式
* 第1行总是以`#!/bin/sh`开始通知shell使用何种解释器。
* 以`#`开头的行是注释,建议在第二行注释中写入脚本名。
* 脚本按从上到下的顺序执行。
#### 保留字和元字符
```
do, done, for, while
if, then, else
```
```
.. # 上级目录
. # 当前目录
* # 任意多个字符
? # 任意一个字符
~ # 用户目录
$ # 变量参数
# # 注释
| # 管道
& # 后台执行
&& # 左边的命令返回真后,右边的命令才能执行
|| # 左边的命令执行错误,则执行右边的命令
() # 在当前shell执行括号内的一组命令
{} # 在子shell执行括号内的一组命令
[...] # 匹配括号内的任意一个字符
[!...] # 不包含括号内的所有字符
> # 输出重定向
< #
>> # 追加
```
#### 条件控制
1. if-then-else
```
if 条件1
then 命令1 //如果条件1为真则执行命令1
elif 条件2
then 命令2 //如果条件1为假且条件2为真则执行命令2
else 命令3 //如果条件1为假且条件2为假则执行命令3
fi //fi意为if语句终止
```
其中elif和else为可选项。
2. case
```
case 值 in
模式1) 命令1
;;
模式2) 命令2
;;
...
*) 命令n
;;
esac
```
将值和每一个模式匹配,若匹配则执行相应命令不再与其它模式匹配,否则执行星号后的命令。
#### 循环
1. for
```
for 变量名 in 列表
do
命令1
命令2
...
done
```
2. until
```
until 条件
命令1
...
done
```
当条件为真时循环结束,注意测试发生在**循环末尾**。
3. while
```
while 条件
do
命令1
...
done
```
4. break
跳出循环或case语句
5. continue
跳过当前循环
#### shell变量
##### 本地变量
- 仅对当前进程有效
##### 环境变量
- 对所有进程有效
- export导出环境变量
- env显示环境变量
- unset清除环境变量
##### 位置变量
- 用$+数字表示传递给脚本的参数
特定变量
```
!$ # 上条命令的最后一个参数
```
`$#, $*, $$, $!, $@, $-, $?`

161
Languages/LinuxShell/awk.md Normal file
View File

@ -0,0 +1,161 @@
#### 简介
awk是一种语言用于处理文本文件。这个名字来源于三个创始人的首字母。
#### 语法
```bash
awk [options] -f <script-file> [--] file //
awk [options] [--] <script> file //
```
#### 选项
```bash
-f, --file # 指定要执行命令的脚本
-F, --field-separator # 指定域的分割符(即预定义变量FS的值)
```
#### AWK程序执行
```aw
@include "filename"
@load "filename"
pattern {action statement}
function name(parameter list) {statements}
```
#### 变量、记录和域
##### 记录
一般来说,一行就是一条记录。但也可以通过修改变量`RS`来决定划分记录的方式。
##### 域
记录再分割就是域,`FS`是域的划分规则。
\$1, \$2等代表了域的编号而\$0则代表了记录本身。
变量`NF`代表了一个记录里域的数量。
##### 内建变量
FILENAME : 当前输入文件的名字。
FS : 输入域的分割符,默认为空格。
NF : 当前输入记录里域的数量。
NR : 当前处理的是第几条记录。
OFMT : 数字的输出格式,默认为`%.6g`。
OFS : 输出域的分隔符,默认为空格。
ORS : 输出记录的分隔符,默认为换行符。
RS : 输入记录的分隔符,默认为换行符。
#### 模式和活动
模式在前,活动在后。
##### 模式
```
BEGIN
END
BEGINFILE
ENDFILE
/正则表达式/ # 只有满足正则表达式的输入记录才会被执行
关联表达式
模式 && 模式 # 逻辑与
模式 || 模式 # 逻辑或
模式 ? 模式 : 模式 # 第1个模式为真则使用第2个模式,第1个模式为假则使用第3个模式
(模式)
! 模式 # 逻辑非
模式1, 模式2 # 从满足模式1的记录开始,到满足模式2的记录结束
```
##### 正则表达式
##### 活动
活动是花括号之间的语句`{statements}`。
##### 操作符
##### 控制语句
```
if (condition) statement [else statement]
```
##### I/O语句
```
print # 打印当前记录
print expr-list # 打印表达式expr-list
print expr-list > file # 将打印出来的表达式重定向到文件
```
##### printf语句
##### 特殊文件名
##### 数字函数
```
cos(expr) # 余弦expr是弧度值
rand() # 返回一个0到1之间的随机数
sin(expr) # 正弦expr是弧度值
sqrt(expr) # 平方根
```
##### 字符串函数
```
length(str) # 返回字符串的长度
tolower(str) # 字符转化为小写
toupper(str) # 字符转化为大写
substr(s,i,[,n]) # 返回从第i的字符开始最多n个字符的子串。如果不给定n则返回字符串全部剩余的部分
```
##### 时间函数
##### 位处理函数
##### 国际化函数
#### 用户定义的函数
#### 动态加载新函数
#### 信号
#### 国际化
#### POSIX兼容
#### GNU扩展
#### 环境变量
#### 退出状态
#### 例子
```bash
awk '{print $0}' demo.txt # 打印demo.txt的内容
```

View File

@ -0,0 +1,65 @@
#### 简介
#### 选项
#### 参数
#### 调用
#### 定义
#### 保留字
#### 语法
#### 注释
#### 引用
#### 参数
#### 扩展
#### 重定向
#### 别名
#### 函数
#### 算术求值
#### 条件表达式
#### 简单命令扩展
#### 命令执行
#### 命令执行环境
#### 环境
#### 退出状态
#### 信号
#### 作业控制
#### READLINE
#### shell内建命令
```
pushd [dir] # 指定目录栈的栈顶,并切换到栈顶所指的目录;如果不加任何参数,则交换最顶端两个参数的位置。
# -n参数只将目录加入栈中而不切换目录
# +N则会把第N个目录加入栈顶(从左边数起从0开始)
# -N则会把第N个目录加入栈顶从右边数起从0开始
# dir参数用于将dir加到栈顶并切换到dir
popd # 删除目录栈中的某个目录;如果不加任何参数,则删除栈顶的目录,并切换到新栈顶所指的目录。
# -n参数只删除栈顶的目录而不切换目录
# +N则会删除第N个目录(从左边数起从0开始)
# -N则会删除第N个目录(从右边数起从0开始)
```
#### 受限的shell

View File

@ -0,0 +1,6 @@
```shell
md5sum
sh1sum
sha256sum
sha512sum
```

View File

@ -0,0 +1 @@
chgrp //更改文件所属群组

Some files were not shown because too many files have changed in this diff Show More