其他¶
FAT文件系统¶


问题1:一个文件系统中(一个磁盘分区)有几个FAT表?
问题2:FAT用到了哪种数据结构?可以用来管理空闲磁盘空间吗?可以的话,如何管理?
问题3:若文件分配方式采取FAT方式(显式链接分配),那么文件控制块(FCB‚目录项)里的物理地址字段中应该保存什么信息?
问题4:如果进程想要访问文件dark.png,假设此时FAT表和该文件的FCB都已经读入内存,那么如果想要访问该文件的第3块,需要执行多少次磁盘I/O操作?如果想要修改第三块呢?
问题5:FAT表中,每个表项的大小对文件系统有什么影响?
问题6:简述操作系统是如何使用FAT表的?
关于FAT的问题答案
- 一个文件系统(磁盘分区)只有一张FAT表,且存放在磁盘中的固定位置,开机后就会读入内存,每个文件在想要修改自己的磁盘块布局时都要修改FAT表;
- 静态链表;可以;如上图,我们可以用-1表示一个文件的最后一块,用-2表示空闲的磁盘块,当需要为文件分配磁盘块时,遍历查找一个表项值为-2的磁盘块分配给它即可;回收磁盘块时,将表项值置为-2;
- 保存起始块号即可;
- 如果只是访问,一次磁盘I/O即可;若需要修改的话,则需要两次磁盘I/O,第一次先读入内存,第二次将修改好的内容写回磁盘;
- 有如下影响:
- 对文件系统支持管理的最大磁盘块数有影响,如:每个FAT表项为16bit,则最多表示
个块号;
- 对FAT表的总大小有影响,每个表项的大小*表项数量 = FAT表的大小;
- 开机后,就会把整个FAT表读入内存,系统运行过程中,FAT表会一直常驻内存,也就是说,查FAT表不需要读磁盘,若进程需要访问某个文件,则需要找到这个文件对应的目录项,从目录项中找到该文件的起始块号,再根据起始块号查FAT表就可以确定后续块号在磁盘中的存放位置了。

- 在该目录文件中,每个目录项都是一个文件的文件控制块(FCB),记录了该文件的相关信息,如果把该表项中除了文件名以外的信息组合起来放进一个结构体文件便构成了索引节点,也就是下面unix系统中的inode结点,此时,目录项中只需要记录文件名和inode编号即可;
- 如何确定目录文件dir1需要占几个磁盘块?
⌈(目录项的数量 * 每个目录项的大小)/磁盘块大小⌉
- 目录文件dir1必须占用连续的磁盘块吗?
不需要,如果目录文件dir1增加新的表项后,原来的磁盘块不够用,那么可以通过遍历FAT表找到一个空闲磁盘块分配给它。根据FAT表我们可以知道dir1文件的最后一块为5号,那么我们还要修改FAT表的5号表项,将它指向新分配的块,新分配的块表项里面填-1;
- 如果现在从根目录开始查找,要将文件“root/dir1/k1.mp3”读入内存,那么这个过程背后发生了什么?
先查内存中的根目录,找到目录项dir1,根据其中记录的dir1文件的起始块号为4,先通过磁盘i/o读入dir1的第一块(4号磁盘块),然后遍历该磁盘块里的目录,尝试找k1.mp3的目录项;如果没找到,再去查FAT表,找到dir1的第二块块号,然后读入5号磁盘块,在里面遍历找到k1.mp3的目录项,根据里面的起始块号知道k1.mp3的第一个块的块号为19,再查FAT表,知道第二块块号为6,因此可以通过磁盘I/O将这两个磁盘块读入内存。
unix文件系统¶

磁盘的inode区介绍¶
Info
inode区介绍:inode就是索引结点,在unix文件系统中,所有的磁盘索引结点集中连续存放,如图所示。每个inode结点大小相同,因此索引结点编号是隐含的,不会显式存放在inode结点里面,此外,用于表示“文件物理结构”的“混合索引表”被包含在inode结点中,通过混合索引表可以找到一个文件中每个块存储的物理位置。
- 在Unix文件系统中,每个文件必须对应一个inode结点,而inode结点的总数量是有上限的。如:该示意图中,仅用两个磁盘块,即8KB作为inode区,假设每个inode大小为64B,则该文件系统最多只能存储8KB/64B=128个inode结点,相应地,该系统最多只能支持128个文件。
- 当一个进程通过open系统调用打开某个文件时,操作系统需要将该文件对应的inode结点读入主存。
磁盘中的根目录介绍¶

