《ORANGE’S:一个操作系统的实现》读书笔记(三十一)文件系统(六)

本文主要是介绍《ORANGE’S:一个操作系统的实现》读书笔记(三十一)文件系统(六),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

上一篇文章记录了对文件的读写操作,那么文件操作到目前为止,已经完成了创建和读写,还剩下的常用操作就是删除文件了。这篇文章就来记录删除文件的实现以及总结一下为文件系统添加系统调用的步骤。

删除文件

删除是添加的反过程,所以要删除文件,我们需要做以下工作:

  • 释放 inode-map 中的相应位。
  • 释放 sector-map 中的相应位。
  • 删除根目录中的目录项。

注意我们不需要在inode_array中释放相应的i-node,因为释放inode-map中的相应位已经将inode_array中的位置标记为“未使用”了,不过在接下来的代码中我们仍然将i-node清空。

另外从最简单的删除功能出发,我们并不需要释放为文件分配的扇区,因为sector-map中的1和0已经清楚地表明了扇区是否可以使用。

现在我们来看一下删除文件的代码。

代码 fs/link.c,do_unlink,这是新建的文件。

/*** Remove a file.* * @note We clear the i-node in inode_array[] although it is not really needed.*       We don't clear the data bytes so the file is recoverable.* * @return On success, zero is returned.    On error, -1 is returned.*/
PUBLIC int do_unlink()
{char pathname[MAX_PATH];/* get parameters from the message */int name_len = fs_msg.NAME_LEN; /* length of filename */int src = fs_msg.source; /* caller proc nr. */assert(name_len < MAX_PATH);phys_copy((void*)va2la(TASK_FS, pathname),(void*)va2la(src, fs_msg.PATHNAME),name_len);pathname[name_len] = 0;if (strcmp(pathname, "/") == 0) {printl("FS:do_unlink():: cannot unlink the root\n");return -1;}int inode_nr = search_file(pathname);if (inode_nr == INVALID_INODE) { /* file not found */printl("FS::do_unlink()::search_file() returns invalid inode: %s\n", pathname);return -1;}char filename[MAX_PATH];struct inode * dir_inode;if (strip_path(filename, pathname, &dir_inode) != 0) {return -1;}struct inode * pin = get_inode(dir_inode->i_dev, inode_nr);if (pin->i_mode != I_REGULAR) { /* can only remove regular files */printl("cannot remove file %s, because it is not a regular file.\n", pathname);return -1;}if (pin->i_cnt > 1) { /* the file was opened */printl("cannot remove file %s, because pin->i_cnt is %d.\n", pathname, pin->i_cnt);return -1;}struct super_block * sb = get_super_block(pin->i_dev);/* free the bit in i-map */int byte_idx = inode_nr / 8;int bit_idx = inode_nr % 8;assert(byte_idx < SECTOR_SIZE); /* we have only one i-map sector *//* read sector 2 (skip bootsect and superblk): */RD_SECT(pin->i_dev, 2);assert(fsbuf[byte_idx % SECTOR_SIZE] & (1 << bit_idx));fsbuf[byte_idx % SECTOR_SIZE] &= ~(1 << bit_idx);WR_SECT(pin->i_dev, 2);/* free the bits in s-map *//**           bit_idx: bit idx in the entire i-map*     ... ____|____*                  \        .-- byte_cnt: how many bytes between*                   \      |              the first and last byte*        +-+-+-+-+-+-+-+-+ V +-+-+-+-+-+-+-+-+*    ... | | | | | |*|*|*|...|*|*|*|*| | | | |*        +-+-+-+-+-+-+-+-+   +-+-+-+-+-+-+-+-+*         0 1 2 3 4 5 6 7     0 1 2 3 4 5 6 7*  ...__/*      byte_idx: byte idx in the entire i-map*/bit_idx = pin->i_start_sect - sb->n_1st_sect + 1;byte_idx = bit_idx / 8;int bits_left = pin->i_nr_sects;int byte_cnt = (bits_left - (8 - (bit_idx % 8))) / 8;/* current sector nr. */int s = 2 /* 2: bootsect + superblk */+ sb->nr_imap_sects + byte_idx / SECTOR_SIZE;RD_SECT(pin->i_dev, s);int i;/* clear the first byte */for (i = bit_idx % 8; (i < 8) && bits_left; i++, bits_left--) {assert((fsbuf[byte_idx % SECTOR_SIZE] >> i & 1) == 1);fsbuf[byte_idx % SECTOR_SIZE] &= ~(1 << i);}/* clear bytes from the second bytes to the second to last */int k;i = (byte_idx % SECTOR_SIZE) + 1; /* the second byte */for (k = 0; k < byte_cnt; k++, i++, bits_left-=8) {if (i == SECTOR_SIZE) {i = 0;WR_SECT(pin->i_dev, s);RD_SECT(pin->i_dev, ++s);}assert(fsbuf[i] == 0xFF);fsbuf[i] = 0;}/* clear the last byte */if (i == SECTOR_SIZE) {i = 0;WR_SECT(pin->i_dev, s);RD_SECT(pin->i_dev, ++s);}unsigned char mask = ~((unsigned char)(~0) << bits_left);assert((fsbuf[i] & mask) == mask);fsbuf[i] &= (~0) << bits_left;WR_SECT(pin->i_dev, s);/* clear the i-node itself */pin->i_mode = 0;pin->i_size = 0;pin->i_start_sect = 0;pin->i_nr_sects = 0;sync_inode(pin);/* release slot in inode_table[] */put_inode(pin);/* set the inode-nr to 0 in the directory 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; /* including unused slots (the file has been deleted but the slot is still there) */int m = 0;struct dir_entry * pde = 0;int flg = 0;int dir_size = 0;for (i = 0; i < nr_dir_blks; i++) {RD_SECT(dir_inode->i_dev, dir_blk0_nr + i);pde = (struct dir_entry *)fsbuf;int j;for (j = 0; j < SECTOR_SIZE / DIR_ENTRY_SIZE; j++, pde++) {if (++m > nr_dir_entries) {break;}if (pde->inode_nr == inode_nr) {memset(pde, 0, DIR_ENTRY_SIZE);WR_SECT(dir_inode->i_dev, dir_blk0_nr + i);flg = 1;break;}if (pde->inode_nr != INVALID_INODE) {dir_size += DIR_ENTRY_SIZE;}}if (m > nr_dir_entries || /* all entries have been iterated OR */flg) { /* file is found */break;}}assert(flg);if (m == nr_dir_entries) { /* the file is the last one in the dir */dir_inode->i_size = dir_size;sync_inode(dir_inode);}return 0;
}

