磁盘为系统提供了最基本的持久化存储。
  文件系统则在磁盘的基础上,提供了一个用来管理文件的树状结构。

文件系统:索引节点和目录项

  文件系统是对存储设备上的文件,进行组织管理的机制。组织方式不同,就会形成不同的文件系统

  在 Linux 中一切皆文件,不仅普通的文件和目录,就连块设备、套接字、管道等,也都要通过统一的文件系统来管理。

  Linux 文件系统为每个文件都分配两个数据结构,索引节点(index node)和目录项(directory entry),分别记录文件的元信息和目录结构。

  • 索引节点,简称为 inode,用来记录文件的元数据,比如 inode 编号、文件大小、访问权限、修改日期、数据的位置等。索引节点和文件一一对应,它跟文件内容一样,都会被持久化存储到磁盘中。所以记住,索引节点同样占用磁盘空间。
  • 目录项,简称为 dentry,用来记录文件的名字、索引节点指针以及与其他目录项的关联关系。多个关联的目录项,就构成了文件系统的目录结构。不过,不同于索引节点,目录项是由内核维护的一个内存数据结构,所以通常也被叫做目录项缓存。

  索引节点是每个文件的唯一标志,而目录项维护的正是文件系统的树状结构,并且多个目录项可以对应一个索引节点,如:硬链接创建的是目录项,但索引节点不变。文件内容会缓存到 Page Cache 中。

  磁盘读写的最小单位是 512B 的扇区,读写这么小的单位,效率会很低。因此,文件系统把连续的 8 个扇区组成 1 个 4KB 大小的逻辑块,每次以该最小单元管理数据。

  Linux 文件系统的四大基本要素:目录项、索引节点、逻辑块、超级块。

软考文件索引节点法 文件系统索引结构_软考文件索引节点法

虚拟文件系统

  为了支持各种不同的文件系统,Linux 内核在用户进程和文件系统的中间,又引入了一个抽象层:虚拟文件系统 VFS(Virtual File System),定义了一组所有文件系统都支持的数据结构和标准接口。用户进程只需要跟 VFS 提供的统一接口交互,就能和内核中的其他子系统交互,而不需要再关心底层各种文件系统的实现细节。

软考文件索引节点法 文件系统索引结构_软考文件索引节点法_02

  文件系统分类,按照存储位置的不同可分为三类,并且要先挂载到 VFS 目录树中的某个子目录(称为挂载点),然后才能访问其中的文件:

  • 基于磁盘的文件系统,把数据直接存储在计算机本地挂载的磁盘中:Ext4、XFS、OverlayFS 。
  • 基于内存的文件系统,不需要磁盘空间,但会占用内存: /proc 、/sys 文件系统。
  • 基于网络的文件系统,用来访问其他计算机数据的文件系统:NFS、SMB、iSCSI 。

文件系统 I/O

  VFS 提供了一组标准的文件访问接口,这些接口以系统调用的方式,提供给应用程序使用。如:

int open(const char *pathname, int flags, mode_t mode); 
ssize_t read(int fd, void *buf, size_t count); 
ssize_t write(int fd, const void *buf, size_t count);

文件读写方式

  • 1)缓冲与非缓冲 I/O:缓冲指的是标准库内部实现的缓存
  • 缓冲 I/O,是指利用标准库缓存来加速文件的访问,而标准库内部再通过系统调度访问文件。
  • 非缓冲 I/O,是指直接通过系统调用来访问文件,不再经过标准库缓存。
  • 2)直接与非直接 I/O:O_DIRECT 标志,本质上都是和文件系统交互,但有别于裸 I/O(跳过文件系统读写磁盘)
  • 直接 I/O,是指跳过操作系统的页缓存,直接跟文件系统交互来访问文件。
  • 非直接 I/O,是先要经过系统的页缓存,然后再由内核或额外的系统调用,真正写入磁盘。
  • 3)阻塞与非阻塞 I/O:O_NONBLOCK 标志,根据应用程序是否阻塞自身运行
  • 阻塞 I/O,是指应用程序执行 I/O 操作后,如果没有获得响应,就会阻塞当前线程,自然就不能执行其他任务。
  • 非阻塞 I/O,是指应用程序执行 I/O 操作后,不会阻塞当前的线程,可以继续执行其他的任务,随后再通过轮询或者事件通知的形式,获取调用的结果。select/poll 配合使用。
  • 4)同步与异步 I/O:O_SYNC 标志、O_DSYNC 标志,根据是否等待响应结果
  • 同步 I/O,是指应用程序执行 I/O 操作后,要一直等到整个 I/O 完成后,才能获得 I/O 响应。
  • 异步 I/O,是指应用程序执行 I/O 操作后,不用等待完成和完成后的响应,而是继续执行就可以。等到这次 I/O 完成后,响应会用事件通知的方式,告诉应用程序。
  • O_DSYNC,表示要等文件数据写入磁盘后,才能返回;而 O_SYNC,则是在 O_DSYNC 基础上,要求文件元数据也要写入磁盘后,才能返回。
  • O_ASYNC,异步 I/O,当访问管道或者网络套接字时,内核会再通过 SIGIO 或者 SIGPOLL,来通知进程文件是否可读写。
  • 阻塞/非阻塞 和 同步/异步 区别,其实就是两个不同角度的 I/O 划分方式。它们描述的对象也不同,阻塞 / 非阻塞针对的是 I/O 调用者(即应用程序),而同步 / 异步针对的是 I/O 执行者(即系统)。

