当前位置: 首页 > news >正文

cms免费企业网站企业推广视频

cms免费企业网站,企业推广视频,沈阳在线制作网站,seo服务合同Github代码仓库链接 0.1 RISC-V硬件机制 1 概述和特权级 RISC-V#xff08;发音为“risk-five”#xff09;是一个基于精简指令集#xff08;RISC#xff09;原则的开源指令集架构#xff08;ISA#xff09;#xff0c;简易解释为开源软件运动相对应的一种“开源硬件”…Github代码仓库链接 0.1 RISC-V硬件机制 1 概述和特权级 RISC-V发音为“risk-five”是一个基于精简指令集RISC原则的开源指令集架构ISA简易解释为开源软件运动相对应的一种“开源硬件”。 指令集架构Instruction Set Architecture简称ISA指的是计算机中处理器的指令集和处理器的内部结构即处理器是如何执行指令的。 1、RISC-V 是一个典型的 Load-Store 类型的指令集架构这意味着几乎所有的指令的操作数都只能是立即数或者寄存器而不能是某个内存地址。如果想要修改内存某处的值只能通过 Load 指令将某个内存地址处的值加载到寄存器中并在修改完成后通过 Store 指令将其写入内存。 2、RISC-V 采用了模块化设计包括几个可以互相替换的基本指令集, 以及额外可以选择的扩展指令集。本章及其后的内容将基于 RV64I并且不会涉及扩展指令集 3、核心的RISC-V指令集即RV32I或RV64I包含了大约40-50条基本指令这些指令主要用于整数计算、内存访问、分支、比较等基础操作 4、RISC-V 硬件线程hart都运行在某个特权等级下 机器模式M-Mode3 可以用来管理RISC-V上的安全执行环境。所有的硬件实现都必须提供 M-Mode这是唯一可以自由访问整个机器的模式监管者模式S-Mode1用于操作系统。系统实现还可以添加监管者模式S-Mode以隔离监管者级操作系统和监管者执行环境SEE。用户模式U-Mode0用于传统的应用程序。许多 RISC-V 实现还将至少支持用户模式以保护系统的其余部分不受应用程序代码的影响。 等级编码名称简写000用户/应用程序User/ApplicationU101监管者SupervisorS210未定义/311机器MachineM 2 寄存器组 1、通用寄存器 变量 XLEN 指指令集位宽整数调用约定提供了 8 个参数寄存器 a0 ~ a7前两个寄存器也用于保存返回值单个参数寄存器中最多存储 XLEN 位宽的数据如果没有可用的寄存器那么参数就会被保存在栈上当将参数存储到寄存器或是栈上时小于 XLEN 位的整数会根据其类型扩展到 32 位接着再符号扩展到 XLEN 位单个长度为 2*XLEN 参数会存储在一对参数寄存器中其中低 XLEN 位存储在编号较小的寄存器中高 XLEN 位存储在编号较大的寄存器。。如果没有可用的寄存器那么参数就会被保存在栈上如果恰好只有一个寄存器可用那么低 XLEN 位会保存在寄存器上而高 XLEN 位会保存在栈上。宽度超过 2*XLEN 位的参数会以引用的方式传递当一个聚合体如数组或者结构体等作为一个参数传递时用于对齐填充的位以及超过聚合体的末尾且其大小不能被 XLEN 整除的位其行为是未定义的由编译器实现。由栈传递的聚合体或是整数会对齐到类型对齐要求或是 XLEN 中较大的一个但是不会超过栈的对齐要求。位字段以小端序存储。如果某个位字段超出了其整数类型的边界将填充空余的位到下一个对齐边界。 位字段是数据结构中的一部分它允许开发者指定特定的位数来存储数据通常用于优化内存使用。例如如果一个变量只需要几个比特如只需要4位来表示一个状态就可以通过位字段将多个值打包在一起节省内存空间。如 struct { unsigned int a : 3; // 3 bits for a unsigned int b : 5; // 5 bits for b }; // a占用3位b占用5位总共是8位。小端序存储低位字节存储在内存的低地址端高位字节存储在高地址端。如果一个16位的值 0x1234 需要存储在内存中将 0x34 存储在较低的内存地址而 0x12 存储在较高的内存地址。则内存顺序为0x34 0x12说明内存从低往高 填充对齐边界如上述例子若存储在一个32位的unsigned int类型中剩下的24位内存位置会被填充为“空位”通常是0直到下一个对齐边界 可变参数的传递方式和命名参数相同但是由一个例外以 2*XLEN 位对齐且大小最多为 2*XLEN 位的可变参数通过一对参数寄存器传递或者在没有寄存器可用的情况下通过栈传递。当一个可变参数被通过栈传递后所有后续的参数也都会通过栈传递例如最后一个参数寄存器可能由于对齐原因未被使用返回值的传递方式和传递第一个命名参数的方式相同。如果这样的参数是以引用方式传递的则由调用者为返回值分配内存指将其内存地址传递给函数返回并将其地址作为隐式的第一个参数传递。栈向下增长向低地址方向栈顶指针在过程函数入口应当对齐到 128 位16字节边界。 2、控制寄存器标准 RISC-V 指令集使用 12 位编码空间来编码控制寄存器CSR使得指令集最多支持 4096 个控制寄存器。控制寄存器只有特殊的指令才可以读写这些指令都是原子指令。csrrw dst, csr, src 先将 csr 的值写入 dst并将 src 的值写入 csrcsrr dst, csr 将 csr 的值读到 dstcsrw csr, src 将 src 的值写入 csrcsrc(i) csr, rs1 将 csr 中指定的位清零csrc 使用通用寄存器作为 maskcsrci 则使用立即数。csrs(i) csr, rs1 将 csr 中指定的位置 1csrc 使用通用寄存器作为 maskcsrci 则使用立即数。 3 中断机制 1、微观上我们使用异常来表示当前硬件线程中执行代码发生的特殊情况而中断则表示一个外部的异步事件这个事件可能引起控制转移通常与当前执行代码无关。宏观上我们将其统称为中断。 2、mstatus 全局中断使能 mstatus 寄存器记录了当前硬件线程的状态由多个状态位组成在 S-Mode 下以 sstatus 被访问在 U-Mode 下以 ustatus 被访问是同一个寄存器的不同视图高权限级的视图可以访问更多的状态位低权限级低视图只能访问受限的位。MIE、SIE 和 UIE分别是不同权限等级的全局中断使能位。w x y如果 xIE1低特权级 w 的中断会被全局禁用无论是否设置 wIE 位优先高特权级。且高特权级 y 的终端会被全局启用无论是否设置 yIE。所以高权限级代码可以使用单独的中断启用位来禁用选定的高权限模式中断然后将控制权让与低权限模式xPIE 存储了中断处理发生前的中断使能位的值即进入该模式前的中断使能值xPP 存储了上一个特权模式比如特权模式 y 进入 x则 xPP y。所以 MPP 是两位宽表示进入机器模式之前的特权级别存储监管者和用户SPP 是一位宽用户嵌套中断处理处理中断时来一个更高优先级的中断。所以需要两套中断使能位才能正确恢复。 当发生中断时系统会根据当前的特权模式 y如监管模式进入另一个特权模式 x如机器模式来处理该中断。 xPIE 被设置为 xIE 的值进入中断处理时系统会将当前中断使能位 xIE 的值表示当前中断使能的状态保存到 xPIE 中。xIE 被设置为 0然后系统会将 xIE 设置为 0禁用中断防止发生其他中断确保当前中断能够被完全处理。xPP 被设置为 y接着系统会将 xPP 设置为当前的特权模式 y表示在进入 x 模式之前系统处于 y 模式。这允许中断处理完后可以恢复到 y 模式。 中断处理完成后恢复当中断处理完成时系统会使用保存的 xPIE 和 xPP 的值来恢复 恢复中断使能位恢复中断使能位 xIE 为 xPIE恢复进入该模式之前的中断使能状态。恢复特权模式恢复特权模式为 xPP即返回到上一个特权模式y 模式。指令 MRET、SRET 和 URET 分别用于从 M-Mode、S-Mode 和 U-Mode 的中断处理中返回。xPP 字段是 WARLWrite any values, read legal values字段只能保存低于或等于 x 的特权模式如果系统没有实现 x 模式xPP 必须被硬连接到 0。 2、具体类型中断使能 mip 存储了未决中断pending interrupts的相关信息未决中断是已经发生、但没有被处理的中断mie 存储了具体类型的中断使能信息指示特定中断是否被允许发生MTIP、STIP、UTIP 位分别用于标识特定模式M-Mode、S-Mode、U-Mode下是否发生时钟中断的标志位。MTIP 位是只读的只能通过写入内存映射的 M-Mode 时钟比较寄存器来清除。UTIP 和 STIP 位可以被 M-Mode 代码修改用来设置低权限级的时钟中断。不同权限模式的多个并发中断按照权限模式递减的顺序进行处理同一个特权模式下的多个并发中断按照以下优先级递减顺序处理MEI、MSI、MTI、SEI、SSI、STI、UEI、USI、UTI。EI-外部中断SI-软件中断TI-定时器中断异步中断是由外部事件触发的中断同步异常是由程序内部状态或执行错误触发的异常同步异常的优先级低于所有异步中断。 3、中断处理程序mtvec 寄存器配置控制转移过程中断函数入口该寄存器由一个向量基地址BASE和向量模式MODE组成 ![[Pasted image 20250223111340.png]] 4、中断信息mcauseMachine Cause Register当控制转移到 M-Mode 中时寄存器会被自动填入代表中断类型的代码 同步异常优先级下面的优先级表决定了 mcause 展示哪个异常 mepcMachine Exception Program Counter 寄存器会被自动填入被异步中断打断的指令地址或者导致同步异常的指令地址中断返回地址mtvalMachine Trap Value当控制转移到 M-Mode 中时寄存器会被自动填入 0 或是异常特有的信息来辅助处理程序处理。 硬件断点当程序执行时遇到预设的硬件断点时mtval 会存储故障时的虚拟地址。地址未对齐当发生地址未对齐异常例如对不满足对齐要求的内存地址进行 Load 或 Store 操作时mtval 会存储出错的虚拟地址。访问故障Access Fault当访问的地址无效例如非法地址或不允许访问的内存区域mtval 会存储故障的虚拟地址。页故障Page Fault当发生页故障时例如试图访问未映射的页或页权限不足mtval 存储的是导致页故障的虚拟地址 5、中断相关指令 ECALL 指令用于向支持执行环境发出请求。在 U-Mode、S-Mode 和 M-Mode 下执行该指令会分别生成 environment-call-from-U-mode 异常、environment-call-from-S-mode 异常和 environment-call-from-M-mode 异常。(ECALL 指令会导致被请求的特权模式的 epc 寄存器被设置为 ECALL 指令本身的地址而不是下一条指令的地址)中断返回每个特权模式都各有中断处理返回指令MRET、SRET 和 URET。除了 mstatus 一节中描述都操作以外该条指令还会将 pc 寄存器设置为 xepc 的值。等待中断WFI 指令会暂停当前硬件线程直到一个异步中断到来 4 内存管理 1、satpSupervisor Address Translation and Protection寄存器控制了 S-Mode 下的地址翻译和保护。 该寄存器的 PPN 字段物理页号保存着根页表的物理页号ASID 字段地址空间标识符是可选的用来降低上下文切换的开销在实现进程时需要使用该字段MODE 字段用来开启分页并选择分页系统。[[Pasted image 20250223124745.png]] MODE名称描述0Bare不开启分页1Sv32基于页的32位虚拟地址系统8Sv39基于页的39位虚拟地址系统9Sv48基于页的48位虚拟地址系统 对于 RV32 来说开启分页的模式只有 Sv32。而 RV64 可以选用 Sv39 和 Sv48在 Sv39 中物理地址有 56 位而虚拟地址有 64 位。虽然虚拟地址有这么多位但是其中只有低 39 位有效第 63 ~ 39 位的值必须等于第 38 位。 页表项 PTEPage table entry V 为 Valid表示这个页表项是否生效。X、W、R 分别是 Execute、Writable 和 Readable表示这个页表项所代表的物理页是否可执行、可写或可读如果这三个位都置为 0表示这个页表项并不是指向最终的物理页而是指向下一级页表。U 为 User表示 U-Mode 下的程序是否可以通过该页表项进行地址映射。如果 U 置为 0那么 U-Mode 下的程序无法使用该页表项。注意如果 U 置为 1 时只有 sstatus 寄存器的 SUM 位也置为 1S-Mode 下的程序才可以访问该页表项否则访问会出现错误。G 为 Global表示该页表项在所有地址空间都有效通常我们不使用这个标记。A 为 Accessed表示自从上次该位被置 0 后是否有程序访问这个页表项访问包括读、写或取指。D 为 Dirty用于在虚拟内存置换时标记脏页。RSW 位暂时保留留给 S-Mode 程序使用。 三级页表和二级页表的每个页表项的 X、W 和 R 位都要置为 0表示该条页表项指向下一级页表而非最终的物理页。 在修改 satp 寄存器后需要通过执行 SFENCE.VMA 指令来刷新相关的地址翻译缓存如 TLB中的值以防止出现未定义行为。 0.2 RISV-V汇编 1 RISC-V汇编 1、RISC-V寄存器 以 t 开头的寄存器表示临时寄存器可以被用于任何用途以 a 开头的寄存器用于向函数传递参数以 s 开头的寄存器sp 除外将在过程调用过程中不被修改。 2、RISC-V 指令集包含了整数指令、逻辑指令以及一些内存指令 指令示例描述lb t0, 8(sp)将内存地址 sp8 处的值Jokerix加载Jokerix解引用到寄存器 t0 中。lb 加载一个字节Jokerixlh 加载半字lw 加载一个字ld 加载双字Jokerixsb t0, 8(sp)将寄存器 t0 的值Jokerix存储Jokerix解引用到内存地址 sp 8 处sb 存储一个字节sh 存储半字sw 存储一个字sd 存储双字add a0, t0, t1将 t0 和 t1 的值相加结果存储在 a0 中addi a0, t0, -10将 t0 的值与 -10 相加结果存储在 a0 中(Jokerix用于寄存器和立即数相加Jokerixi-immediate)sub a0, t0, t1t0 的值减去 t1 的值结果存储在 a0 中mul a0, t0, t1将 t0 的值与 t1 的值相乘结果存储在 a0 中div a1, s3, t3s3 的值除以 t3 的值Jokerix结果Jokerix存储在 a1 中rem a1, s3, t3s3 的值除以 t3 的值Jokerix余数Jokerix存储在 a1 中and a3, t3, s3将 t3 和 s3 的值做与运算结果存储在 a3 中or a3, t3, s3将 t3 和 s3 的值做或运算结果存储在 a3 中xor a3, t3, s3将 t3 和 s3 的值做异或运算结果存储在 a3 中3、伪指令由汇编器转换为真正存在的指令 4、分支指令有三个参数要比较的两个操作数寄存器如果比较为真则是要执行的指令的内存标签 beq - Branch if Equal 如果两个寄存器的值Jokerix相等Jokerix则跳转。bne - Branch if Not Equal 如果两个寄存器的值Jokerix不相等Jokerix则跳转。bgt - Branch if Greater Than 如果第一个寄存器的值Jokerix大于Jokerix第二个寄存器的值则跳转。bge - Branch if Greater or Equal 如果第一个寄存器的值Jokerix大于等于Jokerix第二个寄存器的值则跳转。blt - Branch if Less Than 如果第一个寄存器的值Jokerix小于Jokerix第二个寄存器的值则跳转。ble - Branch if Less or Equal 如果第一个寄存器的值Jokerix小于等于Jokerix第二个寄存器的值则跳转。 例 bge t0, t2, loop_end // t0 t2 则跳转到 loop_end5、栈 从栈上分配空间我们需要将 sp 减去一个值。从栈上回收空间我们需要将 sp 加上一个值。注意我们没有“清理”栈空间这就是为什么 JokerixC 语言中不能直接使用未初始化的局部变量Jokerix。Jokerix栈必须对齐到 8 字节边界这意味着我们只能分配或回收 8 字节倍数的内存Jokerix。 addi sp, sp, -8 // 分配栈空间 sd ra, 0(sp) // 保存返回地址因为printf函数可能修改ra call printf // 调用函数 ld ra, 0(sp) // 加载保存的返回地址 addi sp, sp, 8 // 释放栈空间 ret // 此时可以用原来的ra来回到调用当前函数整段代码的地址6、c 语言到汇编的转换 编译器的工作是将 .c 文件转换成汇编文件汇编器则是将汇编文件作为目标文件汇编成机器码链接器就会将所有的目标文件链接到一个可执行文件 7、函数汇编应用程序二进制接口ABI指定哪些寄存器获得哪些参数以及如何来回返回内容所有的函数包含两部分开场白Prologue为本地存储设置一个栈帧结语Epilogue通常需要加载保存的寄存器和返回地址并在返回之前移动栈指针 my_function:# Prologueaddi sp, sp, -32 // 分配32字节栈空间sd ra, 0(sp) // 存储调用者的寄存器值sd a0, 8(sp)sd s0, 16(sp)sd s1, 24(sp)# Epilogueld ra, 0(sp) // 还原调用者寄存器ld a0, 8(sp)ld s0, 16(sp)ld s1, 24(sp)addi sp, sp, 32ret8、调用 printf 函数任何时候看到函数调用都应该考虑保存返回地址寄存器 .section .rodata prompt: .asciz Value of t0 %ld and value of t1 %ld\n .section .text myfunc:// 分配栈空间存放返回地址addi sp, sp, -8 // 分配栈空间sd ra, 0(sp) // 存放返回地址// 传递参数、调用la a0, prompt // 将 printf 的第一个参数字符串放入 a0 mv a1, t0 // 将想输出的 t0 放入 a1mv a2, t1 // 将想输出的 t1 放入 a2call printf// 还原返回地址为了让 myfunc() 函数可以正确返回ld ra, 0(sp) // 加载保存的返回地址addi sp, sp, 8 // 释放栈空间ret // 此时可以用原来的ra来回到调用当前函数整段代码的地址9、应用程序二进制接口ABIApplication Binary Interface 8 个参数寄存器 a0 到 a7这将是传递给函数的 8 个非浮点数参数。参数可能是指针这种情况下 aX 将包含一个内存地址或者直接传递数值这时 aX 将包含实际的值。ABI 进一步要求我们必须通过 Jokerixa0 返回一个整数值作为返回值Jokerix。 2 C语言内联汇编 1、基本内联格式为asm(汇编代码) asm (movl %ecx %eax); /* 将 ecx 寄存器的内容移至 eax */ asm (movb %bh (%eax)); /* 将 bh 的一个字节数据 移至 eax 寄存器指向的内存 */asm 和 __asm__ 这两个关键字都是有效的设立两个关键字主要是为了防止标识符冲突。如果汇编指令多于一条可以每个指令一行并用双Jokerix引号圈起Jokerix同时为Jokerix每条指令添加 ‘\n’ 和 ‘\t’ 后缀Jokerix。gcc 会通过使用换行符或制表符发送正确格式化后的行给汇编器。 asm (movl %eax, %ebx\n\tmovl $56, %esi\n\tmovl %ecx, $label(%edx,%ebx,$4)\n\tmovb %ah, (%ebx));2、拓展汇编可以同时指定操作数可以指定Jokerix输入寄存器、输出寄存器以及修饰寄存器列表Jokerix。扩展汇编基本格式为 asm ( 汇编语句 : 输出操作数 /* 可选的 */ : 输入操作数 /* 可选的 */ : 修饰寄存器列表(会被修改的寄存器) /* 可选的 */ );输出寄存器表示嵌入汇编执行完后哪些寄存器用于Jokerix存放输出数据Jokerix输入寄存器表示开始执行汇编代码时指定的一些寄存器Jokerix存放输入值Jokerix修饰寄存器列表表示已经对列出的寄存器Jokerix值进行改动Jokerixgcc编译器不能再依赖于原先这些寄存器所加载的值。不用在这个列表里列出输入、输出寄存器因为它们被显式地指定了约束GCC 可以推断 asm 使用了它们。 如果指令隐式或显式地使用了任何除此之外的其他寄存器那么就需要在修饰寄存器列表中指定这些寄存器 asm (cld\n\trep\n\tstosl: /* 无输出 */:c(count), a(fill_value), D(dest) /* 输入列表 */:%ecx, %edi /* 修饰寄存器列表 */);以上的内联汇编是将 fill_value 的值连续 count 次拷贝到寄存器 edi 所指位置每执行 stosl 一次寄存器eax值会拷贝到寄存器 edi指向的位置计数值ecx递减edi 的值会递增或递减这取决于是否设置了 direction 标志因此以上代码实则Jokerix初始化一个内存块Jokerix。 它也告诉 GCC 寄存器 ecx 和 edi 一直无效。如果指令会修改Jokerix条件码寄存器JokerixCondition Code Register又称状态寄存器或标志寄存器则必须将 %cc 添加进修饰寄存器列表。如果我们的指令以不可预测的方式修改了内存那么需要将 memory 添加进修饰寄存器列表。 这可以使 GCC 不会在汇编指令间保持缓存于寄存器的内存值。如果被影响的内存不在汇编的输入或输出列表中我们也必须添加 volatile 关键词。 3、volatile如果汇编语句必须Jokerix在放置它的地方执行Jokerix例如不能为了优化而被移出循环语句使用volatile 以避免编译器不可预知的优化防止它被移动、删除或者其他操作。内核源码经常会有这种写法。 asm volatile ( 汇编程序模板: 输出操作数 /* 可选的 */: 输入操作数 /* 可选的 */: 修饰寄存器列表 /* 可选的 */);4、常用约束 寄存器操作数约束r变量 myval 保存在寄存器中寄存器 eax 的值被复制到该寄存器中然后 myval 的值从寄存器更新到了内存。 rRegister(s)a%eax %ax %alb%ebx %bx %blc%ecx %cx %cld%edx %dx %dlS%esi %siD%edx %di asm (movl %%eax, %0\n :r(myval)); // 将寄存器eax保存在任一寄存器r中再将该寄存器值输出到myval内存操作数约束m约束允许一个内存操作数可以使用机器普遍支持的任一种地址。 asm (sidt %0\n : :m(loc)); // 这里 loc 是一个变量通常是一个内存缓冲区其数据类型是指向某个内存区域的指针用来存储 sidt 的结果 // loc是一个内存操作数slit将中断描述符表的基址存储到loc这个内存地址上匹配数字约束在某些情况下一个变量可能既充当输入操作数又充当输出操作数。 asm (incl %0 :a(var):0(var)); // 0 用于指定与第 0 个输出变量相同的约束 // 寄存器 %eax 既用作输入变量也用作输出变量。 // var 输入被读进 %eax并且等递增后更新的 %eax 再次被存储进 var其他通用约束 o 约束允许一个内存操作数但只有当地址是可偏移的时。即该地址加上一个小的偏移量可以得到一个有效地址V 约束一个不允许偏移的内存操作数。换言之任何适合 “m” 约束而不适合 “o” 约束的操作数i 约束允许一个带有常量的立即整形操作数这包括其值仅在汇编时期知道的符号常量n 约束允许一个带有已知数字的立即整形操作数。许多系统不支持汇编时期的常量因为操作数少于一个字宽。对于此种操作数约束应该使用 n 而不是 ig 约束允许任一寄存器、内存或者立即整形操作数不包括通用寄存器之外的寄存器 约束修饰符 约束修饰符意味着对于这条指令操作数为只写的旧值会被忽略并被输出数据所替换。即意味着这是一个输出寄存器 约束修饰符 0.3 SBI规范 本章将对 SBISupervisor Binary Interface监管者模式二进制接口规范进行简要介绍。图文主要来自 RISC-V 官方的 (SBI 文档)[https://github.com/riscv/riscv-sbi-doc]。 RISC-V SBI 规范描述了 RISC-V 监管者模式二进制接口SBI的实现规范。SBI 通过定义平台或虚拟机管理器特定功能的抽象Jokerix来允许监管者模式S-Mode 或 VS-Mode软件可以在所有 RISC-V 平台进行移植Jokerix。SBI 的设计遵循了RISC-V 的设计哲学拥有一个小的核心和一组可选的模块扩展。 Jokerix理解Jokerix应该是设计监管者模式和 RISC-V 底层核心机器模式进行交互的接口需要遵循一些规范使得监管者模式软件能够在 所有 RISC-V 平台 上运行SBI 的主要目的是为监管者模式软件提供一个统一的接口这样上层的软件如操作系统、虚拟机监控器等可以在不同的硬件平台上执行。通过这种方式SBI 能够解决不同 RISC-V 平台在硬件实现上的差异。 1、二进制编码 SBI 调用与标准 RISC-V 函数调用完全相同除了 使用 ECALL 指令作为控制流程转移指令而不是 CALL 指令。a7 寄存器编码了 SBI 扩展 ID (EID)它与 Linux 系统调用 ABI 中系统调用号的编码方式相匹配。 JokerixSBI 函数必须返回一对值Jokerix存储在 a0 和 a1 寄存器中其中 a0 是一个错误代码。这类似于 C 结构体 struct sbiret {long error;long value; };SBI 错误代码编码如下 错误类型值SBI_SUCCESS0SBI_ERR_FAILED-1SBI_ERR_NOT_SUPPORTED-2SBI_ERR_INVALID_PARAM-3SBI_ERR_DENIED-4SBI_ERR_INVALID_ADDRESS-5SBI_ERR_ALREADY_AVAILABLE-6 不带 H 扩展的 RISC-V 系统 带 H 扩展的 RISC-V 系统 0.4 GDB调试 GDB即 GNU Project Debugger允许你在程序运行时检查内存和寄存器中的内容。JokerixLinux开发调试工具Jokerix 1 GNU调试 由于我们的操作系统是运行在虚拟机中的所以调试的方式与调试普通程序有略微不同。我们需要借助远程调试的方式将虚拟机当作远程机器用本地 gdb 进行连接。 运行提前在 Makefile 中写入代码来启动qemu make qemu-gdbqemu会暂停启动等待 gdb 连接。这时可以开启另一个终端启动一个 gdb 来连接。 $ gdb-multiarch kernel/Kernel (gdb) set architecture riscv:rv64 (gdb) target remote 127.0.0.1:26000 // 手动链接本机qemuu // 26000 是调试端口号在 make qemu-gdb 日志的最下方可以找到该端口号注xv6直接把第二步几条指令写入.gdbinit文件中运行gdb-multiarch -x .gdbinit即可 Jokerix注意Jokerix遇到报错qemu-system-riscv64: -gdb tcp::26000: Failed to find an available port: Address already in use 使用 netstat 或 lsof 命令查看端口 26000 是否被占用lsof -i :26000如果有其他进程占用了该端口可以尝试终止该进程kill PID 官方文档Top (Debugging with GDB) (sourceware.org) 2 断点相关命令 1、break创建断点 break [函数名] 例 b main break [文件名]:[行号] 例 b main.c:4 break *[地址] 例 b *0xffffffff80202bd02、info breakpoints查看断点信息 info breakpoints 例 info b info breakpoints [序号] 例 info b 23、delete删除断点 delete // 删除所有断点 delete [序号] 例 d 24、continue继续运行程序直到遇到断点或错误 continue 例 c 5、step从一个函数跳进另一个函数即持续运行Jokerix直到当前函数运行结束或当前函数调用别的函数Jokerix。 step 例 s6、stepi执行一条机器指令并暂停这个命令会执行一条机器指令。当指令包含一个函数调用时命令就会进入被调用的函数。对于多线程应用stepi 命令会让当前线程执行一条指令同时挂起其他线程。 stepi 例 si7、next单步执行程序但如果遇到函数调用时不会进入该函数。 next 例 n3 输出信息相关指令 1、x用指定格式输出制定内存地址处的内容。 x/[格式控制] [地址表达式] 例 x/wx 0x80000000 x [地址表达式] 例 x 0x80000000 x/[长度][格式控制] [地址表达式]o八进制x十六进制u无符号十进制t二进制f浮点类型a地址类型c字符类型s字符串类型 2、print输出给定表达式的值 Print [表达式] 例print iprint $pcprint *0x800000003、info address输出给定符号变量或函数的地址 info address [符号名] 例 info address main4、info registers输出所有寄存器的内容 info reg / i r / info registers // 输出除控制寄存器CSR和浮点寄存器以外的所有寄存器 info all-registers / i all-registers // 输出所有寄存器 info registers [寄存器名称] // 输出指定寄存器4 GUI客户端——cgdb 左侧的窗口被称为代码窗口右侧为 gdb 窗口。打开 cgdb 时默认两个窗口是上下分隔的可以通过 ctrlw切换成左右分隔模式由于默认的 gdb 只能调试 x86 程序所以我们需要在启动时附带参数来改变默认的 gdb $ cgdb -d gdb-multiarch kernel/Kernel按 esc 键可以将焦点从 gdb 窗口转移到代码窗口在代码窗口可以上下翻看源码空格键可以在焦点行设置一个断点按 i 键可以将焦点从代码窗口转移到 gdb 窗口 Jokerix补充Jokerix B站视频Linux 使用gdb调试入门。哔哩哔哩_bilibili 本地直接进行调试直接通过 gcc -g test.c -o test.out // -g 生成含debug信息的可执行文件 gdb ./test.outxv6Debug XV6_哔哩哔哩_bilibili 操作系统是运行在虚拟机中的所以调试的方式与调试普通程序有略微不同。需要借助远程调试的方式将虚拟机当作远程机器用本地 gdb 进行连接。 // 终端1 make qemu-gdb CPUS1 // 终端2 gdb-multiarch -x .gdbinit layout src // 边显示源码边调试0.5 Jokerix 体系结构 Jokerix 操作系统由两个部分组成监管者模式部分和用户模式部分。 监管者模式部分即Jokerix操作系统内核Jokerix用于对硬件资源进行抽象和调度访问向下沟通位于机器模式的 SBI向上应答用户模式的服务请求。用户模式部分即Jokerix操作系统服务Jokerix目前主要实现的是内核编程接口用户编写的程序不会直接向监管者模式请求服务而是通过调用内核编程接口函数由这些函数代为请求。 1 体系结构 Jokerix 整体采用了宏内核模式宏内核的优点是执行速度快缺点则是层次结构性不强内核代码维护性弱。可以将其大概划分为以下四个模块中断处理模块、内存管理模块、进程调度模块和文件系统模块。 Jokerix中断处理模块Jokerix用于控制操作系统对内外部中断的响应。操作系统通过响应时钟中断来定时检查进程的运行状态可以说中断处理是进程调度的基础。Jokerix内存管理模块Jokerix主要通过虚拟内存管理的方式保证各个进程能够安全共享物理内存互不干扰。同时由于各个进程都运行在独立的虚拟内存空间使得各个程序实现时不必考虑实际的物理内存状态降低了程序实现的难度。Jokerix进程调度模块Jokerix用来控制进程对 CPU 的使用通过 Round-Robin 算法来保证各个进程能够公平地使用 CPU 资源同时内核也能够及时地响应外部中断。Jokerix文件系统模块Jokerix屏蔽了不同文件系统的细节提供了一个通用的文件接口。 2 中断处理模块 1、内部中断 异常Exception在执行一条指令时发生错误这个错误是由这个指令本身引起的这时就需要进入中断来处理错误。有些错误是可以恢复的例如缺页异常中断处理程序将需要的页面调入内存后再次执行导致异常的指令即可。而有些异常不可恢复如除零异常遇到这种异常中断处理程序会直接终止当前程序。陷阱Trap陷阱是由程序主动引发的中断程序通过陷阱机制可以主动向操作系统请求服务。常见的有通过 ecall 指令进行系统调用或者通过 ebreak 指令进入断点。 2、外部中断Interrupt狭义的中断专指外部中断这类中断和当前运行的程序无关是由外部设备引发的。例如时钟中断或外部设备发送数据等。 3、中断处理模块最主要的用途就是通过处理时钟中断来暂时打断当前进程以进行进程调度同时通过处理陷阱来处理来自 U-Mode 下应用程序发起的环境调用。RISC-V 已经规定了中断发生时的 scause 寄存器被设置的值操作系统可以根据这个寄存器中的值来判断中断类型。Jokerix 采取 Direct 模式处理中断这意味着无论发生什么中断Jokerix控制都会跳转到同一个位置Jokerix。 3 内存管理模块 1、Jokerix 操作系统目前只能运行在 QEMU 模拟的 virt 计算机上并且以 OpenSBI 作为 SBI 0x00000000 ~ 0x80000000各种设备内存映射0x80000000 ~ 0x80200000OpenSBI 占据0x80200000 ~ kernel_endJokerix内核空间kernel/kernel.ld 链接脚本定义了一个全局符号 kernel_end标识了内核的结束位置。kernel_end ~ 0x88000000可以任意使用的物理内存0x80000000 ~ 0x88000000这 128 MB 内存是可供操作系统使用的物理内存空间! 2、Jokerix 目前有两种内存分配方式 一种是在上面提到的空闲内存上按Jokerix页Jokerix4 KB进行分配一种是在内核Jokerix堆Jokerix定义在 .bss 段上 8 MB 大小的字节数组上进行动态内存分配 3、内核分页管理Jokerix 采用 RISC-V 提供的 Sv39 分页系统虚拟地址有效位是 39 位这意味着虚拟内存空间共 2^39 字节512 GB远远大于可用的物理内存空间。具体可参考[[pre1-RISC-V 硬件机制#4 内存管理]] 4 进程调度模块 1、在 Jokerix 实现中采用了一个很简单的进程模型一个进程中只包含一个线程故进程调度等于线程调度。除去用于调度的 idle 线程外最多可以支持 64 个线程同时存在。内核通过线程 idpid来标识每个线程。每个进程资源包括 一个线程一个页表一个打开的文件描述符表 2、调度使用 Round-Robin 算法将 CPU 运行时间划分为时间片每个线程一次可以在一个时间片内运行。内核维护一个线程队列当队首线程的时间片用完后调度线程会将其移动到队尾并让下一个线程开始占用 CPU 运行。 3、线程与进程线程结构体定义 typedef struct {usize contextAddr; /* 线程上下文存储的地址 */usize kstack; /* 线程栈底地址 */Process process; /* 所属进程 */int wait; /* 等待该线程退出的线程的 Tid */ } Thread;进程看上去更像是线程的附属维护一个线程的相关资源进程结构体 typedef struct {usize satp; /* 页表寄存器 */File oFile[16]; /* 文件描述符 */uint8 fdOccupied[16]; /* 文件描述符是否被占用 */ } Process;Jokerix上下文Jokerix在一个线程运行时CPU 中寄存器的值以及线程栈上的值称为这个线程的上下文。在线程切换时需要将上一个线程的上下文保存起来以便在未来再次切换到这个线程时能够继续之前的状态运行下去。在线程创建时就会在线程栈上分配一个空间用于存放线程上下文在未来线程切换时上下文都会保存在线程栈上的同一位置。 4、线程状态线程 Thread 结构体本身并不保存其状态状态的维护由线程池来完成 /* 线程状态 */ typedef enum {Ready,Running,Sleeping,Exited } Status;就绪状态Ready线程处于就绪队列中等待被 CPU 调度执行运行状态Running线程此时正占有 CPU 运行睡眠状态Sleeping线程Jokerix等待某个条件满足Jokerix此时线程不会被调度执行直到条件满足后才会被加入就绪队列退出状态Exited线程已经完成了任务执行但是Jokerix还没有回收资源Jokerix 5 文件系统模块 1、Jokerix 中采用的文件系统是 SimpleFileSystem 的一个魔改版本。名称仍然延用 SimpleFSSimpleFS 将磁盘分成了多个大小为 4096 字节的磁盘块 超级块SuperBlock它记录了整个文件系统的基本信息例如总磁盘块数、未使用的磁盘块数、Freemap 块个数等。若干个 Freemap 块它记录了整个文件系统中Jokerix磁盘块的占用情况Jokerix。Freemap 块使用一个 bit 表示一个块0 为未被占用1 为已被占用。通过 Freemap 块我们就可以快速找到空闲可用的磁盘块。根文件系统的 Inode 块SimpleFS 也是以树状结构组织文件的Root 即为 “/” 文件夹。其他文件的 Inode 或者数据块这些块不会做特殊的排序等查找文件需要从根目录开始查找。目前的文件系统实现是在链接时直接将文件系统镜像链接到 .data 段在运行时文件系统就会和内核一起直接被加载进内存。 0.6 实验环境 实验所需的工具主要分为两个部分交叉编译工具链和 QEMU 。 1 交叉编译工具链 交叉编译器Cross compiler是指一个在某个系统平台下可以Jokerix产生另一个系统平台的可执行文件Jokerix的编译器。交叉编译器在目标系统平台开发出来的应用程序序所运行的平台难以或不容易编译时非常有用。 主要包括gcc、ld、objdump 和 gdb 等 2 安装 QEMU 由于实际上我们并没有 RISC-V 架构的机器即使真的编写出了什么操作系统或者程序也没有硬件平台来运行。好在随着 RISC-V 架构的流行很多虚拟机开始支持Jokerix对 RISC-V 架构硬件的模拟Jokerix如 Bochs 和 QEMU 等。这里我们选用 QEMU。
http://www.ho-use.cn/article/10816725.html

