系统优化-IO-基础概念

在讲内存的时候我们提到了buffer用于加速磁盘的读写,cache用于加速文件的读写,那么磁盘和文件它们俩个的区别是什么呢?linux的文件系统的机制又是什么?

磁盘和文件系统

  • 磁盘:是块设备,用于数据的持久化;
  • 文件系统:在磁盘基础上提供管理文件的树状结构;

    文件系统为每个文件都分配了俩种数据结构:

  • 索引节点:【inode】记录文件的原信息,如:数据位置、修改时间、权限等

  • 目录项:【dentry】,多个目录项组成的树状结构就是我们的文件系统,保存,父节点、子节点其他目录项的关联关系、文件名称、inode地址等信息,多个目录项组成了文件树

    整个文件系统由目录项、超级块、索引块、数据块四部分组成,他们的关系如图:

avator

其中,磁盘读写的最小单位是扇区,然而扇区只有512B大小,如果每次都读写这么小的单位,效率一定很低。所以,文件系统又把连续的扇区组成了逻辑块,然后每次都以逻辑块为最小单元,来管理数据。常见的逻辑块大小为 4KB,也就是由连续的 8 个扇区组成。

目录项是缓存在内存中。
索引项是记录磁盘的信息的但是,CPU为了弥补磁盘和内存的访问差异,会将inode也缓存到内存的cache页中。

虚拟文件系统VFS

Liunx在各种文件系统的实现上定义了一层虚拟文件系统【VFS】,它定义了一组所有文件系统都支持的数据结构和标准接口。这样,用户进程和内核中的其他子系统,只需要通过VFS提供的统一接口和磁盘进行交互,而不需要再关心底层各种文件系统的实现细节。

  • 磁盘文件系统:也就是把数据直接存储在计算机本地挂载的磁盘中。常见的 Ext4、XFS、OverlayFS 等,都是这类文件系统。
  • 内存文件系统:这类文件系统,不需要任何磁盘分配存储空间,但会占用内存。我们经常用到的 /proc 文件系统,其实就是一种最常见的虚拟文件系统。此外,/sys 文件系统也属于这一类,主要向用户空间导出层次化的内核对象。
  • 网络文件系统:也就是用来访问其他计算机数据的文件系统,比如 NFS、SMB、iSCSI 等

文件系统的I/O

  • 是否利用标准库缓存的I/O:标准库为了加速I/O的访问效率,会实现一层缓冲,在进行系统调用,比如java里的各种BufferedStream。注意这里的缓冲是标准库实现的
  • 是否利用内存缓存I/O:我们I/O的系统调用,默认都会用到内存的cache页和buffer页,如果采用直接I/O,在I/O操作时候要使用O_DIRECT。
  • 阻塞和非阻塞I/O:如果是阻塞I/O是指应用程序执行 I/O 操作后,如果没有获得响应,就会阻塞当前线程,自然就不能执行其他任务;是指应用程序执行 I/O 操作后,不会阻塞当前的线程,可以继续执行其他的任务,随后再通过轮询或者事件通知的形式,获取调用的结果。访问管道或者网络套接字时,设置O_NONBLOCK标志,就表示用非阻塞方式访问;而如果不做任何设置,默认的就是阻塞访问。
  • 同步I/O和异步I/O:是指应用程序执行 I/O 操作后,要一直等到整个 I/O 完成后,才能获得 I/O 响应。是指应用程序执行 I/O 操作后,不用等待完成和完成后的响应,而是继续执行就可以。等到这次 I/O 完成后,响应会用事件通知的方式,告诉应用程序。

    举个例子,在操作文件时,如果你设置了 O_SYNC 或者 O_DSYNC 标志,就代表同步 I/O。如果设置了 O_DSYNC,就要等文件数据写入磁盘后,才能返回;而 O_SYNC,则是在 O_DSYNC 基础上,要求文件元数据也要写入磁盘后,才能返回。
    再比如,在访问管道或者网络套接字时,设置了 O_ASYNC 选项后,相应的 I/O 就是异步 I/O。这样,内核会再通过 SIGIO 或者 SIGPOLL,来通知进程文件是否可读写。

如何查看I/O

我们可以采用df命令

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

这里的数据是不包括inode的信息的,所以会出现一种情况,明明我们的文件系统还有很大的空间,但是却提示磁盘空间不足了,原因就在于inode是会写入磁盘的,很有可能是小文件太多inode占用了太大的空间,我们可以铜鼓df的-i参数查看inode的信息

如果想查看inode信息

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

缓存

我们CPU为了加速I/O的性能,会将的目录项和索引节点加载到内存中,他们处于内存的内核空间由slab控制,可以通过/proc/slabinfo查看

1
2
3
4
5
6
7
8
9
10
$ 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
...sh
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

主要查看inode_cache、proc_inode_cache、dentry大小。

注意我们如果用下面命令查找文件的时候因为开始没有缓存,会增加dentry、inode_cache、proc_inode_cache用量。

1
find / --name <filename>