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

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

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

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

上一节我们已经有个文件系统在硬盘上了,这节我们就为我们的文件系统创建新建文件的功能,因为 【文件系统 = 布局 + 操作】,在我们这样的布局下,怎样才能将一个文件写进磁盘,这是我们这节要考虑的内容(设计文件系统 API)...

 

·search_file 函数
PUBLIC int search_file(char *path)
{
	int i, j;

	char filename[MAX_PATH]; /* 【应为 MAX_FILENAME_LEN 】 */
	memset(filename, 0, MAX_FILENAME_LEN);
	struct inode *dir_inode;
	// 【逻辑有问题】
	// 从路径中解析出文件名
	if (strip_path(filename, path, &dir_inode) != 0)
		return 0;

	// 如果是 '/'
	if (filename[0] == 0) /* path: "/" */
		return dir_inode->i_num;

	// 在根目录的目录项中找是否有这个文件
	int dir_blk0_nr = dir_inode->i_start_sect;							   /* 父目录的 dir entry们 所在的起始扇区 */
	int nr_dir_blks = (dir_inode->i_size + SECTOR_SIZE - 1) / SECTOR_SIZE; /* 父目录的 dir entry们 占据多少扇区 */
	int nr_dir_entries = dir_inode->i_size / DIR_ENTRY_SIZE;			   /* 父目录有多少个 dir entry */

	int m = 0;
	struct dir_entry *pde;
	for (i = 0; i < nr_dir_blks; i++) /* 以扇区为单位遍历 */
	{
		RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);
		pde = (struct dir_entry *)fsbuf;						  /* 读出来的都是 dir entry */
		for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++, pde++) /* 以 dir entry 为单位遍历 */
		{
			if (memcmp(filename, pde->name, MAX_FILENAME_LEN) == 0) /* 比较目录项中的名字和参数的名字 */
				return pde->inode_nr;
			if (++m > nr_dir_entries) /* 遍历完了所有的 dir entry */
				break;
		}
		if (m > nr_dir_entries) /* all entries have been iterated */
			break;
	}

	/* file not found */
	return 0;
}

  1. 说是在路径指定的父目录下查找该文件是否存在,其实是从 root 目录查找,也没有判断指定的目录是否存在!
  2. 从参数指定的路径中判断文件是否存在,如果不存在就返回 0 ,如果存在就返回此文件的 inode 号(在此文件 dir entry 中)
·strip_path 函数
PUBLIC int strip_path(char *filename, const char *pathname,
					  struct inode **ppinode)
{
	const char *s = pathname;
	char *t = filename;

	if (s == 0)
		return -1;

	if (*s == '/')
		s++;

	while (*s)
	{ /* check each character */
		if (*s == '/')
			return -1;
		*t++ = *s++;
		/* if filename is too long, just truncate it */
		if (t - filename >= MAX_FILENAME_LEN)
			break;
	}
	*t = 0;

	// 返回根目录文件的 inode 号
	*ppinode = root_inode;

	return 0;
}
  1. 这个函数就是把路径名 '/' 后面的部分取出来,如果后面的部分遇见 '/' 的话就报错(应该要报错的,但是这里逻辑有错误,变为了有 '/' 就创建)
  2. 象征性地返回父目录 inode (假定父目录一定存在)

 

·get_inode 函数
PUBLIC struct inode *get_inode(int dev, int num)
{
	if (num == 0)
		return 0;

	struct inode *p;
	struct inode *q = 0;
	// 每一个在 inode_table 中的 inode 都要知道自己的 inode_number!
	for (p = &inode_table[0]; p < &inode_table[NR_INODE]; p++)
	{
		if (p->i_cnt)
		{ /* not a free slot */
			if ((p->i_dev == dev) && (p->i_num == num))
			{
				/* this is the inode we want */
				p->i_cnt++;
				return p;
			}
		}
		else
		{			   /* a free slot */
			if (!q)	/* q hasn't been assigned yet */
				q = p; /* q <- the 1st free slot */
		}
	}

	if (!q)
		panic("the inode table is full");

	q->i_dev = dev;
	q->i_num = num;
	q->i_cnt = 1;
	// 从硬盘加载 inode 入 inode_table
	struct super_block *sb = get_super_block(dev);
	int blk_nr = 1 + 1 + sb->nr_imap_sects + sb->nr_smap_sects + /* inode 所在的扇区 */
				 ((num - 1) / (SECTOR_SIZE / INODE_SIZE));
	RD_SECT(dev, blk_nr);  /* 读 */
	struct inode *pinode = /* 取 */
		(struct inode *)((u8 *)fsbuf +
						 ((num - 1) % (SECTOR_SIZE / INODE_SIZE)) * INODE_SIZE);
	// 只给内存中的 inode 赋值几个成员
	q->i_mode = pinode->i_mode;
	q->i_size = pinode->i_size;
	q->i_start_sect = pinode->i_start_sect;
	q->i_nr_sects = pinode->i_nr_sects;
	return q;
}

 ——通过 inode 号找 inode,如果 inode 在内存中,直接返回,否则将此 inode 从硬盘加载到内存,因为前面我们已经通过文件名到 dir_entry 中获得了 inode 号,所以之后从 inode_array 中找此 inode 很容易!

 

