说明
- 硬盘block:硬盘的最小存储单位是"扇区"(Sector),每个扇区512个字节,操作系统操作硬盘时,为了加快效率,将多个连续扇区合并管理,即一次性操作多个扇区,称为一"块"(block),"块"是文件操作的最小单位,最常见的块大小是4KB,即连续八个sector组成一个block。
数据划分
- linux系统把数据分为以下三部分,分别进行管理。
- 文件数据(内容数据)
- 文件信息,也叫元信息,包含文件创建者、创建日期、大小等信息,储存文件元信息的区域就叫做inode,中文译名为"索引节点"。
- 目录结构(目录树形结构),在linux系统为dentry。
文件数据
文件信息(inode)
- 文件的字节数
- 文件拥有者的User ID
- 文件的Group ID
- 文件的读、写、执行权限
- 文件的时间戳,共有三个:ctime指inode上一次变动的时间,mtime指文件内容上一次变动的时间,atime指文件上一次打开的时间。
- 链接数,即有多少文件名指向这个inode
- 文件数据所在block的编号,使用数组存储,block可以是非连续的,不然文件大小无法动态调整,但是不同文件系统,数组大小可能不同,因此支持的文件大小也被其限制。
- …
- 可以用stat命令,查看某个文件的inode信息:
stat example.txt
inode 大小
- 由于保存的数据是固定的,因此每个inode节点的大小也是固定的,一般是128字节或256字节。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。
- 假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。
- 查看每个硬盘分区的inode总数和已经使用的数量,可以使用df命令。
df -i
sudo dumpe2fs -h /dev/hda | grep "Inode size"
- 由于每个文件都必须有一个inode,因此有可能发生inode已经用光,但是硬盘还未存满的情况。这时,就无法在硬盘上创建新文件。
inode 编号
- 每个inode节点都有一个编号,操作系统内部使用编号来操作不同的文件,而不是通过文件名。
- 使用ls -i命令,可以看到文件名对应的inode号码:
ls -i example.txt
- inode编号可能就是序号,例如:第一个inode节点,第二个inode节点,因此系统可以通过该编号和inode大小计算出偏移来定位到硬盘上的inode数据。
目录结构(dentry)
- 每个目录对应一个dentry节点。
- 目录文件的结构比较简单,就是子文件和子目录列表;每个目录项,主要由两部分组成:
- 文件名。
- 该文件对应的inode号码。
ls /etc
- ls -i命令列出整个目录文件,即文件名和inode号码:
ls -i /etc
- 如果要查看文件的详细信息,就必须根据inode号码,访问inode节点,读取信息。ls -l命令列出文件的详细信息。
ls -l /etc
存储形态
- 文件数据和文件信息都会保存在硬盘上,操作系统会将硬盘分成两个区域。一个是数据区,存放文件数据;另一个是inode区(inode table),存放inode所包含的信息。
- linux并没有使用单独的硬盘区域来保存目录结构数据,因为其将每个目录当做特殊的文件存储,每个目录文件也有对应的inode节点。
内存形态
- 在内存中,inode, dentry和文件数据都会进行缓存。
- 每个文件和目录都需要一个inode和一个dentry空间,无法完全在内存中缓存这些数据,linux采用的是按需创建维护LRU(Least Recently Used)列表,释放掉没使用的dentry项与inode项。
读写流程
- 根据文件名,通过目录结构的对应关系,找到文件对应的Inode number
- 通过文件名,根据保存在内存中的目录结构(dentry)缓存,进行查找其inode number,如果不在缓存中,则可通过根目录或者当前目录进行遍历访问。
- 再根据Inode number读取到文件的Inode table
- 再根据inode number从硬盘inode区域中找到对应的inode节点,
- 再根据Inode table中的Pointer读取到相应的Blocks
- 再根据inode节点中的block块编号,映射为地址偏移,访问其真正的文件内容数据