进程(与线程),地址空间和文件,这是操作系统中最重要的概念,如果深入理解了这三个概念,那么就会成为一个操作系统专家 :-)

文件是进程创建的信息逻辑单元,有关文件的构造,命名,存取,使用,保护,实现和管理方法都是操作系统设计的主要内容。操作系统中处理文件的部分称为文件系统。

文件

  • 目录是管理文件系统结构的系统文件
  • 字符特殊文件和输入/输出有关,用于串行I/O类设备
  • 块特殊文件用于磁盘类设备
  • 普通文件分为ASCII文件和二进制文件

文件存取

可以分为顺序存取和随机存取,read和seek

文件操作

与文件有关的常用系统调用:

  • create
  • delete
  • open
  • close
  • read
  • write
  • append
  • seek
  • get attributes
  • set attributes
  • rename

目录

目录操作

不同系统之间的系统调用差别比较大,下面是 Unix 的系统调用:

  • create
  • delete 删除目录,只能删除空目录
  • opendir
  • closedir
  • readdir
  • rename
  • link 指定一个存在的文件和一个路径名,并且建立从该文件到路径所指名字的链接,这样,可以在多个目录中出现同一个文件,这样的链接增加了该文件的i节点计数器的计数(记录含有该文件的目录项数目),成为硬连接
  • unlink 删除目录项,如果被解除链接的文件只出现在一个目录,则从文件系统中删除,如果出现多个目录,则只删除指定路径名的链接,依然保留其他路径名的链接,在 Unix 中,删除文件的系统调用就是 unlink

  • 符号链接:硬连接是使用两个文件名指向同一个内部数据结构来代表一个文件。软连接是所建立的文件名指向了命名另一个文件的小文件。打开时候,文件系统沿着路径,找到末端的名字,使用新名字启动查找进程。

文件系统的实现

文件系统布局

  • 魔数:很多类型的文件,其起始的几个字节的内容是固定的(或是有意填充,或是本就如此)。根据这几个字节的内容就可以确定文件类型,因此这几个字节的内容被称为魔数

多数磁盘划分为一个或者多个分区,每个分区中有一个独立的文件系统。磁盘的0号扇区称为主引导记录MBR,用来引导计算机。MBR结尾是分区表,给出了每个分区的起始和结束地址。表中的一个分区被标记为活动分区。计算机被引导时候,BIOS读入并且执行MBR,MBR做的第一件事情就是确定活动分区,读入它的第一个块,称为引导块,并且执行。引导块中的程序将装载该分区中的操作系统。每个分区都从一个启动块开始。

  • 超级块:包含文件系统的所有关键参数,确定文件系统类型用的魔数,文件系统中数据块的数量以及其他重要的管理信息
  • 空闲块,可以用位图或者指针列表的形式给出
  • i节点,数据结构数组,每个文件一个,说明文件的各种信息
  • 根目录
  • 文件和目录

文件的实现

文件存储的实现关键问题是记录各个文件分别用到哪些磁盘块。

  • 连续分配
    • 优点是简单且具有高的性能
    • 缺点是磁盘会变的零碎
  • 链表分配
    • 每个块的第一个字作为指向下一块的指针,块的其他部分存放数据
    • 优点是充分利用每个磁盘块,不会因为磁盘碎片而浪费空间
    • 但是随机读取非常慢,因为需要遍历链表
    • 而且因为指针占用了一些字节,导致磁盘块存储数据的字节数不再是2的整数次幂
  • 在内存中采用表的链表分配
    • 将磁盘块中的指针字放在内存中的一个表中,称为文件分配表FAT
    • 缺点是需要将整个表放在内存中,200GB和1KB大的块,需要600-800MB内存。。
  • i节点
    • 给每个文件赋予一个i节点的数据结构,列出了文件属性和文件块的磁盘地址,给定i节点,就有可能找到文件的所有块。只有在对应文件打开的时候,i节点才在内存中。
    • 如果文件包含磁盘块的数目超过了i节点所能容纳的数目,则最后一个磁盘地址不指向数据块,而是指向包含附加磁盘地址的磁盘块的地址,所以说有可能找到文件的所有快。

目录的实现

