1 静态文件与 inode
文件在没有被打开的情况下一般都是存放在磁盘中的,譬如电脑硬盘、移动硬盘、U
盘等外部存储设备,文件存放在磁盘文件系统中,并且以一种固定的形式进行存放,我们把他们称为静态文件。
文件储存在硬盘上,硬盘的最小存储单位叫做“扇区”(Sector
),每个扇区储存
512
字节(相当于
0.5KB
),操作系统读取硬盘的时候,不会一个个扇区地读取,这样效率太低,而是一次性连续读取多个扇区,即一次性读取一个“块”(block
)。这种由多个扇区组成的“块”,是文件存取的最小单位。“块”的大小,最常见的是 4KB
,即连续八个
sector
组成一个
block
。
所以由此可以知道,静态文件对应的数据都是存储在磁盘设备不同的“块”中,那么问题来了,我们在程序中调用 open
函数是如何找到对应文件的数据存储“块”的呢,难道仅仅通过指定的文件路径就可以实现?这里我们就来简单地聊一聊这内部实现的过程。
我们的磁盘在进行分区、格式化的时候会将其分为两个区域,一个是数据区,用于存储文件中的数据; 另一个是 inode
区,用于存放
inode table
(
inode
表),
inode table
中存放的是一个一个的
inode
(也成为
inode节点),不同的 inode
就可以表示不同的文件,每一个文件都必须对应一个
inode
,
inode
实质上是一个结构体,这个结构体中有很多的元素,不同的元素记录了文件了不同信息,譬如文件字节大小、文件所有者、文件对应的读/
写
/
执行权限、文件时间戳(创建时间、更新时间等)、文件类型、文件数据存储的
block
(块)位置等等信息,
(这里需要注意的是,文件名并不是记录在
inode 中)
所以由此可知,inode table
表本身也需要占用磁盘的存储空间。每一个文件都有唯一的一个
inode
,每 一个 inode
都有一个与之相对应的数字编号,通过这个数字编号就可以找到
inode table
中所对应的
inode。
在 Linux
系统下,我们可以通过
"ls -i"
命令查看文件的
inode
编号。 ls 打印出来的信息中,每一行前面的一个数字就表示了对应文件的 inode 编号。除此之外,还可
以使用 stat
命令查看。
通过以上介绍可知,打开一个文件,系统内部会将这个过程分为三步:
1) 系统找到这个文件名所对应的 inode
编号;
2) 通过 inode
编号从
inode table
中找到对应的
inode
结构体;
3) 根据 inode
结构体中记录的信息,确定文件数据所在的
block
,并读出数据。
2 文件打开时的状态
当我们调用 open
函数去打开文件的时候,内核会申请一段内存(一段缓冲区),并且将静态文件的数据内容从磁盘这些存储设备中读取到内存中进行管理、缓存(也把内存中的这份文件数据叫做动态文件、内核缓冲区)。打开文件后,以后对这个文件的读写操作,都是针对内存中这一份动态文件进行相关的操作,而并不是针对磁盘中存放的静态文件。当我们对动态文件进行读写操作后,此时内存中的动态文件和磁盘设备中的静态文件就不同步了,数据的同步工作由内核完成,内核会在之后将内存这份动态文件更新(同步)到磁盘设备中。
因为磁盘、硬盘、U
盘等存储设备基本都是
Flash
块设备,因为块设备硬件本身有读写限制等特征,块设备是以一块一块为单位进行读写的(一个块包含多个扇区,而一个扇区包含多个字节),一个字节的改动也需要将该字节所在的 block
全部读取出来进行修改,修改完成之后再写入块设备中,所以导致对块设备的读写操作非常不灵活;而内存可以按字节为单位来操作,而且可以随机操作任意地址数据,非常地很灵活,所以对于操作系统来说,会先将磁盘中的静态文件读取到内存中进行缓存,读写操作都是针对这份动态文件,而不是直接去操作磁盘中的静态文件,不但操作不灵活,效率也会下降很多,因为内存的读写速率远比磁盘读写快得多。
在 Linux
系统中,内核会为每个进程(关于进程的概念,这是后面的内容,我们可以简单地理解为一个运行的程序就是一个进程,运行了多个程序那就是存在多个进程)设置一个专门的数据结构用于管理该进程,譬如用于记录进程的状态信息、运行特征等,我们把这个称为进程控制块(Process control block
,缩写PCB)。
PCB 数据结构体中有一个指针指向了文件描述符表(
File descriptors
),文件描述符表中的每一个元素索引到对应的文件表(File table
),文件表也是一个数据结构体,其中记录了很多文件相关的信息,譬如文件状态标志、引用计数、当前文件的读写偏移量以及 i-node
指针(指向该文件对应的
inode
)等,进程打开的所有文件对应的文件描述符都记录在文件描述符表中,每一个文件描述符都会指向一个对应的文件表,其示意图如下所示:
前面给大家介绍了 inode
,
inode
数据结构体中的元素会记录该文件的数据存储的
block
块,也就是说可以通过 inode
找到文件数据存在在磁盘设备中的那个位置,从而把文件数据读取出来.