《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() 的代码)。

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

欢迎关注我的公众号


 

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/624786.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微服务自动化docker-compose

一、docker-compose介绍 Docker Compose是一个用来定义和运行多个复杂应用的Docker编排工具。例如&#xff0c;一个使用Docker容器的微服务项目&#xff0c;通常由多个容器应用组成。那么部署时如何快速启动各个微服务呢&#xff0c;一个个手动启动&#xff1f;假如有上百个微服…

计算机毕业设计 | SpringBoot+vue校园问卷调查系统(附源码)

1&#xff0c;绪论 研究目的 在进入21世纪以后&#xff0c;互联网得到了蓬勃的发展&#xff0c;电子问卷调查也开始逐渐流行起来。传统纸质问卷和电子问卷相比较后&#xff0c;传统问卷还存在很多弊端&#xff1a; 问卷分发起来比较困难&#xff0c;并且分发试卷耗费大量的金…

CSS 基本选择器 复合选择器

文章目录 基本选择器标记选择器类别选择器ID选择器测试基本选择器 复合选择器交集选择器并集选择器后代选择器全选选择器测试复合选择器 基本选择器 准备几个HTML标签用来测试 <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"U…

智能小程序能做什么?

一. 自定义Tab页 涂鸦提供了丰富的场景化、个性化的 ToC 智能服务&#xff0c;不仅可以快速低成本的自由搭建出更多智能服务&#xff0c;还为你提供了基于小程序技术方案的可自主可控的自定义开发链路&#xff0c;为拓展更多品牌化、个性化、差异化智能服务提供生态基础。 我…

Javaweb之SpringBootWeb案例的详细解析

SpringBootWeb案例 前面我们已经讲解了Web前端开发的基础知识&#xff0c;也讲解了Web后端开发的基础(HTTP协议、请求响应)&#xff0c;并且也讲解了数据库MySQL&#xff0c;以及通过Mybatis框架如何来完成数据库的基本操作。 那接下来&#xff0c;我们就通过一个案例&#xf…

word无法插入方程式(方程式反灰)

word无法插入方程式&#xff08;方程式反灰&#xff09; 来自实测>插入方程式&#xff0c;反灰用不了>随便存在哪里&#xff0c;右键看属性&#xff1a;>发现真的是doc&#xff0c;得改成docx才可以&#xff1a;>打开原始档案&#xff0c;另存为word文件即可&#…

什么是google算法?

谷歌算法本身指的是谷歌针对搜索引擎做的规定 要想在别人的地盘玩&#xff0c;那肯定要了解这个地盘的规定&#xff0c;不然做了什么违反了规定&#xff0c;谷歌肯定不会让你继续玩下去 要想做谷歌&#xff0c;那肯定要了解谷歌的算法&#xff0c;然而谷歌的算法也不是一成不变…

ros2 基础学习04 -colcon构建工具

一、colcon构建工具介绍 1.1 构建系统与构建工具 构建系统与构建工具区分点在于针对的对象不同&#xff0c;构建系统只针对一个单独的包进行构建&#xff0c;而构建工具重点在于按照依赖关系依次调用构建系统完成一系列功能包的构建。 1.2 构建系统 构建系统包括CMake、Pyt…

【python】进阶--->MySQL数据库(四)

一、主键约束 primary key : 唯一标识数据库中的每一条记录. 被主键的值唯一 主键列不能为null 每个表应该都要设置主键添加主键约束 在创建表时,直接在字段后面添加主键约束 create table 表名 (字段名 类型(长度) primary key )创建表时,不直接在字段后面添加主键…

ChatGPT网站小蜜蜂AI更新了

ChatGPT网站小蜜蜂AI更新了 前阶段郭震兄弟刚开发小蜜蜂AI网站的的时候&#xff0c;写了一篇关于ChatGPT的网站小蜜蜂AI的博文[https://blog.csdn.net/weixin_41905135/article/details/135297581?spm1001.2014.3001.5501]。今天听说小蜜蜂网站又增加了新的功能——在线生成思…

帆软后台(外观配置-主题)文件上传漏洞

漏洞利用 帆软上传主题获取shell&#xff08;管理系统-外观配置&#xff09; 添加主题上传的压缩包中放入shell.jsp马 &#xff08;没有添加主题功能直接构造数据包&#xff09; POST /WebReport/ReportServer?opfr_attach&cmdah_upload&filenametest.zip&widt…

充电桩发生短路故障时应该如何处理

安科瑞武陈燕acrelcy 一、什么是限流式保护器 ASCP系列电气防火限流式保护器是专门为了保护低压配电线路中短路、过载等问题研发&#xff0c;可以有效克服传统断路器、空气开关和监控设备存在的短路电流大、切断短路电流时间长、短路时产生的电弧火花大&#xff0c;以及使用寿…

【uniapp + uView】仿BOSS直聘三级职位列表实现

1. 效果图 2. 完整代码 <template><view class="search-duty-page"><view class=

虾皮广告数据:​如何利用广告数据优化虾皮(Shopee)销售业绩

在虾皮&#xff08;Shopee&#xff09;平台上&#xff0c;广告数据对于卖家来说是至关重要的&#xff0c;它可以帮助卖家了解广告的效果并进行相应的优化。通过监控和分析这些广告数据&#xff0c;卖家可以更好地理解广告的表现&#xff0c;调整广告策略&#xff0c;提高广告的…

数据结构初阶之排序

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 Linux 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力,共赴大厂。 目录 一.前言 二.选择排序 …

大数据深度学习长短时记忆网络(LSTM):从理论到PyTorch实战演示

文章目录 大数据深度学习长短时记忆网络&#xff08;LSTM&#xff09;&#xff1a;从理论到PyTorch实战演示1. LSTM的背景人工神经网络的进化循环神经网络&#xff08;RNN&#xff09;的局限性LSTM的提出背景 2. LSTM的基础理论2.1 LSTM的数学原理遗忘门&#xff08;Forget Gat…

强烈推荐收藏!LlamaIndex 官方发布高清大图,纵览高级 RAG技术

近日&#xff0c;Llamaindex 官方博客重磅发布了一篇博文《A Cheat Sheet and Some Recipes For Building Advanced RAG》&#xff0c;通过一张图给开发者总结了当下主流的高级RAG技术&#xff0c;帮助应对复杂的生产场景需要。 喜欢记得收藏、点赞、关注。 通俗易懂讲解大…

c语言do while循环语句

c语言do while循环语句 c语言do while循环语句 c语言do while循环语句一、do while循环语句格式二、do while 循环案例 一、do while循环语句格式 do while语句先循环后判断,条件不符合就结束循环&#xff0c;条件符合继续循环 do { 代码 } while(表达式);int main() {int a 0…

KEIL MDK 工程(.uvprojx)目录更改后快速修改方法

概述 在项目过多时&#xff0c;为了方便管理&#xff0c; 需借用文件夹命名来&#xff0c;举例&#xff1a; 1、原工程是在“STM32_Test_Project\MDK-ARM”目录下&#xff0c;我现在创建新文件夹&#xff0c;“Test”&#xff0c;避免原来的受污染&#xff0c;或者好管理等等好…

CompletableFuture原理与用法

CompletableFuture 是由Java8引入的&#xff0c;这让我们编写清晰可读的异步代码变得更加容易&#xff0c;该类功能比Future 更加强大。 什么是 CompletableFuture 在Java中CompletableFuture用于异步编程&#xff0c;异步通常意味着非阻塞&#xff0c;运行任务单独的线程&…