【OSLab】Nasm实现加减乘法;FAT镜像查看工具;I/O程序

OSLab1:Nasm实现加减乘法

基本环境

  1. 共享文件夹
    每次都要用该条命令重新运行一下。如果报错mounting with error就把增强功能.exe双击重新安装一下。
    在这里插入图片描述
  2. makefile中不宜写,会有蜜汁报错也会重复编译,没必要。
  3. 编译命令如下,发现不加g也可调试。在这里插入图片描述
    nasm -felf -g cal.asm
    gcc -g -o cal cal.o

NASM基础语法

len:  equ  2 #并没有在最终代码中分配任何空间,它只是将len符号设置为等于2。相当于#define len 2
len:  db   2 #在内存中分配了一个字节存储2,len是该字节的地址。相当于int len=2

在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述

gdb调试nasm

在这里插入图片描述
根据地址查看内容:在这里插入图片描述在这里插入图片描述
查看gdb运行到哪一条:
在这里插入图片描述

加减乘法实现逻辑

offset的作用:防止取到负号’-‘进行运算

  1. 减法逻辑:(异号加法)
    1. esi指向pos_string的末尾,edi指向neg_string的末尾,ebx记录较短的operand_len(不算负号),edx指向add_res的末尾
    2. cal_sub:永远是esi-edi,多余的位数直接存储进edx(为什么不用’\0’计算?引发数字和字符转换的麻烦)
    3. 通过add_res第一个不为0的数判断正负,ebx记录add_res长度
      1. 数字为负,从最后一位开始遍历,若为正,该位-10,上一位+1
      2. 数字为正,从最后一位开始遍历,若为负,该位+10,上一位-1
    4. 从首位开始循环看是否为0,是就后移,直到最后一位
  2. 加法逻辑:(同号加法)
    1. ecx记录进位,edx指向add_res的末尾
    2. cal_add:看该轮次是否有进位
    3. 加上剩余的first/second string
    4. 最后一个进位未处理,和’1’比较
  3. 乘法逻辑:
    1. ecx记录地址的最小值,edx指向mul_res的末尾
    2. cal_mul
    3. 判断ecx/ecx+1是mul_res的开头
    4. 数字转字符
  4. 开头:判断有效性和符号&操作;结尾:清空所有变量和寄存器

编码总结

  1. 有符号跳转和无符号跳转的区别,跳转符号往往容易出错。
  2. 得到了1、4这样的值后,记得加48变成ascii码。(区分数字和字符的值)
  3. gdb有时输r显示权限不够,再输一次r就可以了(不理解为什么)
  4. 函数和跳转的区别:call会把地址保存,ret时就能返回(往往伴随着保存现场和恢复现场)。而跳转只能顺序执行。
  5. 判断两个操作数的符号时用加法(3种情况分类讨论),而不是简单用减法。
  6. 循环开始时记得清理.bss,否则无法进行多次运算。
  7. 减法的符号是否为正负看第一个非0位。(不是直接看首位,反例:100±101)
  8. 减法和乘法最后循环处理多余0(加法检查进位)

OSLab2:FAT镜像查看工具

FAT12结构理解

  1. 引导扇区FAT12Header:存基本信息:见12个字段,共25字节(不全,只是fread了所需字段)

  2. FAT1/FAT2每个表项与数据区的一个簇对应,FAT项的值代表文件的下一个簇号

    1. 通过当前文件的簇号传入FAT,得到FAT项的值==下一个簇号。一个文件被存进若干个簇。利用簇号即可进入数据区读取相应内容。

    2. fatBase = RsvdSecCnt * BytsPerSec;//FAT1的偏移字节 = Boot记录占用的扇区数*每扇区字节数
      
  3. 根目录项RootEntry:见7个字段,共32字节

    1. 文件属性DIR_Attr可判断是目录还是文件

    2. 文件开始的簇号可以知道文件的内容

    3. fileRootBase = (RsvdSecCnt + NumFATs * FATSz) * BytsPerSec; //根目录首字节的偏移数=boot+fat1&2的总字节数
      
    4. 如何获取tree图?

      1. 利用RootEntCnt根目录最大文件数,遍历每个根目录项,得到HOUSE/NJU/ROLL.TXT(见initRootEntry)

        1. 如果是文件

          1. addFileChild:直接加入file_child
          2. 利用开始簇号和FAT遍历结束即可得到文件内容(RetrieveContent)。其中FAT项不仅用于判断该簇是否损坏,还代表文件的下一个簇号,如此遍历到文件的最后一个簇可以获得文件的完整内容
        2. 如果是目录

          1. addDirChild:在加入dir_child时要加入.和…

          2. 开始簇号指示数据区的某个扇区,这个扇区存放着该目录的所有子内容。所以只需要在最开始计算这个簇的FAT值,如果不是坏簇就遍历整个簇的内容(遍历次数为簇的大小512字节/目录项大小32字节=16,比根目录的7要多)

          3. 值大于或等于0xFF8,表示当前簇已经是本文件的最后一个簇

            经实际操作,目录项所在的簇可以且均>=0xFF8,不受该条件约束,推断数据区先存文件再存目录

    5. 是否需要将RootEntry和FileNode联系起来?不需要,RootEntry只存有根目录下的内容,而FileNode通过链表存有所有内容

  4. 数据区

    1. dataBase = BytsPerSec * (RsvdSecCnt + FATSz * NumFATs + (RootEntCnt * 32 + BytsPerSec - 1) / BytsPerSec);//数据区的开始扇区号
      int startByte = dataBase + (currentClus - 2) * BytsPerClus;//通过数据区的起始扇区和簇号可以知道要读取内容的起始字节位置
      
    2. 文件和目录可以自定义结构体存储连接成树,我定义了FileNode。
    3. 从本质上讲,根目录区和数据区都保存着与文件相关的数据,只不过根目录区只能保存目录项信息,而数据区不但可以保存目录项信息,还可以保存文件内的数据。