打开文件时,操作系统利用路径名找到目录项,目录项中提供了查找文件磁盘块所需要的信息。目录系统的主要功能是把ASCII文件名映射为定位文件数据所需要的信息。

对于简单目录,是将文件属性直接存放在目录项中,目录中有一个固定大小的目录项列表,每个文件对应一项,其中包含一个文件名,一个文件属性结构以及用以说明磁盘块位置的一个或者多个磁盘地址。

i节点的系统,将文件属性放在i节点中,目录项因此只包含文件名和i节点号。

  • 目录项在行中实现
    • 每个目录项有一个固定的部分,这个固定部分通常以目录项长度开始,后面是固定格式的文件属性数据,这个固定长度的头后面是实际文件名。
    • 缺点和连续磁盘文件一样,另外一个目录项可能会分布在多个页面,读取文件名的时候可能发生页面故障
  • 目录项在堆中实现
    • 目录项自身有固定长度,文件名放置在目录后面的堆中

目录项的查找:目前只能是从头到尾进行搜索,提高速度的方法是每个目录中使用散列表。如果散列表项没有使用,则将指向文件目录项的指针放入,文件目录项链接在散列表后面,如果散列表项被使用了,就构造一个链表,将该链表表头指针放在散列表项中,链接具有相同散列值的文件目录项。

共享文件

文件系统本身将是一个有向无环图而不是一棵树。

如果目录项包含磁盘地址,那么链接文件的时候,要将目录项中文件的磁盘地址也复制给要共享的目录中,那么随后新添加或者删除(导致增加了磁盘地址或者删除了磁盘地址)会导致被共享的其他目录并不能监测到改变,所以这样不行。

  • i节点
    • 磁盘块地址不列入目录,而使用i节点
    • 这样i节点知道目前有多少目录项指向这个文件,但是如果删除的话就有问题了,而且没有办法知道该文件的全部目录项
  • 符号链接
    • 系统建立一个类型为LINK的新文件,放在要共享的目录里,新的文件包含了要链接的文件的路径名,所以读取的时候,会去找要链接的文件的名字。
    • 删除符号链接不会影响本身的文件,问题是需要额外的开销,必须读取包含路径的文件,然后一个部分一个部分的扫描路径,直到找到i节点。

日志结构文件系统

log-structured File System 日志结构文件系统。系统对于一个大部分由零碎的随机写操作组成的任务,也能充分利用磁盘的带宽。基本思想是将整个磁盘结构化为一个日志。每隔一段时间,被缓冲在内存中的所有未决的写操作都被放在一个单独的段中,作为日志末尾一个邻接段写入磁盘。一个单独的段可能会包含i节点,目录块,数据块或者都有。每一个段的开始都是该段的摘要,说明该段中都包含哪些内容。

这样的话要找i节点就比较困难,所以维护了一个由i节点编号索引组成的i节点图。

另外随着时间增长,空间将不够使用,比如出现文件覆盖,其i节点就会指向新的块,所以旧的磁盘块就没用了。

这时候LFS有一个清理线程,周期的扫描日志进行磁盘压缩,首先读取日志的第一个段的摘要,检查有哪些i节点和文件,查看i节点图,判断该i节点是否有效以及文件块是否在使用,如果没有使用,则信息被丢弃,如果仍在使用,则i节点和块就进入内存等待写回到下一个段中。原来的段被标记为空闲,日志就可以用它来存放新的数据。

日志文件系统

日志文件系统中,对于操作会先写一个日志项,然后日志项被写入磁盘,只有当日志项被写入,不同的操作才可以执行。

一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同

文件系统管理和优化

磁盘空间管理

  • 块大小

大的块会造成空间浪费,但是小的块会导致时间浪费。

根据一些调查表明 4KB 的块能更好的适配磁盘数据率(存取速度)和磁盘空间利用率(有无浪费)两个指标,现在磁盘超过了 1TB,将块大小提升到64KB接受磁盘浪费,但是会更快。

  • 记录空闲块
  1. 使用磁盘块链表,每个块中包含尽可能多的空闲磁盘块号,1KB大小块和32位磁盘块号,每个块有255个空闲块的块号
  2. 使用位图,n个块的磁盘需要n位位图
  • 磁盘配额

系统管理员分给每个用户拥有文件和块的最大数量,操作系统确保每个用户不超过分给他们的配额。