查看容量

  df 命令,能查看文件系统的磁盘空间使用情况。但有时,你明明碰到空间不足的问题,可是用 df 查看磁盘空间后,却发现剩余空间还有很多。那是因为,除了文件数据,索引节点也占用磁盘空间,索引节点 Inode 的容量在格式化磁盘时是设定好的,空间不足是过多小文件导致的。

$ df -h /dev/sda1 
Filesystem      Size  Used Avail Use% Mounted on 
/dev/sda1        29G  3.1G   26G  11% / 

$ df -i /dev/sda1 
Filesystem      Inodes  IUsed   IFree IUse% Mounted on 
/dev/sda1      3870720 157460 3713260    5% /

查看缓存

  free 输出的 Cache,是页缓存和可回收 Slab 缓存的和,你可以从 /proc/meminfo ,直接得到它们的大小:

$ cat /proc/meminfo | grep -E "SReclaimable|Cached" 
Cached:           748316 kB 
SwapCached:            0 kB 
SReclaimable:     179508 kB

  文件系统中的目录项和索引节点缓存
  内核使用 Slab 机制,管理目录项和索引节点的缓存。/proc/meminfo 只给出了 Slab 的整体大小,具体到每一种 Slab 缓存,还要查看 /proc/slabinfo 这个文件,或使用 slabtop :

$ cat /proc/slabinfo | grep -E '^#|dentry|inode' 
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail> 
xfs_inode              0      0    960   17    4 : tunables    0    0    0 : slabdata      0      0      0 
... 
ext4_inode_cache   32104  34590   1088   15    4 : tunables    0    0    0 : slabdata   2306   2306      0hugetlbfs_inode_cache     13     13    624   13    2 : tunables    0    0    0 : slabdata      1      1      0 
sock_inode_cache    1190   1242    704   23    4 : tunables    0    0    0 : slabdata     54     54      0 
shmem_inode_cache   1622   2139    712   23    4 : tunables    0    0    0 : slabdata     93     93      0 
proc_inode_cache    3560   4080    680   12    2 : tunables    0    0    0 : slabdata    340    340      0 
inode_cache        25172  25818    608   13    2 : tunables    0    0    0 : slabdata   1986   1986      0 
dentry             76050 121296    192   21    1 : tunables    0    0    0 : slabdata   5776   5776      0 

$ slabtop  # 按下 c 按照缓存大小排序,按下 a 按照活跃对象数排序 
Active / Total Objects (% used)    : 277970 / 358914 (77.4%) 
Active / Total Slabs (% used)      : 12414 / 12414 (100.0%) 
Active / Total Caches (% used)     : 83 / 135 (61.5%) 
Active / Total Size (% used)       : 57816.88K / 73307.70K (78.9%) 
Minimum / Average / Maximum Object : 0.01K / 0.20K / 22.88K 

  OBJS ACTIVE  USE OBJ SIZE  SLABS OBJ/SLAB CACHE SIZE NAME 
69804  23094   0%    0.19K   3324       21     13296K dentry 
16380  15854   0%    0.59K   1260       13     10080K inode_cache 
58260  55397   0%    0.13K   1942       30      7768K kernfs_node_cache 
   485    413   0%    5.69K     97        5      3104K task_struct 
  1472   1397   0%    2.00K     92       16      2944K kmalloc-2048