代码分四部分:

  1. 释放 inode-map 中的相应位;
  2. 释放 sector-map 中的相应位;
  3. 将 inode_array 中的 i-node 清零;
  4. 删除根目录中的目录项。

第46行我们对pin->i_cnt进行了判断,只有当i_cnt等于1时我们才能继续下去。也就是说,此时的i-node应该只被引用了一次。只要i_cnt大于1,函数就直接返回代表不成功的-1,因为此时一定还有别的文件描述符(file descriptor)引用了i-node。这时文件还在使用中,我们当然不能就这样删除它。

由于代码第39行调用了get_inode(),所以一定不要忘记在使用完pin之后调用put_inode()来释放i_cnt,不然inode_table[]中的相应项就永远不会被覆盖,这就造成内存泄漏了。

在删除根目录的目录项时,我们面临一个选择:是让删除后的目录项空洞留在那里还是重新布置根目录。我们选择了前者,因为这样比较省事。这样做的坏处在于,一旦系统中有过文件删除操作,那么根目录文件的i_size就可能无法准备反应根目录中文件的多少。比如下图所示的情况,虽然根目录中连表示自身的“.”都算上也只有7个文件,但根目录文件的i_size却是(7+N)×DIR_ENTRY_SIZE,因为中间有N个之前存在的文件被删掉,此刻留下了大小为N×DIR_ENTRY_SIZE的空洞。

不过这个坏处其实无关紧要,只是要注意两个地方。一是当我们创建文件时,有空洞就先利用空洞,没有任何空洞时再扩展根目录文件的大小;二是空洞之后的文件都已被删除之后,我们最好改变根目录文件的i_size。

好了,删除文件的核心代码有了,我们在task_fs中将删除文件的消息处理添加上,代码如下所示。

代码 fs/main.c,task_fs。

/*** <Ring 1> The main loop of TASK FS.*/
PUBLIC void task_fs()
{
...switch (fs_msg.type) {case OPEN:fs_msg.FD = do_open();break;case CLOSE:fs_msg.RETVAL = do_close();break;case READ:case WRITE:fs_msg.CNT = do_rdwt();break;case UNLINK:fs_msg.RETVAL = do_unlink();break;default:dump_msg("FS::unknown message:", &fs_msg);assert(0);break;}
...
}

接下来,我们来写一个对用户的接口函数,代码如下所示。

代码 lib/unlink.c,unlink(),这是新建的文件。

