第5章 文件系统的系统调用-2 - Unix操作系统设计-读书笔记

第五章 文件系统的系统调用

5.3系统调用write

算法描述

语法格式:number=write(fd, buffer, count)

这部分和read十分类似,所以没有算法描述,只是将不同点描述出来:

  1. 区别点:如果文件中还没有要写字节偏移量对应的块,核心要用算法alloc分配一个新块并将该块号存放在索引节点内容表的正确位置上;
  2. 当然,特殊情况又出来了,还记得索引节点中的那10个直接索引和间接索引嘛,如果是直接索引,按照上面描述的即可,如果是间接索引,那么就有一点点复杂了;下面通过实例来分析;

实例讲解

  1. 当要写一个文件的字节号为10,240的地方时,很显然这需要用到间接索引;
  2. 算法bmap做映射时,发现:没有对应的块,也没有必要的间接块。
  3. 这时,核心将分配一个磁盘块作为间接块,并将该块号写到内存索引节点中去;
  4. 然后,核心为数据块分配一个磁盘块,并将它的块号写到新分配的间接块的第一个位置上;
  5. 同理,使用2级间接块、3级间接块也是上述流程;

注意事项

思考一个问题,这一过程中需不需要对索引节点加锁?

  1. 如果学过操作系统课程的话,可能比较敏感,即对于可能被多个线程访问的数据,最好都加锁;
  2. 那么我们来讨论下,如果不对索引节点加锁会发生什么呢?
  3. 正如上面的10,240字节,如果有两个进程同时写10240字节处的内容时
  4. 因为没有对索引节点加锁,那么它们均可以操作索引节点里的间接索引,这就可能导致两个进程写数据覆盖;

写整个块还是块的一部分?

  1. 如果是写块的一部分的话,核心必须从磁盘上把该块读进来,以防止改写仍需保持不变的那些部分,这有什么效果?写过程是一块一块进行的鸭,既然最后的单位都是块,那么何必多一次读操作呢?这里没明白;
  2. 如果是写整块,就没必要读,因为其总是要覆盖掉该块先前的内容;

写过程仍然i采用的是延迟写的方法,优缺点在前面章节已经讨论过了;

5.4文件和记录的上锁

这部分没有讨论系统实现,仅仅描述了如何给文件和记录加锁;

文件的加锁是指:防止其他进程读或写整个文件的任何一部分的能力;

记录的加锁是指:防止其他进程读或写特定记录的能力;特定记录是指:文件中两个指定字节之间的部分;

5.5文件的输入/输出位置的调整lseek

语法格式:position=lseek(fd, offset, reference);
fd是文件描述符
offset是一个字节偏移量
reference指出字节偏移量是从哪里开始的,可以是文件的头、文件的当前读/写偏移量位置或文件尾部;其中0表示开头,1表示当前偏移位置,2表示文件尾部
position是返回值,即下次开始读或写的字节偏移量

虽然系统提供了这样的函数,但是我很难想象什么情况下才用到这样奇葩的函数,的确没用过,可能是因为我写的程序几乎都没有操作文件的原因···

该系统调用的实现很简单,仅仅修改文件表中的字节偏移量值;

5.6系统调用close

语法格式:close(fd)

这个函数倒是有点意思,有点意思的原因是因为全局文件表的设计;

  1. 调用该函数的时候,如果文件表项的引用数由于系统调用dup或fork的结果而大于1,那么就意味着还有其他用户引用这个文件表项;这时,核心将引用数减1,关闭操作便完成了;
  2. 如果文件表项的引用计数为1,核心则释放该文件表项,使其变为空,并释放在系统调用open中分配的内存索引节点-使用iput;
  3. 如果还有进程引用该索引节点,核心则使索引节点引用数减1;
  4. 否则,核心归还该索引节点以便再次分配;
  5. 这里需要特别注意的是:文件表项和索引节点都有一个引用计数;

进程退出时,如何操作描述符表?

  • 核心会检查它的活动的用户文件描述符并在内部关闭它们;
  • 因此,没有任何进程在终止运行之后还能保持一个文件打开着;

5.7文件的建立creat

