一文带你慢且不通SEOSLab2

SYuan03 Lv4

主要是针对OS Lab2的一些记录,自己看看的

makefile解读

image-20230427102950131

一共三条,make onlyCpp主要是一开始没写汇编的打印函数,全用的cout,先编译出来看看效果

同时也是为了便于调试,打断点(./main的过程可能也是可以调的,没试


实验开始前

装库

gcc-multilib 和 g+±multilib

不然后面会报错

image-20230427103322790

制作软盘并建立结构

1
2
3
4
mkfs.fat -C a.img 1440
mkdir mountlab2 # 挂载点
sudo mount ./a.img ./mountlab2 # 进行挂载
cd ./mountlab2 # 就可以进行一些创建之类的操作了

补充:

  1. 创建层及目录mkdir可加-p参数

    mkdir -p a/b/c

  2. 进入挂载好的目录之后,切换成root创建各种东西会方便点

    如果是普通用户vim文件时可能显示文件read only

  3. 在盘的根路径下使用tree命令查看结构


代码部分

直接从主代码开始看

整个结构,很重要:

image-20230427143504426

1. 初始化BPB信息

打开文件,用自建的BPB类读取相关信息

BPB类如下:

image-20230427142737793

init函数读取

bpb->init(fat12)进行读取

先读到bpb类的成员函数里面(两行就行

1
2
3
// BPB从第11个字节处开始,0-2为短跳转指令,3-10为厂商名
fseek(fat12, 11, SEEK_SET);
fread(this, 1, 25, fat12); // BPB长度为25字节

再初始化一些后面可能用到的全局变量

要计算的是四个

BytesPerClus = 1 * 512

FATBase = 1 * 512

RootDirBase = (1 + 2 * 9) * 512

DataBase = ((224 * 32 + 32 - 1) / 512 + 1 + 2 * 9) * 512

2. 创建root根节点(所要建立的那个树

Node类如下:

以及还有一部分get set方法

image-20230427145121947

设置一些root的成员变量

image-20230427145612583

3. 开始创建树,利用根目录区开始

根目录区其实就是很多目录项(这里的目录包含文件,比如我使用的img文件中根目录下就应该有三项

0x2600根目录区开始

image-20230427150108412

后面不用管,注意文件名是8+3

由此开始读文件(并且建立树结构)之旅

image-20230427150150503

RootDirEntry结构如下:

RootDirEntry纯粹就是个读东西存放用的工具

一共32字节,也就是两行

image-20230427151107912

这样就是一个目录项

image-20230427150323300

RootDirEntry里有readFile方法,从这个开始递归读取

注1:讲道理这个readFile方法其实就调用了一次,不该作为RootDirEntry的成员方法,而应该是全局的一个函数

注2:RootDirEntry类的DIR_Name就是那个占11字节的名字

注3:就是在这里获取了DIR_FstClus,后面读取内容会用到

主体结构如下

注1:根目录项的文件属性

image-20230427154655628

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void RootDirEntry::readFile(FILE *fat12) {
int base = RootDirBase; // 读取位置,起始位置为根目录区首地址

for (int i = 0; i < RootEntCnt; i++) {
fseek(fat12, base, SEEK_SET);
fread(this, 1, 32, fat12); // 读32字节,即一个条目的大小
base += 32;
if (this->isInvalidName()) {
continue;
}
if ((this->DIR_Attr & 0x10) == 0) { // 属性是0x10表示是文件
string name = dealFileName(this->DIR_Name); // 处理文件名

// 创建node* fileNode将其加入到root的children里
// 略

// 读取文件内容
readFileContent(fat12, fileChild);
} else if ((this->DIR_Attr & 0x20) == 0) { // 0x20表示是目录
string name = dealDirName(this->DIR_Name); // 处理目录名

// 创建node* dirNode将其加入到root的children里
// 略

// 读取目录,更确切的来说是根目录的子目录的内容
readDirContent(fat12, dirChild);
}
}
}

bool RootDirEntry::isInvalidName():

读取根目录项时判断哪些是需要的文件或目录

image-20230427154404957

以’\0’开头(就是空的目录项)或者出现11个字节内出现任何不合法字符就是invalid

可如下验证:

image-20230427154538896

RootDirEntry::readFile内的四个函数

string name = dealFileName(entry->getDirName());

readFileContent(fat12, fileChild);

string name = dealDirName(entry->getDirName());

readDirContent(fat12, dirChild);

递归就是靠这两个readContent函数实现的

个人感觉递归的关键在于

如果根目录下是一个目录,那么在数据区对应的地方也是会有一个32字节的目录项,就跟根目录区是一样的构成

数据区并不全是数据,非根目录的目录也是会在这的

复看代码的时候发现一处问题

linux并不是文件都有扩展名,所以在dealFileName的时候,文件名是不一定包含’.'的

image-20230427160658080

image-20230427160823064

但好像这次实验又说了文件会有扩展名,所以就不修这个地方了,要修的话无非就是判断下后面有没有扩展名,没有的话就把’.'去掉而已

getFatValue()

实现两个readContent函数还有一个关键的地方在于获取fat表项的值

image-20230427162922356

image-20230427162954663

小端存储:低地址向高地址生长

数据的高字节在高位就是小端存储

图中的例子,读到HOUSE的FstClus=3然后

0x200 + 3 * 3 / 2 = 0x204

把204(f0) 205(ff)的字节读出来是fff0,奇数,取高12位fff

说明结束了,HOUSE的内容只在簇3

然后簇3-2=簇1

数据区起始在0x4200一个簇512字节就是0x200所以0x4400的数据就是它的内容

image-20230427164448142

image-20230427163104689

至此,文件终于读完了!树也构建好了

4. 汇编实现的打印函数

1
2
3
4
5
6
7
8
9
10
11
global myAsmPrint

section .text
myAsmPrint:
; 利用栈传递参数
mov eax,4 ; 系统调用号
mov ebx,1 ; 文件描述符,1表示stdout
mov ecx,[esp+4] ; char * s ;加4是因为调用函数首先要压IP进栈,接下来的两个
mov edx,[esp+8] ; length
int 80h
ret

关于参数压栈顺序

(2条消息) C语言 | 函数参数压栈的顺序是?_函数参数压栈顺序_嵌入式大杂烩的博客-CSDN博客

栈应该是高地址往地址生长

C/C++ 参数从右往左压栈,而之前要先压入一个IP,所以栈从高到低是

长度、首地址、IP

image-20230427165628885

5. 实现cat命令

cat和ls都用到了一个关键的函数

findFileNodeByName

找到的话就返回节点,没找到或者找的过程中有不存在的,也会返回空

6. 实现ls和ls -l

注意ls - 是要报错的

ls 和 ls -l差不太多

递归实现下listAllContentWithoutL和listAllContentWithL就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// ls指令,要处理的东西还是很多的
void ls(vector<string> &commands) {
bool haveL = false;
bool haveDir = false;
int dirCount = 0;
string dir;

for (int i = 1; i < commands.size(); i++) {
if (commands[i][0] == '-') {
haveL = true;
bool validL = isValidL(commands[i]);
if (!validL) { // 如果指令不合法,提示输入错误,并直接结束
myPrint(ERROR_COMMAND);
return;
}
} else {
haveDir = true;
dir = commands[i];
dirCount++;
}
}

// 执行到这里如果有l也是合法的
if (dirCount > 1) { // 有多个目录是不合法的
myPrint("Too much directory!");
myPrint("\n");
return;
}

if (haveL) {
if (!haveDir) { // 有l 没目录
listAllContentWithL(root);
} else { // 有l 有目录
Node *file = findFileNodeByName(dir);
if (file != nullptr) {
if (file->getFileType() == FileType::FILE) {
myPrint("This is a File!\n");
return;
}
listAllContentWithL(file);
} else {
myPrint(CANNOT_FOUND);
return;
}
}
} else {
if (!haveDir) { // 没l 没目录
listAllContentWithoutL(root);
} else { // 没l 有目录
Node *file = findFileNodeByName(dir);
if (file != nullptr) { // 如果文件存在,则输出,不存在则报错退出
if (file->getFileType() == FileType::FILE) {
myPrint("This is a File!\n");
return;
}
listAllContentWithoutL(file);
return;
} else {
myPrint(CANNOT_FOUND);
return;
}
}
}
}
  • 标题: 一文带你慢且不通SEOSLab2
  • 作者: SYuan03
  • 创建于 : 2023-04-14 23:45:07
  • 更新于 : 2024-03-10 19:56:10
  • 链接: https://bblog.031105.xyz/posts/2023-Spring-Courses-操作系统/一文带你慢且不通seoslab2.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
评论