- 根目录的信息存储在磁盘的固定位置,这样可以保证开机时,能够找到根目录,再从根目录出发找到其他信息
- 开机时,根目录的内容通常会被加载到内存,并一直常驻内存
- 由于Unix文件系统采用了“索引节点”,因此每个目录项只需包含“文件名、inode号”,文件的具体属性包含在inode中
- 每个目录文件中默认包含两个目录项,分别是“.”和“..”,“.”表示当前目录,“..”表示上一级目录。根目录比较特殊,没有所谓的“上一级目录”,因此它的目录项“..”指向自身

假设在UNIX文件系统下有一进程利用open系统调用打开路径为/dir1/14.md的文件(从根目录开始查找)
- 通过开机常驻内存的根目录找到目录项dir1,根据目录项dir1可知目录文件dir1对应的inode号为1
- 从磁盘的inode区将该inode结点读入内存,通过1号inode结点可以得到目录文件dir1的索引信息,也就知道了目录文件dir1的每一块存储在什么位置,接下来,先读入目录文件dir1的第一块(7号磁盘块),尝试查找目录项14.md,如果没找到,再读入目录文件dir1的第二块(8号磁盘块),找到目录项14.md
- 当找到文件14.md的目录项后,根据目录项记录的7号inode结点,将该inode结点加载进内存专用缓冲区,并且在系统打开文件表中登记表项,指针域记录该inode结点在内存中的物理位置,同时登记其他信息,设置文件打开计数(访问计数)为1,表示当前有一个进程正在使用14.md文件。
- 发起open调用的进程会有进程打开文件表,需要在进程打开文件表中新建一个条目,记录其打开方式以及系统打开文件表索引,最后返回该条目对应的fd。在进程打开文件表中不会记录文件的inode结点,而是用系统打开文件表的索引找到系统打开文件表中对应文件的inode结点。
- 根据open调用返回的fd,即可对该文件进行操作。
硬链接与软链接¶
- 硬链接:基于索引节点的共享方式,在这种共享方式中,文件目录项由文件名和索引节点号构成,需要共享文件时,将用户目录项中的索引节点号保持相同即可(指向同一个索引节点),并且将该索引节点的链接计数+1;
- 软连接:又叫符号链接,共享文件时,目录项指着一个新的索引结点,其索引节点对应的文件类型为软链接文件,里面有一条以绝对路径或相对路径的形式指向其他文件或者目录的引用。
【2009-31】设文件F1的当前引用计数值为1,先建立F1的符号链接(软链接)文件F2,再建立F1的硬链接文件F3,然后删除F1。此时,F2和F3的引用计数值分别是( )。
A. 0、1
B. 1、1
C. 1、2
D. 2、1

用户A创建文件F1,用户B在自己目录下创建F1的硬链接F2,并建立F1的软链接F3。用户C创建F3的硬链接F4,再创建F3的软链接F5。现删除文件F1,下列说法正确的是()
I. F2的计数为1 II. F3的计数为2 III. F4的计数为1 IV.F5的计数为1
V. 文件的现在所有者是用户B
VI. F3可以正常打开文件
VII. F2不能跨文件系统使用,F3可以跨文件系统使用
A. I、II、III、VII B. I、III、IV、V
C. I、V、VI D. I、II、IV、VII
答案:D 。【解析】如图1所示,硬链接和原文件共享一个inode,而软链接是一个新的link文件,有自己inode。因此,本题中一共有三个inode:文件F1的inode1、文件F1的软链接F3的inode2,软链接F3的软链接F5的inode3;inode1由F1,F2共享,原计数值为2,现删除F1,计数值-1,变成1,故I正确;F4是F3的硬链接,故F3和F4共用inode2,所以F3、F4的计数值都是2,II正确、III错误。最后F5的inode3没有其他硬链接,所以计数值为1;文件的所有者owner即使在创建者删除该文件后,只要该inode的计数值不为0,owner都是创建者,因此用户A删除后,inode1的现在所有者还是用户A,例子如下图2;F3是F1的软链接,保存的是F1的地址,当F1删除后,在用户A的对应目录下的目录项被删除,F3自然找不到对应文件,故不能正常打开文件,VI错误;最后,有的文件系统不支持inode,如FAT系统,并且就算是两个支持inode结构的文件系统之间,硬链接也不能跨文件系统使用,因为每个文件系统各自维护自己inode数据结构和列表,故F2不能跨文件系统使用,软链接存的是文件路径,是可以跨文件使用的,VII错误。

