冬天OS(二十八 - 2):文件系统接口(二)

本文深入解析文件系统API的实现机制,包括文件创建、关闭、inode和superblock的管理。通过代码示例,详细介绍了alloc_imap_bit、alloc_smap_bit、new_inode、sync_inode和new_dir_entry等关键函数的作用及流程。

--------------------------------------------------------

创建一个文件并打开(二)

--------------------------------------------------------

上一节我们理清了 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 改变了 */
}
  1. dir entry 是根目录文件的数据,所以增加了 dir entry ,记得更新根目录的 inode 
  2. 有效 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 接口算是完成了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

sssnial-jz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值