简单Linux文件系统核心函数

### 简介 此文承接上文简单Linux文件系统数据结构设计,内容是文件系统中核心函数的实现。

申请iNode节点

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
//申请iNode节点,size单位为Byte
int FileSystem::alloc_inode(unsigned long size, iNode &node,bool is_dentry)
{
unsigned int blocks_needed = ceil((double)size / s_block.blockSize);//需要存储内容的块数
//最大大小超过限制,iNode节点不足时
if (size>s_block.maxBytes || s_block.inode_remain == 0){
return -1;
}
if (size == 0){
blocks_needed = 10;
}
fileDisk.seekg(s_block.inodemap_pos, ios::beg);//挪动到位图位置
bool is_end = false;
int inode_no = 1;//使用的iNode号码
unsigned char* bytes = new unsigned char[s_block.blockSize];//读取的数组

int block_node_num = s_block.blockSize / sizeof(unsigned int);//每块可存的iNode的id的数目
if (blocks_needed <= 10){
//可以在10个直接块中放下
}
else if (blocks_needed <= (10 + block_node_num)){
//加上一个一次间接块
blocks_needed += 1;
}
else if (blocks_needed <= (10 + block_node_num + block_node_num*block_node_num)){
//加上一个二次间接块
blocks_needed += 2 + block_node_num;
}
else if (blocks_needed <= (10 + block_node_num + block_node_num*block_node_num + block_node_num*block_node_num*block_node_num)){
//加上一个三次间接块
blocks_needed += 3 + 2 * block_node_num + block_node_num*block_node_num;
}
if (blocks_needed > s_block.block_remain){
//超过剩余量
return -1;
}
//寻找空闲的iNode
for (int i = 0; i < ceil(s_block.inode_num / (8 * s_block.blockSize)); i++){
if (is_end)
break;//遍历完所有位图后退出
fileDisk.read((char *)bytes, s_block.blockSize);//一次读取一块
unsigned char mask = 1;
mask = mask << 7;
for (int index = 0; index<s_block.blockSize; index++){
if (is_end)
break;//遍历完所有位图后退出
unsigned char byte = bytes[index];
for (int j = 0; j < 8; j++){
if (!((byte << j)&mask)){
//若有空位
bytes[index] = byte | (1 << (7-j));
fileDisk.seekg(s_block.inodemap_pos + i*s_block.blockSize, ios::beg);
fileDisk.write((char *)bytes, s_block.blockSize);//位图改变整块写回
is_end = true;//结束
break;
}
inode_no += 1;
if (inode_no>s_block.inode_num){
is_end = true;
break;
}
}
}
}
vector<unsigned int> block_list;
alloc_blocks(blocks_needed, block_list);//申请磁盘块
node = iNode(inode_no,size,blocks_needed,block_list);
node.i_mode = (7<<8)+(7<<4)+7;//777
if (is_dentry){
unsigned short mode = 1;
mode = mode << 14;
node.i_mode = mode + (7 << 8) + (7 << 4) + 7;//设定为文件夹
}
node.i_uid = currUser.u_id;
node.i_gid = currUser.u_id;
write_inode(node);
//更新相应的超级块信息
s_block.inode_remain--;
s_block.block_remain -= blocks_needed;
seekAndSave<superBlock>(0, s_block);
return 1;
}

