inode结点

2019-04-13 21:11发布

一、 机械硬盘

1.1 工作原理

写入时,磁头线圈加电,在周围产生磁场,磁化其下的磁性材料;电流的方向不同,所以磁场的方向也不同,可以表示 0 和 1 的区别。——“电生磁”。 读取时,磁头线圈(不加电)切割磁场线产生感应电流,磁性材料的磁场方向不同,所以产生的感应电流方向也不同。可以表示出 0 和 1 的区别。——“磁生电”。
硬盘保存数据是有时间限制的,当硬盘消磁后,存储在硬盘里的数据就读不出来了。

1.2 扇区

硬盘的基本存储单位扇区sector),每个扇区一般为512byte。一个硬盘往往会有多个盘片,每个盘片分成两面,每面按照同心圆划分为若干个磁道,每个磁道分为若干个扇区
图1 柱面、磁头
图2-1 磁道、扇区(合理)
图2-2 磁道、扇区(不合理)

如图1 所示:具有相同磁道编号的同心圆组成柱面。那么需要知道某个扇区的具体位置,需要知道柱面号磁头号扇区号。每个盘面同心圆的周长不一样。如图2-2 所示,如果按照每个磁道都拥有相同数量的扇区,那么外围的磁道密度肯定比内圈更加稀疏。但是如果不同的磁道扇区数量不同,计算起来就十分麻烦。产商们最终选择了图2-1 所示的方案。为了屏蔽这些复杂的硬件细节,现代的硬盘普遍使用一种叫做 LBAlogical block address)的方式,即整个硬盘中的所有扇区从0开始编号,一直到最后一个扇区。这个扇区编号叫做逻辑扇区号。在最外圈从0开始编号,缘由将在1.3 节给出。 逻辑扇区号抛弃了所有复杂的磁道、盘面之类的概念。当我们给出一个逻辑的扇区号时,硬盘的电子设备会将其转换成实际的盘面、磁道等这些位置。
 在Linux操作系统中,要读取这个文件的前4096个字节时,会使用一个read系统调用来实现。文件系统收到 read 请求之后,判断出文件的前 4096 个字节位于磁盘的 1000 号逻辑扇区到 1007号逻辑扇区。然后文件系统就向磁盘驱动发出一个读取逻辑扇区为 1000 号开始的 8 个扇区的请求,磁盘驱动程序收到这个请求以后就向硬盘发出硬件命令。向硬盘发送 I/O 命令的方式有很多种,最为常见的一种就是通过读写 I/O 寄存器来实现。在 x86 平台上,共有65 536 (12位)个硬件端口寄存器,不同的硬件被分配到不同的 I/O 端口地址。CPU 提供了两条专门的指令 “in” 和 “out” 来实现对硬件端口的读写。 对 IDE 接口来说,它有两个通道,分别为 IDE0 和 IDE1 ,每个通道上可以连接两个设备,分别为 Master 和 Slave ,一个 PC 中最多可以有 4 个 IDE 设备。假设我们的文件位于 IDE0 的 Master 硬盘上,这也是正常情况下硬盘所在的位置。在 PC 中,IDE0 通道的 I/O 端口地址是 0x1F0~0x1F7 及 0x376~0x377。通过读写这些端口地址就能和 IDE 硬盘进行通信。这些端口的作用和操作方式十分复杂,我们就以实现读取 1000 号逻辑扇区开始的 8 个扇区为例:
  • 第0x1F3~0x1F6 4个字节的端口地址是用来写入 LBA 地址的,那么 1000 号的逻辑扇区的 LBA 地址为 0x0000 03E8,所以我们需要往0x1F3、0x1F4 写入 0x00,往 0x1F5 写入0x03,往 0x1F6 写入0xE8。
  • 0x1F2 这个地址是用来写入命令所需要读写的扇区数。比如读取 8 个扇区即写入 8。
  • 0x1F7 这个地址是用来写入要执行的操作的命令码,对于读取操作来说,命令字为 0x20。所以我们要执行的命令为:
out 0x1F3, 0x00 out 0x1F4, 0x00 out 0x1F5, 0x03 out 0x1F6, 0xE8 out 0x1F2, 0x08 out 0x1F7, 0x20 硬盘收到这个命令以后,它就会执行相应的操作,并且将数据读取到事先设置好的内存地址中(这个地址也是通过类似的命令方式设置的)。

1.3 操作系统

硬盘的磁道定义是从外圈往内圈排顺序,最外圈为零磁道,用于存放引导信息。操作系统也是从最外圈开始安装的。外圈磁道相对内圈磁道更长读写数据更稳定

1.4 块 

文件储存在硬盘上,硬盘的最小存储单位叫做"扇区"(Sector)。每个扇区储存512字节(相当于0.5KB)。操作系统读取硬盘的时候,不会一个一个扇区进行读取——效率太低,而是一次性连续读取多个扇区,即一次性读取一个"块"(block)。问题就来了!需要在效率和磁盘利用率之间做一个折中。
由多个扇区组成的"块",是文件存取的最小单位。
文件数据都储存在"块"中,还必须找到一个地方储存文件的元信息(metadata, 就是文件的"属性, 描述信息"),如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做inode(identifier),中文译名为"索引节点"。
图3 磁盘有无可用空间的因素
磁盘格式化创建文件系统时会生成一定数量的inode和block。磁盘空间是否满了,是由两项参数决定的:inode满或是block满,任何一个满了都不能存放数据。

二、 深入理解inode

2.1 概述

