OSLab2
实验要求
实现一个FAT12镜像查看工具。本次实验重点在于:熟悉掌握FAT12文件系统、gcc+nasm联合编译,了解实模式与保护模式的基本内容。
用C/C++和nasm编写一个FAT12镜像查看工具,读取一个.img格式的文件并响应用户输入。
¶功能列表
- 运行程序后,读取FAT12镜像文件,并提示用户输入指令
- 用户输入ls 路径,输出根目录及其子目录的文件和目录列表。
- 首先输出路径名,加一个冒号:,换行,再输出文件和目录列表;
- 使用红色(\033[31m)颜色输出目录的文件名,不添加特殊颜色输出文件的文件名。
- 当用户不添加任何选项执行ls命令时,每个文件/目录项之前用两个空格隔开
- 当用户添加-l为参数时,
- 在路径名后,冒号前,另输出此目录下直接子目录和直接子文件的数目,两个数字之间用空格连接。此两个数字不添加特殊颜色
- 每个文件/目录占据一行,在输出文件/目录名后,空一格,之后:
- 若项为目录,输出此目录下直接子目录和直接子文件的数目,两个数字之间用空格连接。此两个数字不添加特殊颜色
- 不输出.和…目录的子目录、子文件数目
- 若项为文件,输出文件的大小
- 对于-l参数用户可以在命令任何位置、设置任意多次-l参数,但只能设置一次文件名
- 直接子目录不计算.和…
- 当用户给出不支持的命令参数时,报错
- 当用户不设定路径时,默认路径为镜像文件根目录
- 用户输入cat 文件名,输出路径对应文件的内容, 若路径不存在或不是一个普通文件则给出提示,提示内容不严格限定,但必须体现出错误所在。
- 用户输入exit, 退出程序。
实现思路
读取指令略去,先简单记录一下如何查找目录及文件:
¶FAT12 根据路径字符串查找目录及文件
对于字符串将其按/
分割,根据需求处理.
和..
的问题。然后调用searchTargetDir(target, des)
,函数会搜索并修改target的值,获得在数据区的位置。
1 | // 19-32号扇区为根目录文件项 |
¶实现cat:读取文件内容
1 | int cluster = target / 512 - 33 + 2; //计算簇数,从33号扇区开始,起始对应编号为2 |
cluster = target / 512 - 33 + 2;
计算簇数,从33号扇区开始,起始对应编号为2,在一个簇读取完毕时,需要到下一个簇区,由于FAT12中12位代表一个FAT项,2个FAT占3个字节,故需要分奇偶来处理。
问答题
¶课件相关
¶1.什么是实模式,什么是保护模式?
实模式使用基地址加偏移量的方式就可以直接拿到物理地址的模式。
保护模式是不能直接拿到物理地址的模式,需要进行地址转换。
¶2.什么是选择子?
选择子总共16位,存放在段选择寄存器中,低2位表示请求特权级,第3位表示选择GDT方式还是LDT方式,高13位表示在描述符表中的偏移。
¶3.什么是描述符?
保护模式下引入描述符来描述各种数据段,所有的描述符均为8个字节(0-7),由第5个字节说明描述符的类型。类型不同,描述符的结构也有所不同。
¶4.什么是GDT,什么是LDT?
GDT是全局描述符表,是全局唯一的。存放一些公有的描述符和包含各进程局部描述符表首地址的描述符。
LDT是局部描述符表,每个进程都可以有一个。存放本进程中使用的描述符。
¶5.请分别说明GDTR和LDTR的结构
GDTR:48位寄存器,高32位放置GDT首地址,低16位放置GDT限长,限长决定了可寻址的大小。(注意低16位放的不是选择子)
LDTR:16位寄存器,放置一个特殊的选择子,用于查找当前进程的LDT首地址。
¶6.请说明GDT直接查找物理地址的具体地址
-
给出段选择子(放置在段选择寄存器中)+偏移量
-
若选择了GDT方式,从GDTR中获取GDT首地址,用段选择子中的13位做偏移,拿到GDT中的描述符
-
如果合法且有权限,用描述符中的段首地址加上1中的偏移量找到物理地址,寻址结束
¶7.请说明通过LDT查找物理地址的具体步骤
-
给出段选择子(放置在段选择寄存器中)+偏移量
-
若选择了LDT方式,则从GDTR获取GDT首地址,用LDTR中的偏移量做偏移,拿到GDT中的描述符1
-
从描述符1获取LDT首地址,用段选择子中的13位做偏移,拿到LDT中的描述符2
-
如果合法且有权限,用描述符2中的段首地址加上1中的偏移量找到物理地址,寻址结束
¶8.根目录区大小一定吗?扇区号是多少?为什么?
不一定,根目录区位于第二个FAT表之后,开始的扇区号为19,它由若干个目录条目(Directory Entry)组成,条目最多有BPB_RootEntCnt个。由于根目录区的大小是依赖于BPB_RootEntCnt的,所以长度不固定,根目录区中的每一个条目占用32字节。(BPB_RootEntCnt即根目录文件数(条目数)最大值)
¶9.数据区第一个簇号是多少?为什么?
需要根据根目录大小RootDirSectors计算,数据区DataSectors=RootDirSectors+引导扇区(1)+FAT1(9)+FAT2(9),一般值是33。
数据区的第一个簇是2,因为1.44M的软盘上,FAT前三个字节的值是固定的0xF0、0xFF、0xFF用于表示这是一个应用在1.44M软盘上的FAT12文件系统。本来序号为0和1的FAT表项应该对应簇0和簇1,但是这两个表项都被设置为固定值,簇0和簇1没有存在意义。
¶10.FAT表的作用?
文件分配表被划分为紧密排列的若干个表项,每个表项都与数据区中的一个簇相对应,而且表项的序号也是与簇号一一对应。
¶11.解释静态链接的过程
静态链接是指在编译阶段直接把静态库加入到可执行文件中,这样可能导致可执行文件比较大。
-
空间与地址的分配:每个.o文件都有自己的段属性,比如.text和.data等,链接的第一步就是将这些段属性合并在一起。
-
符号解析和重定位:重定位(修正地址)、重定位表(相对地址修正)
¶12.解释动态链接的过程
动态链接是指链接阶段仅仅加入一些描述信息,而程序执行时再从系统中把相应动态库加载到内存中去。
-
动态链接器自举
动态链接器本身也是一个不依赖其他共享对象的共享对象,需要完成自举。
-
装载共享对象
将可执行文件和链接器自身的符号合并成为全局符号表,开始寻找依赖对象。加载对象的过程可以看作图的遍历过程;新的共享对象加载进来后,其符号将合并入全局符号表;加载完毕后,全局符号表将包含进程动态链接所需全部符号。
-
重定位和初始化
链接器遍历可执行文件和共享对象的重定位表,将它们GOT/PLT中每个需要重定位的位置进行修正。完成重定位后,链接器执行.init段的代码,进行共享对象特有的初始化过程(例如C++里全局对象的构造函数)
-
转交控制权
完成所有工作,将控制权转交给程序的入口开始执行。
¶13.静态链接为什么使用ld链接而不是gcc
gcc工具链包含很多工具,其中用于链接的是ld,ld是binutils工具集的底层部件,所以工作在汇编级别。
¶14.linux下可执行文件的虚拟地址空间默认从哪里开始分配?
0x08048000
¶实验相关问题
¶1.BPB指定字段的含义
名称 | 偏移 | 长度 | 内容 |
---|---|---|---|
BS_jmpBoot | 0 | 3 | 一个短跳转指令 |
BS_OEMName | 3 | 8 | 厂商名 |
BPB_BytsPerSec | 11 | 2 | 每扇区字节数 |
BPB_SecPerClus | 13 | 1 | 每簇扇区数 |
BPB_RsvdSecCnt | 14 | 2 | Boot记录占用多少扇区 |
BPB_NumFATs | 16 | 1 | 共有多少FAT表 |
BPB_RootEntCnt | 17 | 2 | 根目录文件数最大值 |
BPB_TotSec16 | 19 | 2 | 扇区总数 |
BPB_Media | 21 | 1 | 介质描述符 |
BPB_FATSz16 | 22 | 2 | 每FAT扇区数 |
BPB_SecPerTrk | 24 | 2 | 每磁道扇区数 |
BPB_NumHeads | 26 | 2 | 磁头数(面数) |
BPB_HiddSec | 28 | 4 | 隐藏扇区数 |
BPB_TotSecc32 | 32 | 4 | 若BPB_TotSec16为0,由这个值记录扇区 |
BS_DrvNum | 36 | 1 | 中断13的驱动器号 |
BS_Reservedl | 37 | 1 | 未使用 |
BS_BootSig | 38 | 1 | 扩展引导标记 |
BS_VolID | 39 | 4 | 卷序列号 |
BS_VolLab | 43 | 11 | 卷标 |
BS_FileSysType | 54 | 8 | 文件系统类型 |
引导代码及其他 | 62 | 448 | 引导代码、数据、其他填充字符 |
结束标志 | 510 | 2 | 0xAA55 |
¶2.如何进行C代码和汇编之间的参数传递和返回值传递?
参数传递,可以通过栈esp完成,C调用时的函数参数会被放置在esp中,然后汇编从esp+4开始,逐一拿出参数。
汇编语言的返回值,放置在eax中进行返回。