·do_open 函数
PUBLIC int do_open()
{
	int fd = -1; /* return value */

	char pathname[MAX_PATH];

	// 拷贝进程传递的路径名
	int flags = fs_msg.FLAGS;		/* 访问标志 */
	int name_len = fs_msg.NAME_LEN; /* 文件名的长度 */
	int src = fs_msg.source;		/* 哪个进程要打开文件 */
	assert(name_len < MAX_PATH);	/* 小于的原因是最后一位要放结束字符 */
	phys_copy((void *)va2la(TASK_FS, pathname),
			  (void *)va2la(src, fs_msg.PATHNAME),
			  name_len);
	pathname[name_len] = 0; /* pathname[name_len] 就是第 name_len + 1 个字符 */

	// 在文件描述符指针数组中找到一个没有指向文件描述符的指针
	int i;
	for (i = 0; i < NR_FILES; i++)
	{
		if (pcaller->filp[i] == 0)
		{
			fd = i;
			break;
		}
	}
	if ((fd < 0) || (fd >= NR_FILES)) /* 这里 fd 怎么会 >= NR_FILES 呢??? */
		panic("filp[] is full (PID:%d)", proc2pid(pcaller));

	// 在文件描述符数组中找到一个空闲项
	for (i = 0; i < NR_FILE_DESC; i++)
		if (f_desc_table[i].fd_inode == 0)
			break;
	if (i >= NR_FILE_DESC)
		panic("f_desc_table[] is full (PID:%d)", proc2pid(pcaller));

	// 取得文件的 inode 号,可能返回 0 ,糟糕的是返回 0 有两种情况:
	// 1,文件名中包含了 '/' 2,文件确实不存在
	int inode_nr = search_file(pathname);

	struct inode *pin = 0;
	if (flags & O_CREAT) /* 如果想创建这个文件 */
	{
		if (inode_nr) /* 文件已存在 */
		{
			printl("file exists.\n");
			return -1;
		}
		else /* 文件不存在 */
		{
			pin = create_file(pathname, flags);
		}
	}
	else /* 如果想读写这个文件 */
	{
		assert(flags & O_RDWR);

		char filename[MAX_PATH];
		struct inode *dir_inode;
		if (strip_path(filename, pathname, &dir_inode) != 0)
			return -1;
		// 如果 inode_nr == 0(文件不存在),get_inode 直接返回 0!
		pin = get_inode(dir_inode->i_dev, inode_nr);
	}

	if (pin) /* 到这里内存中的 inode 要么是新创建的,要么从磁盘加载的,要么是原先就在内存的 */
	{
		// 重要的代码
		pcaller->filp[fd] = &f_desc_table[i];
		f_desc_table[i].fd_inode = pin;

		f_desc_table[i].fd_mode = flags;
		f_desc_table[i].fd_pos = 0;

		// 打开
		int imode = pin->i_mode & I_TYPE_MASK;
		if (imode == I_CHAR_SPECIAL)
		{
			// 设备文件
			MESSAGE driver_msg;

			driver_msg.type = DEV_OPEN;
			int dev = pin->i_start_sect;
			driver_msg.DEVICE = MINOR(dev);
			assert(MAJOR(dev) == 4);
			assert(dd_map[MAJOR(dev)].driver_nr != INVALID_DRIVER);

			send_recv(BOTH,
					  dd_map[MAJOR(dev)].driver_nr,
					  &driver_msg);
		}
		else if (imode == I_DIRECTORY) /* 目录文件 */
			assert(pin->i_num == ROOT_INODE);
		else
			assert(pin->i_mode == I_REGULAR); /* 普通文件 */
	}
	else
		return -1;

	return fd;
}

一:OPEN 的动作可以有

  1. 创建并打开:如果文件已经存在,返回错误,如果文件不存在,就创建文件,并将创建的 inode 加载进内存
  2. 读写打开:文件一定要存在,如果不存在,将显示打开失败!


二:函数的流程
首先通过文件名到父目录的目录项数组中查找目录项,如果能在目录项数组中匹配到文件名,就证明文件是存在滴,然后取出 inode 号(文件不存在返回 0 inode 号),inode 号取出来了之后判断是创建还是读写,只有文件不存在才能创建,只有文件存在才能读写!创建和读写都要将 inode 从磁盘加载到内存(如果已经在内存那就直接返回 inode 地址就行,当然创建的时候不可能已经存在于内存),最后建立 【文件句柄】、【文件描述符】、【inode】三者之间的指向关系,建立了关联,就意味着文件已经打开了,返回文件句柄就可以了(设备文件还要做点处理)!


三:注意点

  1. 关于文件名有 '/' 的问题
  2. 不要重复打开一个文件(目前并没有防范机制)
     

OK,第一步先做到这里,剩下的任务下一节完成!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sssnial-jz

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

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

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

打赏作者

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

抵扣说明:

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

余额充值