disk(磁盘)          memeory(内存)
Linux把inode分为两种方式保存,一种是在硬盘中的inode(d_inode),一种是在内存中的inode(m_inode)。m_inode除了完全包含d_inode中的字段之外还有一些专门的字段。
图4 目录项结构
如图4 所示:每个文件的目录项存储在该文件所属目录中。inode是文件的唯一标识,文件名和inode的对应关系存放在上一级目录的block中;inode所含字段包含指向文件block的指针和文件的属性等,通过block获得文件数据。 
图5 文件存储结构
如图5 所展示的那样,通过inode访问步骤如下:
  1. 在目录文件中找到文件名对应的inode编号;如图3 所示;
  2. 利用inode号,获取inode信息;
  3. 根据inode信息,找到文件数据所在的block,读出数据。
 
图6 访问流程

2.2 inode所含字段 

图7 inode结构示意图
如图 7所示:
  • 字节数。
  • UID、GID。
  • 读、写、执行权限(w-r-x)。
  • ctime:inode最后修改时间;mtime:文件内容最后变动时间;atime:文件最后打开时间。
  • 链接数:有多少文件名指向这个inode。
  • 块(block)的位置(指向文件block的指针)。

2.3 inode大小

inode会消耗硬盘空间。硬盘格式化的时候,操作系统自动将硬盘分成两个区域。一个是数据区(存放文件数据);另一个是inode区(inode table)(存放inode)。 每个inode节点的大小,一般是128byte或256byte。inode节点的总数,在格式化时就给定,一般是每1KB或每2KB就设置一个inode。假定在一块1GB的硬盘中,每个inode节点的大小为128字节,每1KB就设置一个inode,那么inode table的大小就会达到128MB,占整块硬盘的12.8%。

2.4 d_inode

struct d_inode { unsigned short i_mode;//I结点的模式——如读、写、执行权限。 //i_mode一共10位,第一位表明结点文件类型,后9位依次为:I结点所有者、所属组成员、其他成员的权限(权限有读写执行三种)。 unsigned short i_uid; //I结点的user id。 unsigned long i_size; //I结点文件的大小。 unsigned long i_time; //I结点创建时间。 unsigned char i_gid; //I结点的组id。 unsigned char i_nlinks; //表明有多少进程链接到此结点。 unsigned short i_zone[9]; //文件数据对应在磁盘上的位置。 } 这里要说明一点:由于在Linux0.01内核中,数据最终是存放在磁盘上的。而磁盘存放数据的单位是块(block),大小为1KB。对于小于 7K 的文件,文件信息可直接反映在inode中。(I结点字段 i_zone 的前7个元素来存放前7块数据在磁盘上的地址)。对于大文件,inode 通过利用间接块的方式来支持。i_zone[7] 存放的是一级间接块的地址(一级间接块指向512个数据块),i_zone[8]存放的是二级间接块的地址(二级间接块指向512个一级间接块)。
图8 理解进程PCB
正如图8 所展示的那般:Linux最大可以支持(7+512+512×512)KB ≈ 256.51MB 大小的文件。

2.5 m_inode

m_inode 是存放在内存中的I结点,会比 d_inode 多一些字段,具体字段如下: struct m_inode { unsigned short i_mode; unsigned short i_uid; unsigned long i_size; unsigned long i_mtime; unsigned char i_gid; unsigned char i_nlinks; unsigned short i_zone[9]; //以下是m_inode特有的字段 struct task_struct* i_wait; //task_struct是进程pCB结构。I结点的等待队列。 unsigned long i_atime; //文件上次访问时间。 unsigned long i_ctime; //I结点修改时间,注意和i_mtime区别。 unsigned short i_dev; //表明此结点所属的设备号。 unsigned short i_num; //I结点的结点号。 unsigned short i_count; //I结点被打开次数,主要用于判断文件是否共享! unsigned char i_lock; //判断I结点是否被锁住。 unsigned char i_dirt; //判断I结点是否需要写回磁盘。 unsigned char i_pipe; //判断I结点是否为管道文件。 unsigned char i_mount; //如果有文件系统安装在此结点上,则置此位。 //以下字段Linux0.01版本没有用到。 unsigned char i_seek; //在lseek调用时置此位。 unsigned char i_update; };

2.6 软链接、硬链接

正如图1 所展示的,通过文件名(带路径,不带路径极可能重复)在该文件的目录文件中找到其对应的目录项,文件名和inode号是唯一对应关系。凭着inode号在inode table找出对应的inode结点。
图9 软硬链接示意图
正如图9 所展示的,硬链接hardlink.c 和原文件helloa.c 指向都inode1,不管硬链接hardlink.c还是原文件helloa.c,都可以根据inode1访问block块;软链接softlink.c 指向inode2,通过inode2访问链接文件(该文件主要内容是原文件文件名),通过链接文件获得文件名,通过文件名获得inode1;根据inode1的信息,访问block块。链接文件和原文件是依存的关系。 文件是在磁盘上存放的,删除文件并不是真正删除磁盘上的文件,而是将block块对应的inode链接数减一,同时在目录文件中删除目录项。inode引用计数减到0时也不会真正去删除文件,而是等待新文件内容将其覆盖。
软、硬链接不同点:
  1. 硬链接原文件和新文件的inode编号一致。而软链接不一样。
  2. 对原文件删除,会导致软链接不可用,而硬链接不受影响。这就是第一条性质导致的。
软、硬链接相同点:
  1. 对原文件的修改,软、硬链接文件内容也一样的修改,因为都是指向同一个文件内容的。