--------------------------------------------------------
创建一个文件并打开(二)
--------------------------------------------------------
上一节我们理清了 do_open 服务函数的框架,实际创建文件需要操作哪些数据结构等我们留在这节来完成!
·alloc_imap_bit 函数
PRIVATE int alloc_imap_bit(int dev)
{
int inode_nr = 0;
int i, j, k;
int imap_blk0_nr = 1 + 1; /* 1 boot sector & 1 super block */
struct super_block *sb = get_super_block(dev);
for (i = 0; i < sb->nr_imap_sects; i++)
{
RD_SECT(dev, imap_blk0_nr + i);
for (j = 0; j < SECTOR_SIZE; j++) /* 以字节为单位遍历 */
{
/* skip `11111111' bytes */
if (fsbuf[j] == 0xFF)
continue;
/* skip `1' bits */
for (k = 0; ((fsbuf[j] >> k) & 1) != 0; k++)
;
// 获得空闲 inode 的序号
// 一个字节是 8 位
// 那么 (i * SECTOR_SIZE + j) 个字节就是 (i * SECTOR_SIZE + j) * 8 位
// k 是字节内的偏移
inode_nr = (i * SECTOR_SIZE + j) * 8 + k;
fsbuf[j] |= (1 << k);
/* write the bit to imap */
WR_SECT(dev, imap_blk0_nr + i);
break;
}
return inode_nr;
}
/* no free bit in imap */
panic("inode-map is probably full.\n");
return 0;
}
——此函数很简单,就是在 imap 中寻找一个空闲的位,然后置位这个位并返回序号!
·alloc_smap_bit 函数
PRIVATE int alloc_smap_bit(int dev, int nr_sects_to_alloc)
{
/* int nr_sects_to_alloc = NR_DEFAULT_FILE_SECTS; */
int i; /* sector index */
int j; /* byte index */
int k; /* bit index */
struct super_block *sb = get_super_block(dev);
int smap_blk0_nr = 1 + 1 + sb->nr_imap_sects;
int free_sect_nr = 0;
for (i = 0; i < sb->nr_smap_sects; i++) /* 以扇区为单位遍历 */
{
RD_SECT(dev, smap_blk0_nr + i);
/* byte offset in current sect */
for (j = 0; j < SECTOR_SIZE && nr_sects_to_alloc > 0; j++) /* 以字节为单位遍历 */
{
k = 0;
if (!free_sect_nr)
{
/* loop until a free bit is found */
if (fsbuf[j] == 0xFF)
continue;
for (; ((fsbuf[j] >> k) & 1) != 0; k++)
;
// free_sect_nr 的具体的值其实没有意义
// 这里换为 free_bit_find = true 更合适
free_sect_nr = (i * SECTOR_SIZE + j) * 8 +
k - 1 + sb->n_1st_sect;
}
for (; k < 8; k++)
{ /* repeat till enough bits are set */
assert(((fsbuf[j] >> k) & 1) == 0);
fsbuf[j] |= (1 << k);
if (--nr_sects_to_alloc == 0)
break;
}
}
if (free_sect_nr) /* free bit found, write the bits to smap */
WR_SECT(dev, smap_blk0_nr + i);
if (nr_sects_to_alloc == 0)
break;
}
assert(nr_sects_to_alloc == 0);
return free_sect_nr;
}
——以扇区为单位遍历,在一个扇区内以字节为单位遍历,在找到一个 smap 中的空闲位之后,下次就不判断空闲位了,而是从此空闲位开始赋值 2048 个 1 !
·new_inode 函数
PRIVATE struct inode *new_inode(int dev, int inode_nr, int start_sect)
{
// 因为 inode_nr 是要新建的 inode ,所以 ge_inode 从磁盘加载到内存的 inode
// 是个无效的 inode ,在后面的语句赋值了再刷新到磁盘就有效了
struct inode *new_inode = get_inode(dev, inode_nr);
new_inode->i_mode = I_REGULAR;
new_inode->i_size = 0;
new_inode->i_start_sect = start_sect;
new_inode->i_nr_sects = NR_DEFAULT_FILE_SECTS;
new_inode->i_dev = dev;
new_inode->i_cnt = 1;
new_inode->i_num = inode_nr;
/* write to the inode array */
sync_inode(new_inode);
return new_inode;
}
——需要注意的是:
// 因为 inode_nr 是要新建的 inode ,所以 ge_inode 从磁盘加载到内存的 inode
// 是个无效的 inode ,在后面的语句赋值了再刷新到磁盘就有效了
struct inode *new_inode = get_inode(dev, inode_nr);
·sync_inode 函数
PUBLIC void sync_inode(struct inode *p)
{
struct inode *pinode;
struct super_block *sb = get_super_block(p->i_dev);
int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects +
((p->i_num - 1) / (SECTOR_SIZE / INODE_SIZE));
RD_SECT(p->i_dev, blk_nr);
pinode = (struct inode *)((u8 *)fsbuf +
(((p->i_num - 1) % (SECTOR_SIZE / INODE_SIZE)) * INODE_SIZE));
pinode->i_mode = p->i_mode;
pinode->i_size = p->i_size;
pinode->i_start_sect = p->i_start_sect;
pinode->i_nr_sects = p->i_nr_sects;
WR_SECT(p->i_dev, blk_nr);
}
——一个 inode 32 字节,但我们不能直接将 32 字节写进磁盘,因为磁盘的操作单位是扇区,因此我们先将 inode 所在的扇区读入内存,然后在内存修改 512 字节中 inode 的 32 字节,再将这一字节写入磁盘!
·new_dir_entry 函数
PRIVATE void new_dir_entry(struct inode *dir_inode, int inode_nr, char *filename)
{
/* write the dir_entry */
int dir_blk0_nr = dir_inode->i_start_sect;
int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE) / SECTOR_SIZE;
int nr_dir_entries = dir_inode->i_size / DIR_ENTRY_SIZE;
int m = 0;
struct dir_entry *pde;
struct dir_entry *new_de = 0;
int i, j;
for (i = 0; i < nr_dir_blks; i++) /* 以扇区为单位遍历 */
{
RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);
pde = (struct dir_entry *)fsbuf;
for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++, pde++) /* 以 dir entry 为单位遍历 */
{
if (++m > nr_dir_entries) /* 一个扇区内可能只有几个有效 dir entry */
break;
if (pde->inode_nr == 0)
{ /* it's a free slot */
new_de = pde;
break;
}
}
if (m > nr_dir_entries || /* all entries have been iterated or */
new_de) /* free slot is found */
break;
}
/* 没有在有效的 entry 数组中找到一个可以用的 entry ,那就在后面开辟一个新的 dir entry */
if (!new_de)
{
new_de = pde;
dir_inode->i_size += DIR_ENTRY_SIZE;
}
// 这里达到了复用有效 entry 数组中的无效 entry 的目的!
new_de->inode_nr = inode_nr;
strcpy(new_de->name, filename);
/* write dir block -- ROOT dir block */
WR_SECT(dir_inode->i_dev, dir_blk0_nr + i); /* 只写修改了 dir entry 的那个扇区 */
/* update dir inode */
sync_inode(dir_inode); /* 目录 inode 节点 size 改变了 */
}
- dir entry 是根目录文件的数据,所以增加了 dir entry ,记得更新根目录的 inode
- 有效 dir entry 数组中会有无效的 dir entry,所以要么新建 dir entry,要么复用 dir entry
·init_fs 函数
PRIVATE void init_fs()
{
int i;
/* f_desc_table[] */
for (i = 0; i < NR_FILE_DESC; i++)
memset(&f_desc_table[i], 0, sizeof(struct file_desc));
/* inode_table[] */
for (i = 0; i < NR_INODE; i++)
memset(&inode_table[i], 0, sizeof(struct inode));
/* super_block[] */
struct super_block *sb = super_block;
for (; sb < &super_block[NR_SUPER_BLOCK]; sb++)
sb->sb_dev = NO_DEV;
/* open the device: hard disk */
MESSAGE driver_msg;
driver_msg.type = DEV_OPEN;
driver_msg.DEVICE = MINOR(ROOT_DEV);
assert(dd_map[MAJOR(ROOT_DEV)].driver_nr != INVALID_DRIVER);
send_recv(BOTH, dd_map[MAJOR(ROOT_DEV)].driver_nr, &driver_msg);
/* make FS */
mkfs();
/* load super block of ROOT */
read_super_block(ROOT_DEV);
// 将超级块加载进内存
sb = get_super_block(ROOT_DEV);
assert(sb->magic == MAGIC_V1);
// 将 root_inode 加载进内存
root_inode = get_inode(ROOT_DEV, ROOT_INODE);
}
——初始化了【文件描述符表】、【inode 表】、【超级块表】,并将超级块和 root_inode 加载进内存!
·do_close 函数
PUBLIC int do_close()
{
int fd = fs_msg.FD;
put_inode(pcaller->filp[fd]->fd_inode);
pcaller->filp[fd]->fd_inode = 0;
pcaller->filp[fd] = 0;
return 0;
}
——按照文件打开的意义,文件关闭的意义也就是取消关联!
·create_file 函数
PRIVATE struct inode *create_file(char *path, int flags)
{
char filename[MAX_PATH];
struct inode *dir_inode;
if (strip_path(filename, path, &dir_inode) != 0)
return 0;
// 1
int inode_nr = alloc_imap_bit(dir_inode->i_dev);
// 2
int free_sect_nr = alloc_smap_bit(dir_inode->i_dev,
NR_DEFAULT_FILE_SECTS);
// 3(依赖 1、2)
struct inode *newino = new_inode(dir_inode->i_dev, inode_nr,
free_sect_nr);
// 4(依赖 3 )
new_dir_entry(dir_inode, newino->i_num, filename);
return newino;
}
——可以看到:【alloc_imap_bit】——》【alloc_smap_bit】——》【new_inode】——》【new_dir_entry】是呈依赖关系的!
本节小小结:
调用 open API——》发消息给文件系统(带着参数)——》文件系统在开始服务消息之前会先 init_fs——》文件系统 DEV_OPEN 消息调用 do_open 服务——》
do_open 建立关联关系,返回文件句柄,文件打开了——》调用 close API——》发消息给文件系统,请求 do_close 服务关闭文件!
node:
1,数据结构:【文件描述符表】、【文件句柄表】、【inode数组】、【super block数组】
2,从磁盘访问 imap、smap inode_array 、dir entry 的方法:计算所在的扇区,将扇区读入内存,然后在内存修改扇区,再将扇区写入磁盘!
运行:
检查
前面我们都没有用二进制查看器检查我们的磁盘,但是现在关系到文件是否创建成功,所以必须确保正确!
1,先来看 imap
00111111 的意义为:001 111 1 1
1)保留的
2)root_inode
3)三个 tty 的
4)我们创建的文件的
2,看看 smap
首先 2048 / 8 = 0x100 ——》一个文件在 smap 中占据 0x100 个字节
这就好办了,因为我们的二进制一般都是每行 16 字节,所以每 16 行全 1 就是一个文件在 smap 占的位图(16 * 16 = 0x100)!
这是第一个文件占据的 16 行的 1
这是第二个文件占据的 16 行的 1
至于图中的 1 ,那是保留的!
3,看看 inode_array
首先先要恭喜,前面我们通过代码分析第一个数据区的 LBA 应该是 269:
然后我们看看我们的 root_inode 的 i_start_sec:
果然是 269,说明我们前面几节的分析是对的!
然后我们再来详细分析一下 inode_array:
1)mode
(注意这里是 8 进制......)
可以看到对照 8 进制的数值是对的
2)size
目前只有根目录文件有 size,tty 文件 size 为 0 ,我们新建的文件还没有赋值数据,所以 size 为 0!
可以看到此时根目录文件有 5 个 dir entry 【.】、【三个 tty】、【我们新建的文件】,因此 size 应该为 16 * 5 = 80,可以看到,是正确的!
3)i_start_sec
tty 文件的 i_start_sec 是设备号,其余的都是正确的,我们新建的文件和根目录文件差距在 2048 个扇区是正确的!
4,看看 ent_array
ent_array 也就是根目录文件的数据!
从图中我们看到 blah 目录项已经存在于根目录文件中了,但这里有一点需要注意的是:dir_ent 里的 dir_number 是 【第X】 个的意思,例如 TTy1 的 inode 在 inode_array 的第二个,也在 imap 的第二个(imap中第一个不用,所以不考虑在内)!
OK,至此,文件系统的第一个 open 接口算是完成了!