在讲内存的时候我们提到了buffer用于加速磁盘的读写,cache用于加速文件的读写,那么磁盘和文件它们俩个的区别是什么呢?linux的文件系统的机制又是什么?
磁盘和文件系统
- 磁盘:是块设备,用于数据的持久化;
文件系统:在磁盘基础上提供管理文件的树状结构;
文件系统为每个文件都分配了俩种数据结构:
索引节点:【inode】记录文件的原信息,如:数据位置、修改时间、权限等
目录项:【dentry】,多个目录项组成的树状结构就是我们的文件系统,保存,父节点、子节点其他目录项的关联关系、文件名称、inode地址等信息,多个目录项组成了文件树
整个文件系统由目录项、超级块、索引块、数据块四部分组成,他们的关系如图:
其中,磁盘读写的最小单位是扇区,然而扇区只有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 | $ df -h /dev/sda1 |
这里的数据是不包括inode的信息的,所以会出现一种情况,明明我们的文件系统还有很大的空间,但是却提示磁盘空间不足了,原因就在于inode是会写入磁盘的,很有可能是小文件太多inode占用了太大的空间,我们可以铜鼓df的-i参数查看inode的信息
如果想查看inode信息
1 | $ df -i /dev/sda1 |
缓存
我们CPU为了加速I/O的性能,会将的目录项和索引节点加载到内存中,他们处于内存的内核空间由slab控制,可以通过/proc/slabinfo查看
1 | $ cat /proc/slabinfo | grep -E '^#|dentry|inode' |
主要查看inode_cache、proc_inode_cache、dentry大小。
注意我们如果用下面命令查找文件的时候因为开始没有缓存,会增加dentry、inode_cache、proc_inode_cache用量。
1 | find / --name <filename> |