gdb调试C++

  1. g++ -std=c++11 -g main.cpp -o main 编译cpp文件。如果没有-g可以编译运行,但是不可调试。
  2. 调试命令 常用的有b+代码行数,r,next(不进入函数体),step(进入函数体),print(很好用,如print this->getName()均支持)

编码总结

  1. string->const char *:用c_str()转换
  2. int->string:用std::to_string() 转换。C++和Java/Python不同,string+int不能自动转换。
  3. 获取FAT值:因为2个FAT项占用3个字节,如下图在这里插入图片描述
    所以簇号的奇偶决定了值:
//获得第num个FAT簇的值
int getFAT(FILE *fat12, int n){
    int pos = FATBase + n * 3 / 2;

    uint16_t bytes;
    //设置流fat12的文件位置为给定的偏移fatPos,SEEK_SET文件的开头
    fseek(fat12, pos, SEEK_SET);
    //从给定流fat12读取数据到bytesPtr所指向的数组中 size=1 nmemb=2
    fread(&bytes, 1, 2, fat12);// 读入2字节

    if(n % 2 == 0){//取的是3字节中的1/2
        return bytes & 0x0fff;//低12位
    }else{//取的是3字节中的2/3
        return bytes >> 4;//高12位
    }
}
  1. 在一个成员函数中调用另一个成员函数时写不写this->均可,和成员变量一个道理
  2. 大bug:在这里插入图片描述
    注意uint8_t是char,所以不能直接用于乘法运算(uint16_t同理),而要先变成int(所以即使成员变量中有这些值,依然要赋给全局变量,不经过类型转换的值乘法时会出错)
    在这里插入图片描述
    经正确赋值给int后发现扇区位置符合该表
  3. 注意movTargetNode(相当于BFS的一层)和findFileByName(DFS)时遇到空字符串时返回root而不是error,这样路径开头有没有/都不会影响程序,免得split后产生空字符串传入得到errNode。
  4. findFileByName(DFS)和findFileByPath不同,前者是全局搜索,后者要沿着路径顺序搜索。二者结合使用对比结果可以判断到底是路径问题还是文件/目录本身就不存在。
  5. fseek和fread结合使用可以读取流文件指定位置的内容。
//设置流 stream 的文件位置为给定的偏移 offset, 这里stream=fat12, offset=11
    fseek(fat12, 11, SEEK_SET); // BPB块从11字节开始 SEEK_SET:文件的开头

    //从给定流 stream 读取数据到 ptr 所指向的数组中
    // headptr:指向带有最小尺寸为size*nmemb字节的内存块的指针 size=1要读取的每个元素的大小 nmemb=元素的个数 stream=fat12
    fread(this, 1, 25, fat12);

OSLab3:I/O程序

基本环境

  1. mount point /mnt/floppy does not exist解决:mkdir挂载点不存在
  2. dlopen failed for module ‘x‘:解决:sudo apt-get install bochs-x
  3. 对__stack_chk_fail未定义的引用:解决:加字段,意为不需要栈保护CFLAGS = -I include/ -c -fno-builtin -fno-stack-protector 加在下面失败 加在上面成功

部分知识

其实一知半解:主要记住CONSOLE与TTY一一对应?共用KEYBOARD/一块显存

  1. terminal是终端,不在主机上,远端控制

  2. console是主机上的面板开关,本机控制

  3. nr_current_console记录当前的控制台是哪个,只有TTY对应的控制台是当前控制台时,才可以读取键盘缓冲区

  4. tty_do_write:把字符写入指定的CONSOLE

  5. TTY:任务 ABC:用户进程在这里插入图片描述

  6. 显存:专注于图形显示,更擅长于图形显示,而显卡进行图形显示时是需要内存资源的,如果其所需的内存资源都从内存(CPU主存,后面简称内存)分配,那相对于使用显卡自带的内存,性能肯定有损耗。

  7. 显存:通过内核的DRM模块进行管理,用户态和内核程序都可以使用DRM提供的接口分配和释放显存。 用户态程序通过Libdrm提供的用户态接口(本质上为ioctl调用)来分配和释放显存。

  8. key & FLAG_EXT包含:

    1. ENTER
    2. BACKSPACE
    3. TAB
    4. UP
    5. DOWN
    6. F1-F12

代码改变

  1. const.h
    1. 定义tab & color
    2. 定义current_mode 共4种
  2. global.h
    1. 定义EXTERN INT current_mode 记录当前所处的模式
  3. proto.h
    1. tty.c console.c声明了一些PUBLIC 方法
  4. console.c
    1. 用Action实现存储操作信息
    2. 用reverse存储每个操作的逆操作,用于撤回
    3. 支持tab:out_char
    4. 支持撤回:在out_char时保存reverse操作,然后
  5. tty.c
    1. 清空:利用get_ticks和refresh和clean_screen方法
    2. 搜索:in_process里面
    3. put_key:将key放入缓冲区
    4. end_search:清除工作
    5. search:双重循环

出错点

  1. 其实不太理解,就是key_len在定义时赋值为0了,但函数体中必须要赋值为0,否则会出错
  2. 当匹配文本遇到tab时,记得记录实际长度,在输出红色文本时传入(别人与实际地址1对2,tab与实际地址1对8)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值