申请块

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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//申请块
int FileSystem::alloc_blocks(int num, vector<unsigned int> &list){
fileDisk.seekg(s_block.bitmap_pos, ios::beg);//挪动到位图位置
bool is_end = false;
unsigned int block_no = 1;//使用的block号码
unsigned char* bytes = new unsigned char[s_block.blockSize];//读取的数组
//寻找空闲的block
for (int i = 0; i < ceil(s_block.block_num / (8 * s_block.blockSize)); i++){
if (is_end)
break;//遍历完所有位图后退出
fileDisk.read((char *)bytes, s_block.blockSize);//一次读取一块
bool modify = false;//是否有更改需要写回磁盘
unsigned char mask = 1;
mask = mask << 7;
for (int index = 0; index<s_block.blockSize; index++){
if (is_end)
break;//遍历完所有位图后退出
unsigned char byte = bytes[index];
for (int j = 0; j < 8; j++){
if (block_no < s_block.first_data_block_no){
block_no++;
continue;
}
if (!((byte << j)&mask)){
//若有空位
bytes[index] = byte | (1 << (7 - j));
byte = bytes[index];//更新byte
list.push_back(block_no);
modify = true;
if (num == list.size()){
is_end = true;//结束
break;
}
}
block_no += 1;
if (block_no>s_block.block_num){
is_end = true;
break;
}
}
}
if (modify){
fileDisk.seekg(s_block.bitmap_pos + i*s_block.blockSize, ios::beg);
fileDisk.write((char *)bytes, s_block.blockSize);//位图改变整块写回
}
}
clearBlockContent(list);//清除分配的块中的内容
//排放block
int block_per_num = s_block.blockSize / sizeof(unsigned int);//每块可存的block的id的数目
if (list.size() <= 10){
//可以在10个直接块中放下
}
else if (list.size() <= (11 + block_per_num)){
//加上一个一次间接块
int cnt = 0, i;//写入次数和写入下标
unsigned int once_no = list[10];
fileDisk.seekg((once_no-1)*s_block.blockSize);//去到正确的偏移位置
for (i = 11; cnt< block_per_num&&i<list.size(); i++, cnt++){
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
list.resize(11);
}
else if (list.size() <= (12 + 2*block_per_num + block_per_num*block_per_num)){
//加上一个一次间接块
int cnt=0,i;//写入次数和写入下标
unsigned int once_no = list[10];
unsigned int twice_no = list[11];
fileDisk.seekg((once_no - 1)*s_block.blockSize);//去到正确的偏移位置
for (i = 12; cnt <block_per_num ; i++,cnt++){
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
//加上一个二次间接块
vector<unsigned int> once_in_twice;//二次间接块中的一次间接块
fileDisk.seekg((twice_no - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num; i++,cnt++){
once_in_twice.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
//二次间接块的一次间接块
for (auto item : once_in_twice){
fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num&&i<list.size(); i++, cnt++){
once_in_twice.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
}
list.resize(12);
}
else if (list.size() <= (13 + 3*block_per_num + 2*block_per_num*block_per_num + block_per_num*block_per_num*block_per_num)){
//加上一个一次间接块
int cnt = 0, i;//写入次数和写入下标
unsigned int once_no = list[10];
unsigned int twice_no = list[11];
unsigned int third_no = list[12];
fileDisk.seekg((once_no - 1)*s_block.blockSize);//去到正确的偏移位置
for (i = 13; cnt <block_per_num; i++, cnt++){
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
//加上一个二次间接块
vector<unsigned int> once_in_twice;//二次间接块中的一次间接块
fileDisk.seekg((twice_no - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num; i++, cnt++){
once_in_twice.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
//二次间接块的一次间接块
for (auto item : once_in_twice){
fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num; i++, cnt++){
once_in_twice.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
}
//加上一个三次间接块
vector<unsigned int> twice_in_third;//三次间接块中的一次间接块
fileDisk.seekg((third_no - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num; i++, cnt++){
twice_in_third.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
once_in_twice.clear();
for (auto item : twice_in_third){
fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num; i++, cnt++){
once_in_twice.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
}
for (auto item : once_in_twice){
fileDisk.seekg((item - 1)*s_block.blockSize);//去到正确的偏移位置
cnt = 0;
for (; cnt<block_per_num&&i<list.size(); i++, cnt++){
once_in_twice.push_back(list[i]);
fileDisk.write((char*)&list[i], sizeof(unsigned int));
}
}
list.resize(13);
}
return 1;
}

目录搜索

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
//通过路径vector寻找目录项,返回1表示找到文件夹,返回2表示找到文件,0表示未找到,type为1找文件夹,type为2找文件
int FileSystem::findDentry(vector<string> list,dentry *&p_dentry,char firstChar,int type)
{
int ret = 0;
p_dentry = curr_dentry;
if (firstChar == '/'){
p_dentry = &root_dentry;//从根目录开始
list.erase(list.begin());//若第一个字符是/则split后第一位为空
}
if (list.size() == 0){
return 1;//直接当前目录下创建
}
for (auto item : list){
ret = 0;
if (item == ".."){//父层目录
p_dentry = p_dentry->parent;
ret = 1;
}
else if (item == "."){//当前目录
ret = 1;
}
else{//遍历寻找目录
for (auto child_dentry : p_dentry->child_list){
if (child_dentry->fileName == item){
//如果名字对上了,还要判断文件类型
if (child_dentry->is_dir()){
p_dentry = child_dentry;
if (p_dentry->child_list.size() == 0){
InitDentry(*p_dentry);//初始化自身及子目录
}
for (auto item : p_dentry->child_list){
InitDentry(*item);
}
//若不是路径上的最后一个,则必须是文件类型
if (type == FOLDER_TYPE || item != list[list.size() - 1]){
ret = FOLDER_TYPE;//找的是文件夹
break;
}
}
else{
p_dentry = child_dentry;
//若是路径上的最后一个,则看是不是寻找的文件
if (type == FILE_TYPE && item == list[list.size() - 1]){
ret = FILE_TYPE;//找的是文件
break;
}
}
}
}
}
if (ret == 0){
return ret;
}
}
return ret;
}

小结

此处只说明了比较难缕清思路的几个函数,其他函数没有作过多的说明,项目地址在GitHub,代码中都有注释,在多进程间同步的方法采用的是:
单SimDisk,多Shell的方式,Shell与SimDisk之间通讯首先要加锁,进程间通讯用的是共享内存的方式。
流程大致如下:

至于如何鉴定Shell对应的用户,采用的是登录后保存token,每次执行名利带上token的方式来校验,校验流程如下: