块
常见的文件系统,windows上有FAT32、NTFS等,linux上面有ext2、ext3、ext4、xfs等。当然,linux上也支持ntfs,fat32等。这是从操作系统的层面粗略的讲。
另外,fat32常用于U盘、闪卡等介质,现在的U盘等做的越来越好了,所以NTFS的U盘也很常见。
我们知道以前的U盘不能存大文件,也就是说超过4GB的文件存不了。为什么呢?因为fast32是32位的文件系统,没有错,文件系统也分32位64位。ext2也是和fat32属于同时期的产品。最大文件4GB等等各种限制,在64位文件系统中得到突破。所以现在常用的文件系统基本上是ext3 ext4 xfs ntfs这些文件系统是能够满足我们的非商业需求的。
程序在读磁盘数据的过程中,其实在寻道上花的时间是比较长的,其实找到数据位置之后,开始读取数据并不慢。机械磁盘慢,慢就是慢在寻道上。所以为了提高性能,在设计文件系统的时候,要让程序读取合适大小的数据。如果每次读取几个字节,那这显然是不合适的。于是,就把磁盘设备分块,也叫扇区,现在一个扇区基本上是512B。块是这中设备存储的最小单位。除了磁盘之外,SD卡,U盘等也都是块设备。还有一种叫字符设备,如键盘打印机这些。这两种设备就是IO设备中最最常见的两类。
其实在文件系统存储中的角度,这个也太小了,所以windows上会有一个簇的概念,linux对应是块组,它们规定了存储文件的最小单位。一个簇,一般是8个块,这个在格式化文件系统的时候,可以指定。windows上,点击分区(逻辑磁盘)格式化,当然不要真的格式化,有个最小分配单元,有4k、8k、16k、32k、64k可供选择。默认一簇8个块,一个块512B,那么就是4kB。
inode
那么文件是文件系统中,怎么管理的呢?
早期的FAT文件系统,使用一个类似链表的数据结构存储点和块位置的信息。一个块对应一个条目,一个文件有可能有多个块,就会有很多个条目。一个条目大概有2~3B,也就是说一个小文件如果只有一个块,那么实际占用512B+2B。如果有512G的文件,还要额外的2G来存放条目。最关键的是,这些条目要放进内存,这就太可怕了。
所以,后面设计者们改进了所以方式,我们现在熟知的索引节点(inode)的这种方式。
索引节点存放文件的属性和块位置,块指针。当文件载入内存时,先读取inode信息,然后通过块位置读取块数据,如果是大文件块非常多,可以通过块指针访问其他描述信息。这就解决了上面的问题。
另外,inode中还有一个非常重要的成员,就是引用计数。其中硬链接计数i_count,可以实现同一个inode,但不同文件名。删除其中一个文件的时候,就把引用计数减一。如果为0,就从磁盘上删除。额,其实这样说也不严谨,如果在删除的时候,这个文件被一个进程打开了呢?其实还有一个计数 i_writecount,记录着被进程打开的数量,只有这两个同时为0时,才意味着inode被删除。是的,我说的是inode被删除,真正的数据并没有删除,如果不做其他操作,其实块数据还是存放在磁盘上。其实这也是格式化的区别,快速格式化,删除的就是inode,而低级格式化,会把分区上的数据进行置零。
目录在linux上也是一种特殊的文件,可以试一下vim 一个文件夹,看看会发生什么。
日志文件系统
早期的文件系统,每一次持久化,也就是说往磁盘中写入数据的时候,如果有多个任务操作,就会大致大量的线程阻塞在IO上。
后来加入了缓冲区pagecache,缓冲区定时刷数据到磁盘。 但是写与写之间是存在一致性的问题。
于是又加入了日志,和数据库系统的日志一样,文件系统日志会先记录操作信息。当读操作触发的时候,会先访问日志,日志通过最新的操作返回数据。当缓冲区的日志,到时间或者到量的时候,就刷数据到硬盘。
但是如果刷数据到磁盘的过程中失败了怎么办,这就会导致数据的丢失。数据还会出现错乱。
这个时候,又引入了还原点Checkpoint,日志刷入磁盘的时候,不会直接操作目标块,二是会先存入一个Checkpoint区域,输入完毕之后,这个区域标记为绿色。那么从文件系统中读数据的时候,文件系统就可以读这块的数据。如果没有成功,那么标记为红色,就舍弃掉。这样即便出现异常情况,也至于造成数据错乱不可用。
总结
我们可以看到文件系统技术在不断的发展中日趋完善。同业的大佬们也不断的开发出新的功能,满足人们的需求。文件系统其实还用了B+树,bitmap等等数据结构,还有pagechae缓存等,另外磁盘分区表mbr,gpt。后面有时间也会总结一下。
文中如有错误,欢迎指正。