写一个简单的服务器 这里我们在运行服务器后 在新窗口中测试 写入一个hello 然后服务器中输出了hello 并回了一个大写的HELLO。实现一个服务器 客户端发送一个小写的hello给服务器 服务器返回一个大写的HELLO。接下来先用nc指令测试 这里我们使用127.0.0.1 本地ip测试。1.打开用vim创建一个server.c文件 开始编写服务器代码。
RSIC-V指令及介绍(1) add rd, rs1, x0 # rd = rs1 + x0 x0代表零号寄存器 只读 代表0 这个语句就是把rs1赋给rd。s0 - s11 对应的编号x8 - x9 和 x18 - x27的寄存器用来作为保存寄存器,保存原进程中的关键数据避免在函数调用过程中被破环。addi rd, rs1, 10 # rd = rs1 + 10 针对寄存器和立即数的加法操作。
编译器-最优寄存器分配 而全局寄存器分配是刚好相反的 在comile time需要跟多的时间 但是performance会表现很好 注意全局寄存器分配不需要像局部分配一样在每一个block结束以后需要把所有的def和use都store 然后在新的block中又把需要的所有def和use进行load 全局分配需要通过conlict graph算法求出以插入最少的store和load为代价使得代码能流畅的作为一个整体运行下去。指需要在最左边的block中def b后面插入一个store b 在use b之前插入一个load b即可。.
编译器-条件/循环代码生成 BB代表的basic block 这里遇到分支branch 或 jump 或 一个新的label 都是一个basic block。在得到AST之后 编译器会构建这样的一个CFG作为一部分中间表达 每一个baisc block里面有一些线性的汇编指令。在优化代码的时候 划分成这些basic block会使逻辑更加清晰 使得优化工作更加简单。这里的com_LT是比较less than 如果r1比r2小 返回true到r3。cbr是condition branch 如果r3是true 走L1 否则走L2。...
编译器-基于AST表达式代码生成 执行完t2 = Expr(node.right);接下来return后 就回到了乘号Op的t1 = Expr(node.left);接下来执行下一行t2 = Expr(node.right);已经执行完毕 return r3 开始执行t2 = Expr(node.right);首先第一步call root 加号+ 调用Expr函数 push进栈中 +是Op 所以会call t1 = Expr(node.left);然后再次进入到Op中的t1 = Expr(node.left);...
编译器-语法制导翻译STD GNU Bison 是一个通用的解析器生成器,它可以将注释的无上下文语法转换为使用 LALR (1) 解析表的确定性 LR 或广义 LR (GLR) 解析器。一旦您熟练使用 Bison,您可以使用它开发广泛的语言解析器,从简单的桌面计算器中使用的解析器到复杂的编程语言。最后Term作为$1 mult作为$2 Factor作为$3 它们的cost分别为 4 3 1 加起来是8 传给上面的$$也就是说 每次在LR(1)文法中执行reduce 7的时候 执行的其实是$$ = $1来生成AST。...
编译器-LR(1) 这里其实是无法明确置换的 比如Expr+Term*Factor可以reduce 4 但同时也可以reduce 6形成Expr+Term*Term 也是合规的 这就说明虽然我们通过表达式逻辑可以很容易的做出选择 但是系统没有一个明确的算法去确定合适的选择。但其实在这个例子中就能发现 一旦shift读取一个字符到栈中 只要有合理的置换方式 就需要一直置换下去 直到没有任何一个可以置换且遵循follow的时候 才会继续去shift读取下一个字符到栈中。发现d可以置换成B且不违背follow集 所以形成了aAB。.
编译器-LL(1)-First集与Follow集 first集合非常简单 但是要注意follow集合 主要是先看语法箭头右边的内容找到需要判断的符号类型所在位置 如果在行尾 那么就包含箭头左边符号类型的所有follow集合 其次如果不在行尾 就包含后面紧跟着的符号类型的first集 但是要注意的是如果紧跟着的符号类型在first集可以为∈ 那么又要包含箭头左边的符号类型的follow集。//同时第三行Term ‘可以为∈ 那么说明Factor可以作为Term的最后出现 所以要包含Follow(Term)第三行:Term → Factor Term '..
编译器-LL1-parsing tree左右递归 第八步:此时读取的是乘号* Term '有三个选择 Term ' → * Factor Term ' | / Factor Term ' | ∈ 刚好对应上了* 而且Term作为一个项也是不允许把*作为结尾的 不能选空 所以构建第一个 并开始读取下一个字符y。第四步:此时读取的是加号+ 而Term '中有三个选择Term ' → * Factor Term ' | / Factor Term ' | ∈ 然而即不是*也不是/ 所以只能选择∈。接下来用转换好的右递归来parse x + 5 * y。.
编译器-上下文无关语法 在计算机高级语言中 我们把变量名(identifier) 这一个类作为计算机语言表中的一个symbol 在scanner(词法分析)的过程中 无疑就是传入了一个字符串 返回一个类别 把各种类别组合在一起 形成了context-free language 然后用特定的grammar语法定义去判断这一堆类别组合是不是符合所规定的语法 如果不合规就会出现编译错误。这时在数学中(a + b)是一个factor c也是一个factor (a + b) * c是一个term。......
编译器-NFA转DFA d1是E-closure of q1 即为集合{q1,q2,q3,q4,q6,q9} 这个集合中没有任何一个state走的通a 所以为d空 走b的话集合中q4可以走到E-closure of q5 是个集合需要设为d2 同理走c的话q6可以走到E-closure of q7 是个集合 需要设为d3。d3是E-closure of q7 走a走不通 设为d空 走b的话q4可以走到E-closure of q5 就是d2 走c的话q6可以走到E-closure of q7 即为自己d3本身。.........
编译器-正则表达式转NFA 2.Concatenation 串联、链接的意思。Regular operation (正则操作符)2.Concatenation在NFA中的体现。1.Union 或的意思。3.Star 重复的意思。a (b U c)* 的 NFA。3.Star在NFA中的体现。(b U c)* 的NFA。b U c 的NFA。...
编译器-有限自动机和正则表达式 编译器的词法分析是基于finite automata(有穷自动机) 和 regular expression(正则表达式)alphabet:finite set of symbols 字母表:有限符号的集合string:sequence of symbols from a given alphabet 字符串:给定字母表的符号序列language:set of string 字符串的集合例如x12 = 32;中有token;......
编译器概述-指令调度的概念 假设每个指令为了得到结果都有一个周期load为3周期mult为2周期div为5周期store为2周期add为1周期注意:这里的周期可以理解为占用的cpu运算资源时间r1r2r3r4r5r6r7x可以看到 第一个load从周期1开始到3经历了1 2 3 三个周期第二个load不依赖于第一个load 所以不需要等待第一个load执行完毕 就可以直接开始执行第三个mult需要依赖于r1和r2的数据 所以必须等上面都执行完毕 再从周期5开始执行。...
编译器概述-寄存器分配的概念 可以看出 当只有两个物理寄存器的时候 不能把每一个虚拟寄存器都映射到物理寄存器上 而是需要临时存入内存 这种情况英文中叫spill 就是溢出到内存中的意思 register allocation会尽可能的不去发生spill的情况。其次这里发现r1和r2都存储着真实的变量(variable)a和b 而r3却是一个中转的临时变量(temporaries) 实际中的汇编是没有变量这个概念的 实际中只有寄存器和内存地址。//第一个行抽象汇编指令实际是第二行的简化 第二行才是抽象汇编指令的完整写法。......
编译器概述-结构和主要组件 并不是说所有的编译器优化都能保证提高代码速度 只能说优化是有可能提高代码速度 例如上面那个优化就会使得代码在cpu中运行的负担加重 因为t其实只是维护了两个变量的相加 但却要多占用一个寄存器的位置 到不如直接算两遍a+b了 如果t中维护了很多变量 显然这个优化是有用的 所以编译器中的优化无疑不是绝对意义上的智能优化。虽然说看上去优化的这个过程和cpu内部设计是无关的 但是其实这也不是绝对的 寄存器的数量 cpu内部的设计逻辑其实也会影响到优化设计的过程。看一个例子 x = a*b + c*d;...
进程-信号的基本概念和产生函数 信号是Linux进程间通信的最古老方式。信号是软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。这里我们学习的信号就是属于这么一种中断。我们在终端上敲Ctrl + c就产生了一个中断,相当于产生了一个信号,接着就会处理这个一个中断任务信号的特点:1.简单2.不能携带大量信息3.满足某个特设条件才能发送。...
进程-共享存储映射 其实linux系统给我们提供了创建匿名映射区的方法,无需依赖一个文件即可创建映射区。共享内存可以说是最有用的进程间通信方式,也是最快的PC形式,因为进程可以直接读写内存,而不需要任何数据的拷贝。offset:以文件开始处的偏移量,必须是4k的整数倍,通常为0,表示从文件头开始映射。addr:指定映射的起始地址,通常设为NULL,由系统指定。fd:由open返回的文件描述符,代表要映射的文件。存储映射I/O使一个词磁盘文件与存储空间中的一个缓冲区相映射。参数:addr:使用mmap函数创建的映射区的首地址。.
进程-exec/僵尸和孤儿进程/wait/管道 任何一个进程的全局变量在另一个进程中都看不到,所以进程和进程之间不能相互访问,要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。孤儿进程父进程先于子进程结束则子进程成为孤儿进程,子进程的父进程成为init进程,称为init进程领养孤儿进程。成功清理掉的子进程id失败-1(没有子进程)僵尸进程进程终止,父进程尚未回收,子进程残留资源(PCB)存放于内核中,变成僵尸进程。...
进程-CPU和MMU/环境变量/创建子进程 Linux操作系统是一个多任务多用户的开源操作系统多任务并发多用户同一时间点了可以有多个用户登陆到一台计算上环境变量是指在操作系统中用来指定操作系统运行环境的一些参数通常有以下特征1.字符串(本质)2.有统一的格式名=值[值]3.值用来描述进程环境信息PATH用来记录文件的可执行路径echo$PATHSHELL记录当前的命令解析器是什么当前Shell它的值通常是/bin/bashTERM。......