open系统调用¶
磁盘索引结点与内存索引结点
¶
Note
- 磁盘索引结点存放在磁盘的inode区中,进程通过检索目录获取文件名对应的目录项,并根据里面的inode号将对应的索引结点载入内存,索引节点里面存放着文件的元数据,里面有着文件类型、存取权限、创建时间、物理地址(直接索引、间接索引)、链接计数/文件大小(总块数)等信息;
- 文件类型:标识该文件是目录/普通文件/软链接等
- 存取权限:控制文件的访问权限
- 物理地址(直接索引、间接索引等):实现逻辑地址到磁盘物理地址的映射
- 链接计数:记录文件被硬链接的次数
- 其他信息:如文件大小、创建时间等辅助信息
- 当磁盘索引结点调入内存(内核专用缓冲区)以后,可以供多个进程共享使用,因此需要增加一些内存属性以便操作系统使用,如
- inode编号:标识对应外存inode的编号
- 状态位:包含修改位(决定是否需要写回外存)
- 互斥锁:保证多进程共享访问时的互斥
- 访问计数:记录文件被打开的次数
- device号:标识inode所在的逻辑设备号
在某些系统的设计中,为了不在内存inode中加入太多信息使其冗余,便建立一张系统级打开文件表,每个表项对应一个已经打开的文件(即inode已经载入内存),表项中保存了inode在内存中的位置信息以及其他附属信息。

各进程虽然可以共享已经装入内存的inode(系统级打开文件表中的表项),但是每个进程对于文件的访问可能有模式/访问速度等各方面的差异,为了记录不同进程对于文件的不同访问情况,操作系统为每个进程创建了一张进程级打开文件表(或文件描述符表/用户级打开文件表),该表的物理地址被记录在进程PCB中,这个表也是在内存中的内核区域(不能在用户态下访问)
假设进程P1首次打开使用fd = open("root/dir1/c.mp4",r),并且该文件之前从未有人打开过,下面便是这个打开文件背后所做的事

Tip
open系统调用必要参数:文件路径,访问权限(如只读、读&写)。打开文件后,文件的读写和关闭都不再以文件路径为参数,而是以打开文件返回的文件标识符fd为参数,即必须打开文件后,才可以进行文件读写和关闭操作。
因此,我们可以总结一下open系统调用的处理过程
- 根据路径查找各级目录,如果在目录缓存中没有命中,就去磁盘中将目录文件读入内存,逐层检索直到找到目标文件的目录项
- 根据目录项,得到inode编号,然后根据该编号,去系统打开文件表中检索
- 如果未命中,需要从磁盘中将该inode结点载入内核专用缓冲区,检查是否逾越访问权限,如果没有,在系统级打开文件表中新增一个表项,访问计数记为1,并将inode在内存中的位置信息记录在inode指针里;如果访问非法,则此次打开失败;
- 如果命中,且没有访问越权,则将访问计数值+1;
- 在进程打开文件表中,新建一个表项,并记录其对应的系统打开文件表索引;
- 将该表项的索引值作为fd返回给用户进程
【2014-29】在一个文件被用户进程首次打开的过程中,操作系统需要做的是( )。
A. 将文件内容读到内存中
B. 将文件控制块读到内存中
C. 修改文件控制块中的读写权限
D. 将文件的数据缓冲区首指针返回给用户进程
各进程读取打开文件的起始位置被记录在(C)
A、外存索引节点中
B、系统级打开文件表中
C、用户级打开文件表中
D、文件对应的目录项中
解析:文件指针用来记录各进程对文件的读写位置被记录在用户级打开文件表的表项中,选 C
open系统调用执行成功之后¶