相关文章:

  • 网站建设费用预算表、网站开发和后期维护费用
  • 怎么做微拍网站手机网站建设策划
  • 做公司网站用哪个公司比较好怎么查网站空间
  • 做网站后期需要什么费用百度搜索指数入口
  • 官方建网站哪个好响应式网站和传统网站
  • 网站手机访问跳转代码电力公司 网站开发报价单
  • 免费做电子目录的网站wordpress不加载样式表
  • 重庆 网站设计内部卷网站怎么做的
  • wordpress安装百度站长资源平台网站数据库在空间吗
  • php做网站页面在哪做毕节地seo
  • 移动网站开发环境 主流国外服务器商
  • 家用电脑网站建设wordpress 发布时间
  • 自己做免费手机网站吗福州外文网站建设
  • IT科技资讯新闻类织梦网站模板电商入门基础知识
  • 做网站源码流程做微商代理去哪个网站
  • 网站集约化建设困难建设工程168网手机版下载
  • jsp网站服务建设是什么seo关键字排名
  • 做网站公司教程网站建设 广西
  • python 网站开发流程图建网页要钱吗
  • wordpress搜索页面不同系统优化大师官方下载
  • 开启wordpress多站点英网站建设
  • 徐州住房和城乡建设部网站网页怎么赚钱
  • 做游戏的网站有哪些移动网站建设解决方案
  • 网站做的跟别人的一样可以吗双语网站用什么程序做
  • 建设自己网站男女做那个网站动态图
  • 1小时快速搭建网站google搜索引擎入口下载
  • 株洲网站设计外包首选企业注册号
  • 网站备案信息可以更改吗有没有在网上做ps赚钱的网站
  • 如何搭建一个网站开发环境中国室内设计联盟网站
  • p2p金融网站开发腾讯公司网页设计