目录
- 概述
- 用户空间层面
- 1.应用程序可以直接使用内核提供的系统调用访问文件:
- 2.应用程序也可以使用 glibc 库封装的标准 I/O 流函数访问文件:
- 硬件层面
- 1.块设备
- 2.闪存
- 3.NVDIMM
- 内核空间层面
概述
在 Linux 系统中,一切皆文件,除了通常所说的狭义的文件(文本文件和二进制文件)以外,目录、设备、套接字和管道等都是文件。
文件系统在不同的上下文中有不同的含义。
(1)在存储设备上组织文件的方法,包括数据结构和访问方法。
(2)按照某种文件系统类型格式化的一块存储介质。我们常说在某个目录下挂载或卸
载文件系统,这里的文件系统就是这种意思。
(3)内核中负责管理和存储文件的模块,即文件系统模块。
Linux 文件系统的架构如图所示,分为用户空间、内核空间和硬件 3 个层面:
用户空间层面
1.应用程序可以直接使用内核提供的系统调用访问文件:
(1)一个存储设备上的文件系统,只有挂载到内存中目录树的某个目录下,进程才能访问这个文件系统。系统调用 mount 用来把文件系统挂载到内存中目录树的某个目录下。可以执行命令“mount -t fstype device dir”,把文件系统挂载到某个目录下,mount 命令调用系统调用 mount 来挂载文件系统。
(2)系统调用umount 用来卸载某个目录下挂载的文件系统。可以执行命令“umount dir”来卸载文件系统,umount 命令调用系统调用 umount。
(3)使用 open 打开文件。
(4)使用 close 关闭文件。
(5)使用 read 读文件。
(6)使用 write 写文件。
(7)使用 lseek 设置文件偏移。
(8)当我们写文件的时候,内核的文件系统模块把数据保存在页缓存中,不会立即写到存储设备。我们可以使用 fsync 把文件修改过的属性和数据立即写到存储设备,或者使用 fdatasync 把文件修改过的数据立即写到存储设备。
2.应用程序也可以使用 glibc 库封装的标准 I/O 流函数访问文件:
标准 I/O 流提供了缓冲区,目的是尽可能减少调用 read 和 write 的次数,提高性能。glibc 库封装的标准 I/O 流函数如下所示。
(1)使用 fopen 打开流。
(2)使用 fclose 关闭流。
(3)使用 fread 读流。
(4)使用 fwrite 写流。
(5)使用 fseek 设置文件偏移。
(6)使用 fwrite 可以把数据写到用户空间缓冲区,但不会立即写到内核。我们可以使用 fflush 冲刷流,即把写到用户空间缓冲区的数据立即写到内核。
硬件层面
外部存储设备分为块设备、闪存和 NVDIMM 设备3类。
1.块设备
块设备主要有以下两种。
(1)机械硬盘:机械硬盘的读写单位是扇区。访问机械硬盘的时候,需要首先沿着半径方向移动磁头寻找磁道,然后转动盘片找到扇区。
(2)闪存类块设备:使用闪存作为存储介质,里面的控制器运行固化的驱动程序,驱动程序的功能之一是闪存转换层(Flash Translation Layer,FTL),把闪存转换为块设备,对外表现为块设备。常见的闪存类块设备是在个人计算机和笔记本电脑上使用的固态硬盘(Solid State Drives,SSD),以及在手机和平板电脑上使用的嵌入式多媒体存储卡(embedded Multi Media Card,eMMC)和通用闪存存储(Universal Flash Storage,UFS)。
闪存类块设备相对机械硬盘的优势是:访问速度快,因为没有机械操作;抗振性很高,便于携带。
2.闪存
闪存(Flash Memory)的主要特点如下。
(1)在写入数据之前需要擦除一个擦除块,因为向闪存写数据只能把一个位从1变成0,不能从0变成1,擦除的目的是把擦除块的所有位设置为1。
(2)一个擦除块的最大擦除次数有限,NOR 闪存的擦除块的最大擦除次数是104~105,NAND 闪存的擦除块的最大擦除次数是105~106。
闪存按存储结构分为 NAND 闪存和 NOR 闪存,两者的区别如下。
(1)NOR 闪存的容量小,NAND 闪存的容量大。
(2)NOR 闪存支持按字节寻址,支持芯片内执行(eXecute In Place,XIP),可以直接在闪存内执行程序,不需要把程序读到内存中;NAND 闪存的最小读写单位是页或子页,一个擦除块分为多个页,有的 NAND 闪存把页划分为多个子页。
(3)NOR 闪存读的速度比 NAND 闪存块,写的速度和擦除的速度都比 NAND 闪存慢。
(4)NOR 闪存没有坏块;NAND 闪存存在坏块,主要是因为消除坏块的成本太高。
NOR 闪存适合存储程序,一般用来存储引导程序,比如 U-Boot 程序;NAND 闪存适合存储数据。
为什么要针对闪存专门设计文件系统?主要原因如下。
(1)NAND 闪存存在坏块,软件需要识别并且跳过坏块。
(2)需要实现损耗均衡(wear leveling),损耗均衡就是使所有擦除块的擦除次数均衡,避免一部分擦除块先损坏。
机械硬盘和 NAND 闪存的主要区别如下。
(1)机械硬盘的最小读写单位是扇区,扇区的大小一般是 512 字节;NAND 闪存的最小读写单位是页或子页。
(2)机械硬盘可以直接写入数据;NAND 闪存在写入数据之前需要擦除一个擦除块。
(3)机械硬盘的使用寿命比 NAND 闪存长:机械硬盘的扇区的写入次数没有限制;NAND 闪存的擦除块的擦除次数有限。
(4)机械硬盘隐藏坏的扇区,软件不需要处理坏的扇区;NAND 闪存的坏块对软件可见,软件需要处理坏块。
3.NVDIMM
NVDIMM(Non-Volatile DIMM,非易失性内存;DIMM 是 Dual-Inline-Memory-Modules的缩写,表示双列直插式存储模块,是内存的一种规格)设备把 NAND 闪存、内存和超级电容集成到一起,访问速度和内存一样快,并且断电以后数据不会丢失。在断电的瞬间,超级电容提供电力,把内存中的数据转移到 NAND 闪存。
内核空间层面
在内核的目录 fs 下可以看到,内核支持多种文件系统类型。为了对用户程序提供统一的文件操作接口,为了使不同的文件系统实现能够共存,内核实现了一个抽象层,称为虚拟文件系统(Virtual File System,VFS),也称为虚拟文件系统切换(Virtual Filesystem Switch,VFS)。
文件系统分为以下 4 种。
(1)块设备文件系统,存储设备是机械硬盘和固态硬盘等块设备,常用的块设备文件系统是 EXT 和 btrfs(读作|bΛtəfs|)。EXT 文件系统是 Linux 原创的文件系统,目前有3个版本:EXT2、EXT3 和 EXT4。
(2)闪存文件系统,存储设备是 NAND 闪存和 NOR 闪存,常用的闪存文件系统是 JFFS2(日志型闪存文件系统版本 2,Journalling Flash File System version 2)和 UBIFS(无序区块镜像文件系统,Unsorted Block Image File System)。
(3)内存文件系统,文件在内存中,断电以后文件丢失,常用的内存文件系统是 tmpfs,用来创建临时文件。
(4)伪文件系统,是假的文件系统,只是为了使用虚拟文件系统的编程接口,常用的伪文件系统如下所示。
1)sockfs,这种文件系统使得套接字(socket)可以使用读文件的接口 read 接收报文,使用写文件的接口 write 发送报文。
2)proc 文件系统,最初开发 proc 文件系统的目的是把内核中的进程信息导出到用户空间,后来扩展到把内核中的任何信息导出到用户空间,通常把 proc 文件系统挂载在目录“/proc”下。
3)sysfs,用来把内核的设备信息导出到用户空间,通常把 sysfs 文件系统挂载在目录“/sys”下。
4)hugetlbfs,用来实现标准巨型页。
5)cgroup 文件系统,控制组(control group,cgroup)用来控制一组进程的资源,cgroup文件系统使管理员可以使用写文件的方式配置 cgroup。
6)cgroup2 文件系统,cgroup2 是 cgroup 的第二个版本,cgroup2 文件系统使管理员可以使用写文件的方式配置 cgroup2。
访问外部存储设备的速度很慢,为了避免每次读写文件时访问外部存储设备,文件系统模块为每个文件在内存中创建了一个缓存,因为缓存的单位是页,所以称为页缓存。
块设备的访问单位是块,块大小是扇区大小的整数倍。内核为所有块设备实现了统一的块设备层。
为了避免每次读写都需要访问块设备,内核实现了块缓存,为每个块设备在内存中创建一个块缓存。缓存的单位是块,块缓存是基于页缓存实现的。
访问机械硬盘时,移动磁头寻找磁道和扇区很耗时,如果把读写请求按照扇区号排序,可以减少磁头的移动,提高吞吐量。I/O 调度器用来决定读写请求的提交顺序,针对不同的使用场景提供了多种调度算法:NOOP(No Operation)、CFQ(完全公平排队,Complete Fair Queuing)和 deadline(限期)。NOOP 调度算法适合闪存类块设备,CFQ 和 deadline 调度算法适合机械硬盘。
每种块设备需要实现自己的驱动程序。
内核把闪存称为存储技术设备(Memory Technology Device,MTD),为所有闪存实现了统一的 MTD 层,每种闪存需要实现自己的驱动程序。
针对 NVDIMM 设备,文件系统需要实现 DAX(Direct Access,直接访问;X 代表eXciting,没有意义,只是为了让名字看起来酷),绕过页缓存和块设备层,把 NVDIMM设备里面的内存直接映射到进程或内核的虚拟地址空间。
libnvdimm 子系统提供对 3 种 NVDIMM 设备的支持:持久内存(persistent memory,PMEM)模式的 NVDIMM 设备,块设备(block,BLK)模式的 NVDIMM 设备,以及同时支持 PMEM 和 BLK 两种访问模式的 NVDIMM 设备。PMEM 访问模式是把 NVDIMM 设备当作内存,BLK访问模式是把 NVDIMM 设备当作块设备。每种 NVDIMM 设备需要实现自己的驱动程序。