open是存取已存在的文件的过程,creat则在系统中创建一个新文件,语法格式:
fd=creat(pathname, modes);

这里我的吐槽下:这个creat是个锤子单词啊,用create多好啊,非得减去最后一个字母,这不是闲的蛋疼嘛,我透了;

算法描述

[外链图片转存失败(img-euZGzsDv-1567150864220)(en-resource://database/4022:0)]

关键点是:如果文件存在,则清空原文件的内容;

关键点解读

  1. 核心首先调用namei算法,做路径名和索引节点之间的对应;
  2. 在namei到达最后一个分量即要创建的文件名时,namei几下其目录中的第一个空目录槽的字节偏移量,并将该偏移量保存在u区中; 这里我体会到一点,工程实践中,处处透露着小细节,而这些小细节多半涉及缓存,尤其可见缓存有多么的牛逼哄哄;
  3. 上面为什么要保存字节偏移量呢?因为如果核心在目录中没有找到该路径名分量,那么它最会将这个文件名写到刚刚找到的空槽中;
  4. 如果该目录中已没有空槽,核心则要记下目录尾的偏移量,并在那儿建立一个新槽;
  5. 核心还要在u区中记下被查找的目录的索引节点,并锁住该索引节点
  6. 当前的这个目录称为新文件的父目录,此时,核心还不把新文件名写入该目录,这是为了在以后发生错误事件时,可以少做一点儿恢复工作;
  7. 核心还要检查有没有对该目录的写权限;

细节还是很多的;

再来思考个问题:
如果文件不存在,那么需要给其分配一个索引节点,还要将该文件名和索引节点信息写入到父目录中,那么这两个操作哪个先进行呢?

  1. 考虑这个问题的角度在于系统崩溃的随机性,断电也是系统崩溃,如果考虑断电的情况,那么先执行哪个比较好呢?
  2. 如果前者先执行,执行完就断电,那么该目录下是没有该文件的,但是我们浪费了一个索引节点的磁盘空间,但是丝毫不影响系统的正常运行;
  3. 如果后者先执行的话,执行完就断电,那么新建的这个文件引用了一个坏索引节点,为什么是坏索引节点呢?因为还没有将内存版本的索引节点写到磁盘上,索引节点的初始化数据丢失了鸭;

细节就那么多,所以说嘛,系统这个东西,要考虑的东西还是非常非常多的鸭;

5.8特殊文件的建立mknod

还记得什么是特殊文件不?管道、设备文件、目录;

该系统调用的格式:
mknod(pathname, type and permissions, dev);
pathname是要建立的节点名
tyoe及permissions给出节点的类型和要被建立的新文件的访问许可权
dev为块特殊文件和字符特殊文件规定主设备号的次设备号

算法描述

[外链图片转存失败(img-qdRh0yuw-1567150864221)(en-resource://database/4024:0)]
按照上述描述,如果mknod正在创建的是一个目录节点,在系统调用完成后,该节点会存在,但是其内容的格式是不对的,其没有".“和”…"目录表项,习题5.33会考虑将一个目录变成正确格式所需的步骤

5.9改变目录及根chdir,chroot

当通过fork创建一个新进程时,新进程则继承老进程在u区中的当前目录,核心将该索引节点的引用数加1,这里原来也是要加1的鸭;

语法格式:chdir(pathname);

算法描述

[外链图片转存失败(img-U4doHyMM-1567150864223)(en-resource://database/4026:0)]

注意事项

这里面涉及到引用数的操作,而引用数和某个索引节点是否被释放息息相关;

一个进程通常对所有以“/”开始的路径名使用全局文件系统的根。核心中有一个全局变量,指向全局根的索引节点;这个全局变量是系统启动时由iget分配的。进程能够用系统调用chroot改变它们对文件系统的根的概念;

chroot的应用价值在哪里呢?
如果一个用户想模仿一般的文件系统层次结构,并在那里运行进程,用chroot是很有用的,调用格式是:chroot(pathname);

操作完成后,将把它作为进程的根目录,这将意味着在算法namei中,所有从根/开始的路径名的查找都将从这个索引节点开始;

同时也会将其改变了的根传给其新的子进程;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值