- 第一次open文件成功之后,会将该文件的inode结点调入内存,并且修改系统打开文件表和进程打开文件表,并且会返回给进程一个fd;
- 之后进程需要访问该文件,只需要根据fd去查找自己的进程打开文件表(该表的起始地址在进程PCB里面),找到对应表项,再去查对应的系统打开文件表的表项,找到目标文件inode结点的物理位置信息,便能够访问该inode结点;
- inode结点里面记录了该文件的磁盘物理地址信息(例如本例,从文件c.mp4的inode我们可以知道,该文件的第一个磁盘块号为6号,第二个磁盘块为16号)
多个进程打开同一个文件¶

多个进程同时打开同一个文件的情况:假设现在有另外一个进程P2,同样的想要打开文件c.mp4。
由于进程P1之前已经打开了文件c.mp4,同时文件c.mp4的inode已经被加载到内存当中,且在系统打开文件表中注册了表项,所以当进程P2打开文件c.mp4的时候,操作系统做的事情就是在进程P2的进程打开文件表当中,新建一个表项。在这个表项当中,会用一个指针指向系统打开文件表。同时,系统打开文件表当中这个文件的访问计数+1。现在c.mp4的访问计数为2,表示此时有两个进程打开了文件入。
值得注意的是,不同的进程在打开文件的时候,它的打开方式可以各不相同。比如在这个例子当中,进程P1是以只读的方式打开文件c.mp4的。而进程P2是以读和写的方式打开这个文件的。打开文件的方式不同,那么接下来这两个进程可以对文件进行的操作就不同。对于进程P1来说,接下来只能去读这个文件。而对于进程Q来说,可以读也可以写这个文件。
总之,在系统打开文件表当中,一个文件只会对应一个表项。如果这个文件被多个进程同时打开,那么会让这些进程的打开文件表去指向系统打开文件表相应的位置。
Tip
多个进程同时打开一个文件时,可以任意对文件进行读写操作,操作系统并不保证写的互斥性,进程可以通过系统调用对文件进行上锁,从而实现对文件内容的保护。
【2017-31】若文件f1的硬链接为f2,两个进程分别打开f1和f2,获得对应的文件描述符为fd1和fd2,则下列叙述中,正确的是( )。
Ⅰ. f1和f2的读写指针位置保持相同
Ⅱ. f1和f2共享同一个内存索引结点
Ⅲ. fd1和fd2分别指向各自的用户打开文件表中的一项
A. 仅Ⅲ
B. 仅Ⅱ、Ⅲ
C. 仅Ⅰ、Ⅱ
D. Ⅰ、Ⅱ和Ⅲ

