【操作系统】文件管理

文件系统

文件系统的组成

  • 索引节点(index node):文件的唯一标识,记录文件的元信息,比如 inode 编号、文件大小、访问权限、创建时间、修改时间、数据在磁盘的位置等。存储在磁盘。
  • 目录项(directory entry):记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。缓存在内存加速文件的查找。
  • 逻辑块:磁盘读写的最小单位,4KB

磁盘的组成:

  • 超级块:存储文件系统的详细信息,比如块个数、块大小、空闲块等等。当文件系统挂载时进入内存;

  • 索引节点区:存储索引节点;当文件被访问时进入内存;

  • 数据块区:存储文件或目录数据;

虚拟文件系统

Why?

文件系统多种多样,用户操作文件需要统一的接口,加入中间层:虚拟文件系统(Virtual File System,VFS)

文件系统分为三类:

  • 磁盘的文件系统: 数据存储在磁盘中,比如 Ext 2/3/4、XFS 等都是这类文件系统。
  • 内存的文件系统: 数据存储在内存空间,例如 /proc/sys 文件系统,读写这类文件,实际上是读写内核中相关的数据数据。
  • 网络的文件系统: 访问其他计算机主机数据的文件系统,比如 NFS、SMB 等等。

How?

VFS 定义了一组所有文件系统都支持的数据结构和标准接口

文件的使用

How?

  1. 根据路径打开文件,返回文件描述符(文件打开的标识)
  2. write写入数据
  3. 关闭文件

用户读写以字节为单位,文件系统以块为单位

文件的存储

  • 连续空间存放:读写效率很高,前提知道文件的大小,文件头里需要指定「起始块的位置」和「长度」

    • 缺点:「磁盘空间碎片」、「文件长度不易扩展」
  • 非连续空间存放

    • 链表:

      • 隐式链表:文件头要包含「第一块」和「最后一块」的位置,双向链表

        • 缺点:无法直接访问数据块,只能通过指针顺序访问文件
      • 显式链接:数组格式的链表,存储在内存,一个磁盘一个表

        • 提高了检索速度,而且大大减少了访问磁盘的次数。不适用大磁盘。
    • 索引:文件创建一个「索引数据块」,存放指向文件数据块的指针列表

      • 文件的创建、增大、缩小很方便;不会有碎片的问题;支持顺序读写和随机读写;缺点是索引数据块大小有限,不能表示大文件
    • 链式索引块:在索引数据块留出一个存放下一个索引数据块的指针

    • 多级索引块:相当于二级索引

早期 Unix 文件系统是组合了前面的文件存放方式的优点

只用在了 Linux Ext 2/3 文件系统里,虽然解决大文件的存储,但是对于大文件的访问,需要大量的查询,效率比较低。

空闲空间管理

Why?

存放数据放在哪一个空闲的块里,如何找到空闲块?

How?

  • 空闲表法:每行保存空闲区的第一个块号和该空闲区的块个数

    • 只能连续存储,大量小空闲区时效率低
  • 空闲链表法:每一个空闲块里有一个指针指向下一个空闲块

    • 不能随机访问,工作效率低
  • 位图法(Linux采用):bitmap

文件系统的结构

  • 块组:「一个块的位图 + 一系列的块」,外加「一个块的 inode 的位图 + 一系列的 inode 的结构」,128M

  • 超级块:包含文件系统的重要信息,inode 总个数、块总个数、每个块组的 inode 个数、每个块组的块个数等

  • 块组描述符:文件系统中各个块组的状态,比如块组中空闲块和 inode 的数目等,每个块组都包含了文件系统中「所有块组的组描述符信息」。

  • 数据位图和 inode 位图: 用于表示对应的数据块或 inode 是空闲的,还是被使用中。

  • inode 列表,包含了块组中所有的 inode,inode 用于保存文件系统中与各个文件和目录相关的所有元数据。

  • 数据块,包含文件的有用数据。

目录的存储

  • 目录文件的块里面保存的是目录里面一项一项的文件信息

软链接和硬链接

Why?

给某个文件取个别名

How?

  • 硬链接:多个目录项中的「索引节点」指向一个文件(inode),不可用于跨文件系统的

    • 只有删除文件的所有硬链接以及源文件时,系统才会彻底删除该文件
  • 软链接:重新创建一个文件,有独立的 inode,内容是另外一个文件的路径,软链接是可以跨文件系统的

    • 目标文件被删除了,链接文件还是在

文件I/O

缓冲与非缓冲 I/O

  • 缓冲 I/O:利用的是标准库的缓存实现文件的加速访问,而标准库再通过系统调用访问文件。
  • 非缓冲 I/O:直接通过系统调用访问文件,不经过标准库缓存。

直接与非直接 I/O

  • 直接 I/O:不会发生内核缓存和用户程序之间数据复制,而是直接经过文件系统访问磁盘。O_DIRECT标志

  • 非直接 I/O:读操作时,数据从内核缓存中拷贝给用户程序,写操作时,数据从用户程序拷贝给内核缓存,再由内核决定什么时候写入数据到磁盘。

    • 写入时机:缓存的数据太多的时候、调用 sync、内存十分紧张、缓存时间超过某个时间

阻塞与非阻塞 I/O VS 同步与异步 I/O

  • 同步调用:read 调用时,内核将数据从内核空间拷贝到应用程序空间,过程都是需要等待的,也就是说这个过程是同步的

    • 阻塞 I/O:用户程序执行 read 先阻塞,直到内核数据准备好,并把数据从内核缓冲区拷贝到应用程序的缓冲区中,当拷贝过程完成,read 才会返回。
    • 非阻塞 I/O:read 请求在数据未准备好的情况下立即返回,可以继续往下执行,此时应用程序不断轮询内核,直到数据准备好,内核将数据拷贝到应用程序缓冲区,read 调用才可以获取到结果。O_NONBLOCK
    • select I/O 多路复用: 如 select、poll,它是通过 I/O 事件分发,当内核数据准备好时,再以事件通知应用程序进行操作。
    • 同步I/O:read 调用时,内核将数据从内核空间拷贝到用户空间的过程都是需要等待
  • 异步 I/O :「内核数据准备好」和「数据从内核态拷贝到用户态」这两个过程都不用等待。应用程序并不需要主动发起拷贝动作。

I/O 是分为两个过程的:

  1. 数据准备的过程
  2. 数据从内核空间拷贝到用户进程缓冲区的过程

阻塞 I/O 会阻塞在「过程 1 」和「过程 2」,而非阻塞 I/O 和基于非阻塞 I/O 的多路复用只会阻塞在「过程 2」。异步 I/O「过程 1 」和「过程 2 」都不会阻塞。

猜你喜欢

转载自juejin.im/post/7222310732293603387