/*** Delete a file.* * @param pathname  The full path of the file to delete.* * @return Zero if successful, otherwise -1.*/
PUBLIC int unlink(const char * pathname)
{MESSAGE msg;msg.type = UNLINK;msg.PATHNAME = (void*)pathname;msg.NAME_LEN = strlen(pathname);send_recv(BOTH, TASK_FS, &msg);return msg.RETVAL;
}

然后就可以在用户进程中调用它了,代码如下所示。

代码 kernel/main.c,删除文件。

void TestA()
{
...char * filenames[] = {"/foo", "/bar", "/baz"};int i;/* create files */for (i = 0; i < sizeof(filenames) / sizeof(filenames[0]); i++) {fd = open(filenames[i], O_CREAT | O_RDWR);assert(fd != -1);printf("File create: %s (fd %d)\n", filenames[i], fd);close(fd);}char * rfilenames[] = {"/foo", "/bar", "/baz", "/dev_tty0"};/* remove files */for (i = 0; i < sizeof(rfilenames) / sizeof(rfilenames[0]); i++) {if (unlink(rfilenames[i]) == 0) {printf("File removed: %s\n", rfilenames[i]);} else {printf("Failed to remove file: %s\n", rfilenames[i]);}}spin("TestA");
}

现在我们可以make并运行一下,看一下运行结果了,由于我们增加了C文件,所以不要忘记更改Makefile。运行结果如下图所示。

我们创建了“foo”、“bar”和“baz”三个文件,然后又以不同的顺序删除它们。另外我们还试图删除特殊文件“/dev_tty0”,这也被及时地“制止”了。

为文件系统添加系统调用的步骤

如今我们已经有了open()、close()、read()、write()以及unlink()等系统调用,如果对比Linux的系统调用,这显然还不够,不过更多的系统调用也不会有太多的新意了,有兴趣的话,可以自行添加需要的系统调用。为文件系统添加一个系统调用xxx()的步骤大致上有这些:

  1. 定义一种消息,比如MM(可参照 include/const.h 中 UNLINK 的定义方法)。
  2. 写一个函数来处理MM消息(可参照 fs/link.c 中 do_unlink() 的代码)。
  3. 修改task_fs(),增加对消息MM的处理。
  4. 写一个用户接口函数xxx()(可参照 lib/unlink.c 中 unlink() 的代码)。

另外诸如增加函数声明的琐碎事宜,在此不再赘述。

欢迎关注我的公众号


 

公众号中对应文章附有当前文章代码下载说明。

这篇关于《ORANGE’S:一个操作系统的实现》读书笔记(三十一)文件系统(六)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/609986

相关文章

hdu1043(八数码问题,广搜 + hash(实现状态压缩) )

利用康拓展开将一个排列映射成一个自然数,然后就变成了普通的广搜题。 #include<iostream>#include<algorithm>#include<string>#include<stack>#include<queue>#include<map>#include<stdio.h>#include<stdlib.h>#include<ctype.h>#inclu

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

C#实战|大乐透选号器[6]:实现实时显示已选择的红蓝球数量

哈喽,你好啊,我是雷工。 关于大乐透选号器在前面已经记录了5篇笔记,这是第6篇; 接下来实现实时显示当前选中红球数量,蓝球数量; 以下为练习笔记。 01 效果演示 当选择和取消选择红球或蓝球时,在对应的位置显示实时已选择的红球、蓝球的数量; 02 标签名称 分别设置Label标签名称为:lblRedCount、lblBlueCount

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略

Kubernetes PodSecurityPolicy:PSP能实现的5种主要安全策略 1. 特权模式限制2. 宿主机资源隔离3. 用户和组管理4. 权限提升控制5. SELinux配置 💖The Begin💖点点关注,收藏不迷路💖 Kubernetes的PodSecurityPolicy(PSP)是一个关键的安全特性,它在Pod创建之前实施安全策略,确保P

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

C++——stack、queue的实现及deque的介绍

目录 1.stack与queue的实现 1.1stack的实现  1.2 queue的实现 2.重温vector、list、stack、queue的介绍 2.1 STL标准库中stack和queue的底层结构  3.deque的简单介绍 3.1为什么选择deque作为stack和queue的底层默认容器  3.2 STL中对stack与queue的模拟实现 ①stack模拟实现

基于51单片机的自动转向修复系统的设计与实现

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W+,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设计 主要对象是咱们电子相关专业的大学生,希望您们都共创辉煌!✌💗 👇🏻 精彩专栏 推荐订阅👇🏻 单片机