close系统调用¶
关闭文件的系统调用格式为close(fd),调用成功返回0,失败则返回-1,具体操作如下:
- 根据文件描述符fd找到“进程打开文件表”中对应表项并删除;
- 系统级打开文件表中的对应表项的访问计数(打开计数)-1
- 若-1后不为0,说明还有其他进程正在使用该文件,直接返回即可;
- 若-1后为0,则需要删除该表项,如果对应的内存索引节点没有修改,直接释放即可,若修改可以根据OS的策略决定是否将该inode写回外存;
【2020-23】若多个进程共享同一个文件F,则下列叙述中,正确的是( )。
A. 各进程只能用“读”方式打开文件F
B. 在系统打开文件表中仅有一个表项包含F的属性
C. 各进程的用户打开文件表中关于F的表项内容相同
D. 进程关闭F时,系统删除F在系统打开文件表中的表项
create系统调用¶
目的:在指定路径path下创建一个空文件,可以指定该文件的类型和访问权限等;
步骤:
- 查询磁盘里的inode表,为该文件分配一个空闲的inode,在里面登记文件类型 访问权限 创建时间等信息;
- 打开path所对应的新创建文件所在目录,在该目录中加入一个新的目录项,目录项的文件名从path中截取,inode编号关联到给它新分配的inode上。
delete系统调用¶
目的:删除指定路径path下的目录项,若该目录项对应的inode链接计数为1,则还需要删除该文件的文件实体;
具体步骤:
- 根据路径找到目标文件的目录项,然后找到inode;
- 根据inode里的访问权限判断“删除”操作是否允许(文件保护),若允许则获取其链接计数;
- 若该链接计数为1,则释放该文件所占有的所有磁盘块,同时释放该磁盘inode;
- 若不为1,则将其减1;
- 在目标文件所在目录中,删除其对应的目录项;
进程打开文件表(文件描述符表)的共享与继承¶
在进程那一章节的学习我们说,打开文件作为进程的一种资源,那学习了文件操作之后我们知道这是因为进程拥有进程级打开文件表(文件描述符表),拥有对文件的操作能力;
同一个进程的不同线程共享进程打开文件表,因此每一个线程都可以用相同的文件描述符fd访问相同的文件。
父进程通过fork()系统调用创建子进程时,子进程会继承(复制)父进程的文件描述符表,这意味着子进程也可以访问父进程可以访问的文件,这也是管道通信的基础,父进程通过pipe()系统调用创建管道文件,然后fork()创建子进程,子进程继承该文件描述符,至此,父子进程都可以访问到这个管道文件,借助该管道文件实现进程间通信。
lseek系统调用¶
lseek() 是 Linux/Unix 系统中用于调整文件读写指针(文件偏移量)的系统调用,允许对文件进行随机访问。
- 参数说明:
fd:文件描述符(通过open()或creat()获得)。offset:偏移量(字节数),其意义由whence决定。whence:基准位置,可选值如下:SEEK_SET:文件开头(偏移量直接指定位置)。SEEK_CUR:当前读写位置(偏移量为相对值)。SEEK_END:文件末尾(偏移量为相对末尾的偏移)。
- 返回值:
- 成功时返回新的文件读写指针(文件偏移量(以字节为单位))。
- 失败时返回
-1
使用示例
- 移动文件指针到文件开头
- 移动文件指针到文件末尾
- 相对当前位置移动
- 获取文件大小
- 在文件中间插入数据

read系统调用¶
read 系统调用用于从指定的文件描述符所对应的文件读取数据到用户空间缓冲区。文件描述符可以指向普通文件、设备文件、管道、套接字等支持读取操作的对象。
fd:文件描述符,通常通过open()、pipe()等系统调用获得buf:用户空间缓冲区的指针,用于存储(读/写)读取的数据。count:期望读取的最大字节数- 返回值:若执行成功,返回值代表实际读取的字节数,若失败返回-1.
读/写文件的逻辑区域为以文件读写指针指向位置为起点,长度为count的字节流。
具体处理过程:
- 用户程序 在用户态下调用
read(fd, *buf, count),陷入内核态; - 根据fd找到进程打开文件表(文件描述符表)的对应表项,根据访问权限位判断是否支持读取操作;
- 若支持读操作,再去系统打开文件表,然后找到文件对应的inode,根据读写指针位置,再根据inode里的文件位置信息,去将读区域所在的磁盘块读入内存中的内核缓冲区;
- 将需要读取的数据从内核缓冲区读入用户空间的buf中;
- 返回实际读取的字节数。

Tip
读操作是从当前进程的读写指针所指明的位置开始往后偏移的,比如此时进程P1要读1024个字节。首先根据读写指针的位置和此次要读的数据量,操作系统就可以知道,这一次读的数据一定是被包舍在0号块和1号块当中,根据文件的inode就可以找到这个文件的各个块存放在磁盘的什么位置,接下来,操作系统会把这两个磁盘块都读入到内存,并且是读入到内存的内核缓冲区。然后再把0号块的末尾504字节复制到用户区,接下来再把1号块头部的520字节也复制到用户区。这样就完成了read系统调用。
因为磁盘、硬盘、U盘等存储设备基本都是Flash块设备,因为块设备硬件本身有读写限制等特征,块设备是以一块一块为单位进行读写的(一个块包含多个扇区,而一个扇区包含多个字节), 一个字节的改动也需要将该字节所在的block全部读取出来进行修改,修改完成之后再写入块设备中,所以导致对块设备的读写操作非常不灵活 ;而内存可以按字节为单位来操作,而且可以随机操作任意地址数据,非常地很灵活 ,所以对于操作系统来说,会先将磁盘中的静态文件读取到内存中进行缓存,读写操作都是针对这份动态文件,而不是直接去操作磁盘中的静态文件,不但操作不灵活,效率也会下降很多,因为内存的读写速率远比磁盘读写快得多。
若再让进程P2执行read(0,*buf,1024)

