文件系统接口¶
本章问题:文件名、文件描述符和文件内容分别是什么?删除一个名字后,已打开的文件为什么还能读?
学习顺序:
- 从打开、读取、定位和关闭认识文件接口。
- 分清名字、打开实例、文件对象三层引用。
- 按路径逐层查找,理解硬链接与符号链接。
- 检查文件与目录权限,区分可见和持久化。
文件把存储变成什么¶
文件让程序按名字与字节位置访问内容,例如读取文本第100个字节,而不用先告诉程序它位于哪台设备的哪个物理块。
文件名帮助查找,打开后得到的句柄帮助后续操作,文件偏移记录这次访问走到了哪里;这几个信息分别承担不同工作。
磁盘只提供存储位置,程序更希望使用名字和内容。文件把一组数据组织成可命名、可访问的对象,常有类型、大小、所有者、权限和时间等属性。
文件内容可以被视为字节流,也可按固定或变长逻辑记录解释;记录组织由接口和格式约定决定。
- 常见顺序是创建或打开→读写或定位→关闭。
open取得访问对象的句柄,Unix风格中叫文件描述符(fd);read从当前偏移读数据并推进偏移;write写入;seek改变位置;close释放打开引用。- 调用成功与实际处理的字节数要看返回值,不能只按请求长度推断。
名字、打开实例与内容¶
flowchart TB
A["fd a"] --> O1["打开实例1:偏移与打开状态"]
B["fd b:由dup得到"] --> O1
C["fd c:独立open得到"] --> O2["打开实例2:独立偏移"]
O1 --> F["同一文件对象:身份、属性、数据位置"]
O2 --> F
N["目录中的文件名"] -->|"关联文件身份"| F
沿箭头判断谁影响谁:a与b汇入同一打开实例,所以任意一方读取推进的是同一个偏移;c汇入另一个实例,读取不会改变a、b的偏移。三者最后都到同一文件对象,所以看到的文件身份和内容来源相同。
把三层分清:目录项把名字关联到文件身份;文件对象保存身份、属性与存储关系;打开实例保存本次打开的偏移与状态,进程fd再引用它。同一文件可以有多个名字,也可以被独立打开多次。
dup(a)通常让新fd引用a的同一打开实例;独立再 open 则有独立偏移。fork继承的fd也可能共享打开实例。这说明共享偏移要看引用关系,不能只看“是否同一文件”。
例子与推演
例:文件内容为0123456789,a=open(file),b=dup(a),c=open(file),初始偏移全0。
| 操作 | 读到内容 | a、b共享偏移 | c偏移 |
|---|---|---|---|
| a读2字节 | 01 | 2 | 0 |
| b读3字节 | 234 | 5 | 0 |
| c读1字节 | 0 | 5 | 1 |
| b定位到1 | — | 1 | 1 |
| a再读2字节 | 12 | 3 | 1 |
路径怎样找到文件¶
绝对路径从根目录出发,相对路径从当前目录出发;.表示当前目录,..表示父目录。
当前为 /home/u/notes 时,../draft.txt得到 /home/u/draft.txt。系统逐层搜索目录项,并检查路径上每层的搜索权限。
单级目录容易重名,两级目录按用户分组,树形目录允许逐层组织。共享链接会使结构出现图,遍历时需注意重复访问与环。
挂载把另一个文件系统接到一个目录位置,改变该路径下当前可见的内容;底层原有文件通常被遮盖,并未因挂载而删除。
- 顺序访问沿当前位置处理;
- 直接访问按字节或记录编号定位。
- 固定长度记录长 \(L\) 时,第 \(i\) 条记录从 \(iL\) 开始;
- 变长记录则需扫描或索引。
- 索引访问先按键找到记录位置,再读取内容,这个“索引”与下章的磁盘块索引属于不同层次。
练习 1¶
题目
自编题:Unix风格文件内容为0123456789,偏移从0计;读取均完整成功,无并发操作。a=open(file),b=dup(a),c=open(file),两个独立open的初始偏移都为0。依次操作:a读2字节;b读3字节;c读1字节;b绝对定位到偏移1;a再读2字节。
- ① 各次读取内容及最终a、b、c偏移?
- ② 当前目录/home/u/notes时,相对路径../draft.txt解析为什么?
参考解答
解答
- a与b共享打开实例,c有独立实例。
- a先读01,共享偏移到2。
- b读234,共享偏移到5。
- c读0,c的偏移到1。
- b定位到1,把a、b的共享偏移都设为1。
- a再读12,共享偏移到3。
- 最终a、b偏移为3,c偏移为1,单位均为字节。
- 相对路径解析为/home/u/draft.txt。
判分要点
- 须按打开实例移动偏移,不能给dup建立独立偏移。
- 须让b的定位影响a,并保持c不受影响。
- 须从给出的当前目录解释父目录符号。
删除名字后还有什么¶
硬链接是多个目录项指向同一文件身份;符号链接保存目标路径。删除一个硬链接只减少名字引用,其他硬链接和已打开fd仍可访问。符号链接的目标路径若消失,它成为悬空链接,即使文件还有其他名字。
例子与推演
例如a与b是硬链接,s保存路径a,fd已打开a。删除a后,b和fd继续有效,s失效;再删除b,fd仍保留打开引用。
直到没有硬链接、没有打开及其他必要引用,系统才可回收内容。这些结论采用Unix风格对象模型,删除名字与关闭引用是不同操作。
练习 2¶
题目
自编题:采用本章Unix风格对象模型。同一文件有两个硬链接名字a、b。进程已通过a打开文件得到fd,之后删除名字a,名字b和fd都仍存在,文件未被修改。有人说“a删了,fd和b必然马上失效”。
- ① 分别追踪名字、打开实例和文件身份。
- ② 仅关闭fd后能否据此断定数据空间回收?
参考解答
解答
- 删除a只移除一个目录名字;
- b仍指向同一文件身份,已有fd仍引用打开实例,打开实例继续关联原文件,故二者不必失效。
- 关闭fd减少打开引用,但b这个名字仍存在,不能据此断言文件数据空间已经回收。
- 这里依赖题设Unix风格语义,不推广为所有系统的删除规则。
判分要点
- 须分别解释目录项、打开实例、文件对象。
- 须指出硬链接b和打开fd是不同保留关系。
- 须避免把删除一个名字等同于回收文件本体。
权限与共享保证¶
- 目录可以看作“名字到文件身份的表”。
- 列出这张表的名字需要目录读权限;
- 已知名字后在表中寻找条目需要搜索权限;
- 添加或删除条目涉及目录写权限。
- 文件自身读写权限控制内容,因此“能读取文件”与“能删除文件名”可能得到不同答案。
Unix常用r/w/x表示读、写、执行权限,分别给所有者、组和其他人。文件的r允许读内容;目录的r允许列名字,x允许按名字搜索,w影响增删目录项,通常还需目录x。
删除文件主要看父目录权限,不能只看文件本身是否可写。
例子与推演
例如目录710、文件640,Bob属于该组但非所有者。目录组权限只有x,文件组权限有r:知道名字时可读文件,不能列目录,也不能删除条目。
八进制位中r=4、w=2、x=1;ACL可进一步给特定主体列出权限,具体匹配规则按系统约定。
多个程序同时访问时还要确定一致性语义:修改何时对其他读取者可见,偏移是否共享,是否需要锁,以及何时真正持久化到设备。
缓存可见不等于掉电不丢,统一文件接口也不自动保证不同文件系统具有相同同步语义。
练习 3¶
题目
自编题:Unix风格;除以下条件外无额外ACL或限制。Alice拥有目录/d和文件/d/a,两者所属组G。目录权限710,文件640;Bob在G且不是所有者。
- ① Bob知道文件名,能读a、列目录、删除a吗?
- ② Alice建硬链接/d/b和符号链接/d/s→a,并open(a)得fd后依次unlink(a)、unlink(b)。此时s和fd能否访问原文件?何时可回收?
- ③ 在/d挂载另一个文件系统后,原文件数据是否被删除?从新/d路径还能直接找到原b吗?
参考解答
解答
- Bob有目录搜索x但无r、w;
- 文件组权限为r。
- 因此可按已知路径读a,不能列目录或删除条目a。
- 删除a后s保存的目标路径a失效,成为悬空链接。
- fd仍引用原打开实例,删除b后也可继续访问。
- 无硬链接、fd关闭且无其他引用时才可回收。
- 挂载改变该路径下的可见内容,不删除底层数据。
- 原b此前已经被unlink;
- 挂载不会使它重新出现。
- 即使原名字未删除,也会被挂载视图遮盖,不能把“当前路径找不到”都解释为文件本体已回收。
判分要点
- 逐层检查目录搜索与文件读权限,删除看父目录。
- 分别追踪软链接路径、硬链接数、打开引用。
- 注意b先已删除,不能声称卸载后必然恢复b。
记忆要点¶
本章记忆要点
- 文件名用于查找,fd引用打开实例,打开实例保存本次访问偏移。
- dup与继承可能共享打开实例;独立open通常有独立偏移。
- 硬链接指向文件身份;符号链接保存路径,目标路径消失会悬空。
- 删除名字与关闭打开引用分别计数,最后引用消失后才可回收。
- 文件权限管内容,目录权限管列名、搜索和改条目。
- 挂载改变路径视图;修改对别人可见仍需另查持久化保证。