对于进程P2的read系统调用,同样进程P2也要读入1024个字节,根据进程P2的读写指针和此次读的数据量,操作系统可以知道这一次读的数据一定是被包舍在2号块和3号块当中,然后根据inode找到该文件所在的2号和3号块存储在磁盘的位置,再把这两个块读入内存的内核缓冲区,之后复制到用户区。这样也就处理完了进程P2的read系统调用。
可以看到不同的进程,它的读写指针不一样,那这个读写指针指明了要从文件的哪个逻辑地址开始往后读或者写,之后操作系统会根据inode当中记录的信息,把文件的逻辑地址映射为物理地址。最终在磁盘里边找到相应的数据。
write系统调用¶
write 系统调用用于将用户空间缓冲区中的数据写入到指定的文件描述符。文件描述符可以指向普通文件、设备文件、管道、套接字等支持写入操作的对象。
fd:文件描述符,通常通过open()、pipe()等系统调用获得buf:用户空间缓冲区的指针,包含要写入的数据。count:期望写入的字节数- 返回值:若执行成功,返回值代表实际写入的字节数,若失败返回-1.
具体处理过程:
- 用户程序 在用户态下调用
write(fd, *buf, count),陷入内核态; - 根据fd找到进程打开文件表(文件描述符表)的对应表项,根据访问权限位判断是否支持写操作;若进程打开文件时是以只读的方式打开,那么执行write操作是不被允许的;
- 若支持写操作,先将用户空间buf里待写入的数据写到内核缓冲区;再去系统打开文件表,然后找到文件对应的inode,根据读写指针位置和inode里的文件位置信息,将内核缓冲区待写入的数据写到磁盘对应位置;
- 返回实际写入的字节数。

提问:为什么发起read系统调用后,磁盘I/O不能直接将数据从磁盘读入用户空间,而是先读入内核缓冲区再读入用户空间的buf?
镜像问题:为什么发起write系统调用后,需要先将buf里待写入的数据复制到内核缓冲区,然后再通过磁盘I/O写入读写指针所指位置?而不是直接从用户空间写入磁盘?
read:假设进程P1通过read( fd, *buf, count)系统调用请求读入磁盘指定区域数据,那么首先P1会通过trap指令陷入内核态,在read系统调用服务例程中,内核会做这些事:将*buf数组里的数据复制到内核缓冲区,然后开中断(允许设备控制器发出中断请求),查询磁盘状态,若为就绪,发送磁盘读入命令,之后主动阻塞进程P1;
调度器根据调度策略选择进程P2上处理机,P2在cpu上运行的时候,DMA控制器也在同步运行,负责把数据从磁盘读入内核缓冲区,当读入一批数据后,DMA控制器发出中断请求,P2进程响应中断陷入内核态,执行该中断对应的中断服务程序,也就是先判断需要的数据是否全部读入?若没有,则对DMA接口重新初始化,继续启动一轮传送;若已经全部读入,将进程P1唤醒,将其从阻塞态修改为就绪态,之后中断返回;若P1优先级不比P2大,那么P2回到用户态继续运行;

从上面描述的过程我们可以看到,执行中断服务程序的时候,是处于进程P2的地址空间之中,此时是无法将数据直接读入进程P1的用户空间,而内核区域由于被映射到每一个进程的内核空间,因此便可以作为一个通信中介先缓存这部分数据;
等到进程P1被调度上处理机的时候,便可以继续执行read系统调用剩下的事情,也就是把数据从内核缓冲区读入用户空间的buf中了,至此read系统调用执行完毕,返回用户态继续执行。
write系统调用的过程与之类似,不再过多介绍,可以自行推导或者看强化专题IO那边;
真题考察的键盘中断服务大部分过程与此类似,差异点就是键盘输入是程序IO中断方式,而这里介绍的是DMA方式;
文件保护¶
- 用户打开文件时申请的权限不能超过文件允许的权限;
- 用户对文件的操作不能超过自身打开文件时申请的权限;
文件分配¶
借助inode或者FCB,将文件的逻辑地址转换为实际的磁盘物理地址。
