摘要:本文目的是分析在linux系统中如何实现新的文件系统。在介绍文件系统具体实现前先介绍文件系统的概念和作用,抽象出了文件系统概念模型。熟悉文件系统的内涵后,我们再近一步讨论Linux系统中和文件系统的特殊风格和具体文件系统在Linux中组成结构,为读者勾画出Linux中文件系统工作的全景图。最后,我们再通过Linux中最简单的Romfs作实例分析实现文件系统的普遍步骤。(我们假定读者已经对Linux文件系统初步了解)
什么是文件系统
首先要谈的概念就是什么是文件系统,它的作用到底是什么。
文件系统的概念虽然许多人都认为是再清晰不过的了,但其实我们往往在谈论中或多或少地夸大或片缩小了它的实际概念(至少我时常混淆),或者说,有时借用了其它概念,有时说的又不够全面。
比如在操作系统中,文件系统这个术语往往既被用来描述磁盘中的物理布局,比如有时我们说磁盘中的“文件系统”是EXT2或说把磁盘格式化成FAT32格式的“文件系统”等——这时所说的“文件系统”是指磁盘数据的物理布局格式;另外,文件系统也被用来描述内核中的逻辑文件结构,比如有时说的“文件系统”的接口或内核支持Ext2等“文件系统”——这时所说的文件系统都是内存中的数据组织结构而并非磁盘物理布局。还有些时候说“文件系统”负责管理用户读写文件——这时所说的“文件系统”往往描述操作系统中的“文件管理系统”,也就是文件子系统。
虽然上面我们列举了混用文件系统的概念的几种情形,但是却也不能说上述说法就是错误的,因为文件系统概念本身就囊括众多概念,几乎可以说在操作系统中自内存管理、系统调度到I/O系统、设备驱动等各个部分都和文件系统联系密切,有些部分和文件系统甚至未必能明确划分——所以不能只知道文件系统是系统中数据的存储结构,一定要全面认识文件系统在操作系统中的角色,才能具备自己开发新文件系统的能力。
为了澄清文件系统的概念,必须先来看看文件系统在操作系统中处于何种角色,分析文件系统概念的内含外延。所以我们先抛开Linux文件系统的实例,而来看看操作系统中文件系统的普遍体系结构,从而增强对文件系统的理论认识。
下面以软件组成的结构图[1]的方式描述文件系统所涉及的内容。
我们针对各层做以简要分析:
首先我们来分析最低层——设备驱动层,该层负责与外设——磁盘等——通讯。基于磁盘的文件系统都需要和存储设备打交道,而系统操作外设离不开驱动程序。所以内核对文件的最后操作行为就是调用设备驱动程序完成从主存(内存)到辅存(磁盘)的数据传输。文件系统相关的多数设备都属于块设备,常见的块设备驱动程序有磁盘驱动,光驱驱动等,之所以称它们为块设备,一个原因是它们读写数据都是成块进行的,但是更重要的原因是它们管理的数据能够被随机访问——不需要向字符设备那样必须顺序访问。
设备驱动层的上一层是物理I/O层,该层主要作为计算机外部环境和系统的接口,负责系统和磁盘交换数据块。它要知道据块在磁盘中存储位置,也要知道文件数据块在内存缓冲中的位置,另外它不需要了解数据或文件的具体结构。可以看到这层最主要的工作是标识别磁盘扇区和内存缓冲块[2]之间的映射关系。
再上层是基础I/O监督层,该层主要负责选择文件 I/O需要的设备,调度磁盘请求等工作,另外分配I/O缓冲和磁盘空间也在该层完成。由于块设备需要随机访问数据,而且对速度响应要求较高,所以操作系统不能向对字符设备那样简单、直接地发送读写请求,而必须对读写请求重新优化排序,以能节省磁盘寻址时间,另外也必须对请求提交采取异步调度(尤其写操作)的方式进行。总而言之,内核对必须管理块设备请求,而这项工作正是由该层负责的。
倒数第二层是逻辑I/O层,该层允许用户和应用程序访问记录。它提供了通用的记录(record)I/O操作,同时还维护基本文件数据。由于为了方便用户操作和管理文件内容,文件内容往往被组织成记录形式,所以操作系统为操作文件记录提供了一个通用逻辑操作层。
和用户最靠近的是访问方法层,该层提供了一个从用户空间到文件系统的标准接口,不同的访问方法反映了不同的文件结构,也反映了不同的访问数据和处理数据方法。这一层我们可以简单地理解为文件系统给用户提供的访问接口——不同的文件格式(如顺序存储格式、索引存储格式、索引顺序存储格式和哈希存储格式等)对应不同的文件访问方法。该层要负责将用户对文件结构的操作转化为对记录的操作。
对比上面的层次图我们再来分析一下数据流的处理过程,加深对文件系统的理解。
假如用户或应用程序操作文件(创建/删除),首先需要通过文件系统给用户空间提供的访问方法层进入文件系统,接着由使用逻辑I/O层对记录进行给定操作,然后记录将被转化为文件块,等待和磁盘交互。这里有两点需要考虑——第一,磁盘管理(包括再磁盘空闲区分配文件和组织空闲区);第二,调度块I/O请求——这些由基础I/O监督层的工作。再下来文件块被物理I/O层传递给磁盘驱动程序,最后磁盘驱动程序真正把数据写入具体的扇区。至此文件操作完毕。
当然上面介绍的层次结构是理想情况下的理论抽象,实际文件系统并非一定要按照上面的层次或结构组织,它们往往简化或合并了某些层的功能(比如Linux文件系统因为所有文件都被看作字节流,所以不存在记录,也就没有必要实现逻辑I/O层,进而也不需要在记录相关的处理)。但是大体上都需要经过类似处理。如果从处理对象上和系统独立性上划分,文件系统体系结构可以被分为两大部分:——文件管理部分和操作系统I/O部分。文件管理系统负责操作内存中文件对象,并按文件的逻辑格式将对文件对象的操作转化成对文件块的操作;而操作系统I/O部分负责内存中的块与物理磁盘中的数据交换。
数据表现形式再文件操作过程中也经历了几种变化:在用户访问文件系统看到的是字节序列,而在字节序列被写入磁盘时看到的是内存中文件块(在缓冲中),在最后将数据写入磁盘扇区时看到的是磁盘数据块[3]。
本文所说的实现文件系统主要针对最开始讲到第二种情况——内核中的逻辑文件结构(但其它相关的文件管理系统和文件系统磁盘存储格式也必须了解),我们用数据处理流图来分析一下逻辑文件系统主要功能和在操作系统中所处的地位。
其中文件系统接口与物理布局管理是逻辑文件系统要负责的主要功能。
文件系统接口为用户提供对文件系统的操作,比如open、close、read、write和访问控制等,同时也负责处理文件的逻辑结构。
物理存储布局管理,如同虚拟内存地址转化为物理内存地址时,必须处理段页结构一样,逻辑文件结构必须转化到物理磁盘中,所以也要处理物理分区和扇区的实际存储位置,分配磁盘空间和内存中的缓冲也要在这里被处理。
所以说要实现文件系统就必须提供上面提到的两种功能,缺一不可。
在了解了文件系统的功能后,我们针对Linux操作系统分析具体文件系统如何工作,进而掌握实现一个文件系统需要的步骤。
Linux 文件系统组成结构
Linux 文件系统的结构除了我们上面所提到的概念结构外,最主要有两个特点,一个是文件系统抽象出了一个通用文件表示层——虚拟文件系统或称做VFS。另外一个重要特点是它的文件系统支持动态安装(或说挂载、登陆等),大多数文件系统都可以作为根文件系统的叶子接点被挂在到根文件目录树下的子目录上。另外Linux系统在文件读写的I/O操作上也采取了一些先进技术和策略。
我们先从虚拟文件系统入手分析linux文件系统的特性,然后介绍有关文件系统的安装、注册和读写等概念。
虚拟文件系统
虚拟文件系统为用户空间程序提供了文件系统接口。系统中所有文件系统不但依赖VFS共存,而且也依靠VFS系统协同工作。通过虚拟文件系统我们可以利用标准的UNIX文件系统调用对不同介质上的不同文件系统进行读写操作[4]。
虚拟文件系统的目的是为了屏蔽各种各样不同文件系统的相异操作形式,使得异构的文件系统可以在统一的形式下,以标准化的方法访问、操作。实现虚拟文件系统利用的主要思想是引入一个通用文件模型——该模型抽象出了文件系统的所有基本操作(该通用模型源于Unix风格的文件系统),比如读、写操作等。同时实际文件系统如果希望利用虚拟文件系统,既被虚拟文件系统支持,也必须将自身的诸如,“打开文件”、“读写文件”等操作行为以及“什么是文件”,“什么是目录”等概念“修饰”成虚拟文件系统所要求的(定义的)形式,这样才能够被虚拟文件系统支持和使用。
我们可以借用面向对象的一些思想来理解虚拟文件系统,虚拟文件系统好比一个抽象类或接口,它定义(但不实现)了文件系统最常见的操作行为。而具体文件系统好比是具体类,它们是特定文件系统的实例。具体文件系统和虚拟文件系统的关系类似具体类继承抽象类或实现接口。而在用户看到或操作的都是抽象类或接口,但实际行为却发生在具体文件系统实例上。至于如何将对虚拟文件系统的操作转化到对具体文件系统的实例,就要通过注册具体文件系统到系统,然后再安装具体文件系统才能实现转化,这点可以想象成面向对象中的多态概念。
我们个实举例来说明具体文件系统如何通过虚拟文件系统协同工作。
例如:假设一个用户输入以下shell命令:
$ cp /hda/test1 /removable/test2
其中 /removable是MS-DOS磁盘的一个安装点,而 /hda 是一个标准的第二扩展文件系统( Ext2)的目录。cp命令不用了解test1或test2的具体文件系统,它所看到和操作的对象是VFS。cp首先要从ext3文件系统读出test1文件,然后写入MS-DOS文件系统中的test2。VFS会将找到ext3文件系统实例的读方法,对test1文件进行读取操作;然后找到MS-DOS(在Linux中称VFAT)文件系统实例的写方法,对test2文件进行写入操作。可以看到 VFS是读写操作的统一界面,只要具体文件系统符合VFS所要求的接口,那么就可以毫无障碍地透明通讯了。
Unix风格的文件系统
虚拟文件系统的通用模型源于Unix风格的文件系统,所谓Unix风格是指Unix传统上文件系统传统上使用了四种和文件系统相关的抽象概念:文件(file)、目录项(dentry)、索引节点(inode)和安装点(mount point)。
文件——在Unix中的文件都被看做是一有序字节串,它们都有一个方便用户或系统识别的名称。另外典型的文件操作有读、写、创建和删除等。
目录项——不要和目录概念搞混淆,在Linux中目录被看作文件。而目录项是文件路径中的一部分。一个文件路径的例子是“/home/wolfman/foo”——根目录是/,目录home,wolfman和文件foo都是目录项。
索引节点——Unix系统将文件的相关信息(如访问控制权限、大小、拥有者、创建时间等等信息),有时被称作文件的元数据(也就是说,数据的数据)被存储在一个单独的数据结构中,该结构被称为索引节点(inode)。
安装点——在Unix中,文件系统被安装在一个特定的安装点上,所有的已安装文件系统都作为根文件系统树中的叶子出现在系统中。
上述概念是Unix文件系统的逻辑数据结构,但相应的Unix文件系统(Ext2等)磁盘布局也实现了部分上述概念,比如文件信息(文件数据元)存储在磁盘块中的索引节点上。当文件被载如内存时,内核需要使用磁盘块中的索引点来装配内存中的索引接点。类似行为还有超级块信息等。
对于非Unix风格文件系统,如FAT或NTFS,要想能被VFS支持,它们的文件系统代码必须提供这些概念的虚拟形式。比如,即使一个文件系统不支持索引节点,它也必须在内存中装配起索引节点结构体——如同本身固有一样。或者,如果一个文件系统将目录看作是一种特殊对象,那么要想使用VFS,必须将目录重新表示为文件形式。通常,这种转换需要在使用现场引入一些特殊处理,使得非Unix文件系统能够兼容Unix文件系统的使用规则和满足VFS的需求。通过这些处理,非Unix文件系统便可以和VFS一同工作了,是性能上多少会受一些影响[5]。这点很重要,我们实现自己文件系统时必须提供(模拟)Unix风格文件系统的抽象概念。
Linux文件系统中使用的对象
Linux文件系统的对象就是指一些数据结构体,之所以称它们是对象,是因为这些数据结构体不但包含了相关属性而且还包含了操作自身结构的函数指针,这种将数据和方法进行封装的思想和面向对象中对象概念一致,所以这里我们就称它们是对象。
Linux文件系统使用大量对象,我们简要分析以下VFS相关的对象,和除此还有和进程相关的一些其它对象。
VFS相关对象
这里我们不展开讨论每个对象,仅仅是为了内容完整性,做作简要说明。
VFS中包含有四个主要的对象类型,它们分别是:
超级块对象,它代表特定的已安装文件系统。
索引节点对象,它代表特定文件。
目录项对象,它代表特定的目录项。
文件对象,它代表和进程打开的文件。
每个主要对象中都包含一个操作对象,这些操作对象描述了内核针对主要对象可以使用的方法。最主要的几种操作对象如下:
super_operations对象,其中包括内核针对特定文件系统所能调用的方法,比如read_inode()和sync_fs()方法等。
inode_operations对象,其中包括内核针对特定文件所能调用的方法,比如create()和link()方法等。
dentry_operations对象,其中包括内核针对特定目录所能调用的方法,比如d_compare()和d_delete()方法等。
file对象,其中包括,进程针对已打开文件所能调用的方法,比如read()和write()方法等。
除了上述的四个主要对象外,VFS还包含了许多对象,比如每个注册文件系统都是由file_system_type对象表示——描述了文件系统及其能力(如比如ext3或XFS);另外每一个安装点也都利用vfsmount对象表示——包含了关于安装点的信息,如位置和安装标志等。
其它VFS对象
系统上的每一进程都有自己的打开文件,根文件系统,当前工作目录,安装点等等。另外还有几个数据结构体将VFS层和文件的进程紧密联系,它们分别是:file_struct 和fs_struct
file_struct结构体由进程描述符中的files项指向。所有包含进程的信息和它的文件描述符都包含在其中。第二个和进程相关的结构体是fs_struct。该结构由进程描述符的fs项指向。它包含文件系统和进程相关的信息。每种结构体的详细信息不在这里说明了。
缓存对象
除了上述一些结构外,为了缩短文件操作响应时间,提高系统性能,Linux系统采用了许多缓存对象,例如目录缓存、页面缓存和缓冲缓存(已经归入了页面缓存),这里我们对缓存做简单介绍。
页高速缓存(cache)是 Linux内核实现的一种主要磁盘缓存。其目的是减少磁盘的I/O操作,具体的讲是通过把磁盘中的数据缓存到物理内存中去,把对磁盘的I/O操作变为对物理内存的I/O操作。页高速缓存是由RAM中的物理页组成的,缓存中每一页都对应着磁盘中的多个块。每当内核开始执行一个页I/O操作时(通常是对普通文件中页大小的块进行磁盘操作),首先会检查需要的数据是否在高速缓存中,如果在,那么内核就直接使用高速缓存中的数据,从而避免了访问磁盘。
但我们知道文件系统只能以每次访问数个块的形式进行操作。内核执行所有磁盘操作都必须根据块进行,一个块包含一个或多个磁盘扇区。为此,内核提供了一个专门结构来管理缓冲buffer_head。缓冲头[6]的目的是描述磁盘扇区和物理缓冲之间的映射关系和做I/O操作的容器。但是缓冲结构并非独立存在,而是被包含在页高速缓存中,而且一个页高速缓存可以包含多个缓冲。我们将在文件后面的文件读写部分看到数据如何被从磁盘扇区读入页高速缓存中的缓冲中的。
文件系统的注册和安装
使用文件系统前必须对文件系统进行注册和安装,下面分别对这两种行为做简要介绍。
文件系统的注册
VFS要想能将自己定义的接口映射到实际文件系统的专用方法上,必须能够让内核识别实际的文件系统,实际文件系统通过将代表自身属性的文件类型对象(file_system_type)注册(通过register_filesystem()函数)到内核,也就是挂到内核中的文件系统类型链表上,来达到使文件系统能被内核识别的目的。反过来内核也正是通过这条链表来跟踪系统所支持的各种文件系统的。
我们简要分析一下注册步骤:
struct file_system_type {
const char *name; /*文件系统的名字*/
int fs_flags; /*文件系统类型标志*/
/*下面的函数用来从磁盘中读取超级块*/
struct super_block * (*read_super) (struct file_system_type *, int,
const char *, void *);
struct file_system_type * next; /*链表中下一个文件系统类型*/
struct list_head fs_supers; /*超级块对象链表*/
};
其中最重要的一项是read_super()函数,它用来从磁盘上读取超级块,并且当文件系统被装载时,在内存中组装超级块对象。要实现一个文件系统首先需要实现的结构体便是file_system_type结构体。
注册文件系统只能保证文件系统能被系统识别,但此刻文件系统尚不能使用,因为它还没有被安装到特定的安装点上。所以在使用文件系统前必须将文件系统安装到安装点上。
文件系统被实际安装时,将在安装点创建一个vfsmount结构体。该结构体用代表文件系统的实例——换句话说,代表一个安装点。
vfsmount结构被定义在<linux/mount.h>中,下面是具体结构
―――――――――――――――――――――――――――――――――――――――
struct vfsmount
{
struct list_head mnt_hash; /*哈希表*/
struct vfsmount *mnt_parent; /*父文件系统*/
struct dentry *mnt_mountpoint; /*安装点的目录项对象*/
struct dentry *mnt_root; /*该文件系统的根目录项对象*/
struct super_block *mnt_sb; /*该文件系统的超级块*/
struct list_head mnt_mounts; /*子文件系统链表*/
struct list_head mnt_child; /*和父文件系统相关的子文件系统*/
atomic_t mnt_count; /*使用计数*/
int mnt_flags; /*安装标志*/
char *mnt_devname; /*设备文件名字*/
struct list_head mnt_list; /*描述符链表*/
};
――――――――――――――――――――――――――――――――――――――
文件系统如果仅仅注册,那么还不能被用户使用。要想使用它还必须将文件系统安装到特定的安装点后才能工作。下面我们接着介绍文件系统的安装[7]过程。
安装过程
用户在用户空间调用mount()命令——指定安装点、安装的设备、安装类型等——安装指定文件系统到指定目录。mount()系统调用在内核中的实现函数为sys_mount(),该函数调用的主要例程是do_mount(),它会取得安装点的目录项对象,然后调用do_add_mount()例程。
do_add_mount()函数主要做的是首先使用do_kern_mount()函数创建一个安装点,再使用graft_tree()将安装点作为叶子与根目录树挂接起来。
整个安装过程中最核心的函数就是do_kern_mount()了,为了创建一个新安装点(vfsmount),该函数需要做一下几件事情:
l 1 检查安装设备的权利,只有root权限才有能力执行该操作。
l 2 Get_fs_type()在文件链表中取得相应文件系统类型(注册时被填加到练表中)。
l 3 Alloc_vfsmnt()调用slab分配器为vfsmount结构体分配存储空间,并把它的地址存放在mnt局部变量中。
l 4 初始化mnt->mnt_devname域
l 5 分配新的超级块并初始化它。do_kern_mount( )检查file_system_type描述符中的标志以决定如何进行如下操作:根据文件系统的标志位,选择相应的方法读取超级块(比如对Ext2,romfs这类文件系统调用get_sb_dev();对于这种没有实际设备的虚拟文件系统如 ramfs调用get_sb_nodev())——读取超级块最终要使用文件系统类型中的read_super方法。
安装过程做的最主要工作是创建安装点对象,挂接给定文件系统到根文件系统的指定接点下,然后初始化超级快对象,从而获得文件系统基本信息和相关操作方法(比如读取系统中某个inode的方法)。
总而言之,注册过程是告之内核给定文件系统存在于系统内;而安装是请求内核对给定文件系统进行支持,使文件系统真正可用。
转载
⑵ 数据结构的一些小问题
5.4 文件系统的实现
5.4.1 存储空间的分配与回收
在计算机系统中,存储空间是一种宝贵的资源。外存储设备中的空间容量虽然比较大,但也不是无限的,故对文件删除之后而不再使用的空间,必须加以回收,然后在建立文件等操作中重新利用。
对于制度的存储设备,无所谓回收,也无所谓动态分配,这种存储设备在物理上就是不可重用的。
为了进行存储空间的分配与回收,在外存储设备上设置有空闲空间登记表,该表动态跟踪该外存储设备上所有还没有分配给任何文件的空闲块的数目和块号。
该空闲空间登记表虽然称为表,但不一定以一个二维表格的形式实现。从方便、高效和安全的角度考虑,通常把空闲空间登记表放在存储介质上。
对空闲空间表的访问与修改工作是经常发生的。在进行文件删除、文件建立、写文件等操作中都会访问和修改空闲空间表。
在设计空闲空间登记表的数据结构时,一般有四种不同的方案可以考虑,下面分别介绍。
1、 位示图
位示图的基本思想是,利用一串二进制位(BIT)的值来反映磁盘空间的分配使用情况。在位示图中,没一个磁盘中物理块用一个二进制位对应,如果某个物理块为空闲,则相应的二进制位为0;如果该物理块已分配了,则相应的二进位为,如图5-15所示。
位示图对空间分配情况的描述能力强。一个二进位就描述一个物理块的状态。另外,位示图占用空间较小,因此可以复制到内存,使查找既方便又快速。位示图适用于各种文件物理结构的文件系统。使用位示图能够简单有效地在盘上找到N个连续的空闲块。
2、 空闲块表
空闲块表是专门为空闲块建立的一张表,该表记录外存储器全部空闲的物理块:包括每个空闲块的第一个空闲物理块号和该空闲块只能感空闲物理块的个数。如图5-16所示。空闲块表方式特别适合于文件物理结构为顺序结构的文件系统。
在建立新文件时,应该寻找一组连续的空闲物理块,其空闲块个数恰好等于所申请值,或接近于所申请值。系统首先查找空闲块表,主要查看该空闲块表项中是否有符合上述申请值的对应表项,如果有,就将该表项从空闲块表中删去,并且把所对应的一组连续的空闲物理块分配给申请者。
当删除文件时,系统收回它所占用的物理块,并且考虑所收回的物理块是否可以与原有空闲块相邻接,以便合并成更大的空闲区域,最后修改有关空闲块表项。
3、 空闲块链表
将外存储器中所有的空闲物理块连成一个链表,用一个空闲块首指针指向第一个空闲块,随后的每个空闲块中都含有指向下一个空闲块的指针,最后一块的指针为空,表示链尾,这样就构成了一个空闲块链表,如图5-17所示。
在图45-17中,一个空闲块链表的首指针维持一个指向物理块12的指针,该块是第一个空闲物理块。物理块12包含一个指向物理块13的指针,物理块13指向物理块14,如此等等。
空闲块链表模式效率低。要遍历整张空闲块链表,必须读每一个物理块,这就需要大量的I/O时间。
在空闲块链表模式中对空间的申请和释放是以块为单位的。申请空间时从链首取空闲块。空间释放时将物理块接入链尾。
空闲块链表法节省内存,但申请空间和回收空间的速度较慢,实现效率较低。
4、 成组链接
对链接表的一个改进方案是将N个空闲盘块的地址存放在第一个空闲块中,如图5-18所示。期于N-1个空闲盘块是实际空闲的。假设每100个空闲块为一组。第一组的100个空闲块块号放在第二组的头一块中,而第二组的其余99块是完全空闲的。第二组的100个块号又放在第三组的头一块中,依次类推,组与组之间形成链接关系。在最后一组的块号中第2个单元填“0”,表示该块中指出的块号是最后一组的块号,空闲块链到此结束。在这个空闲块链中,不组100块的一个组的块号,通常放在内存的一个专用块中。这种方式称为成组链接。
系统在初始化时先把专用块内容读到内存中,当需分配空闲块时,就直接在内存中找到哪些块是空闲的,每分配一块后把空闲块数减1。但在把一组中的第一个空闲块分配出去之前,应把登记在该块中的下一组的块号及块数保存到专用块中(此时原专用块中的信息已经无用,因为它指出的一组空闲块都已被分配了)。当一组空闲块被分配完后,则再把专用块的内容读到内存中,指出另一组可供分配的空闲块。
假设初始化时系统已把专用块读入主存储器L单元开始的区域中,分配和回收的算法如下:
⑴分配一个空闲块
查L单元内容(空闲块数):
当空闲块数>1,I:=L+空闲块数;
从I单元得到一空闲块号;
把该块分配给申请者;
空闲块数减1。
当空闲块数=1,取出L+1单元内容(一组的第一块块号或0);
取值 =0,无空闲块,申请者等待;
≠0,把该块内容复制到专用块;
把专用块内容独到主存L开始的区域。
⑵归还一块
查L单元的空闲块数;
当空闲块数〈100,空闲块数加1;
J:=L+空闲块数;
规划块号填入J单元。
当空闲块数=100,把主存中登记的信息写入归还块中;
把归还块号填入L+1单元;
将L单元置成1。
采用成组链接后,分配回收空闲块时均在内存中查找和修改,只有在一组空闲块分配完或空闲的磁盘块构成一组时才需要启动磁盘读写。因此,成组链接的管理方式比普通的链接方式效率高。
成组链接这种方案能够迅速找到大量空闲盘块地址。有些版本的UNIX操作系统便采用了这种方案。
5.4.2 实现文件系统的表目
当用户申请打开一个文件时,系统要在内存中为该用户保存一些必要的信息,这些信息以表格栏目中内容的形式出现,被称为表目。在实现文件系统时所需要的表目有若干种,其中在内存中所需的重要表目有如下一些:
1、 系统打开文件表
系统打开文件表,专门用于保存已打开文件的文件控制块。该系统打开文件表放在内存。除了保存已打开文件的文件控制块之外,在该表格中还保存有已打开的文件号、共享计数、修改标志等等,图5-19。
2、 用户打开文件表
在每个进程中,都有一个“用户打开文件表”。该表的内容有文件描述符、打开方式、读写指针、系统打开文件表入口等,图5-20。另外在进程的进程控制块PCB中,还记录了“用户打开文件表”的位置。
3、 系统打开文件表与用户打开文件表之间的关系
用户打开文件表指向了系统打开文件表。如果多个进程共享同一个文件,则一定有多个用户打开文件表目对应着系统打开文件表的同一入口,图5-21。
5.4.3 记录的成组与分解
用户的文件毫无疑问是由用户按名自己的需要组织的。用户还可按信息的内在逻辑关系,把文件划分成若干个逻辑记录。显然,逻辑记录的大小是由文件性质决定的。
另外,存储介质上的物理分块与存储介质的特性有关,尤其是磁盘。磁盘上的块的大小是在磁盘初始化时预先划好的。因此,逻辑记录的大小往往与存储介质物理分块的大小不一直。
当用户文件的逻辑记录比存储介质的物理分块小得多时,把一个逻辑记录存入一个物理块中,就会造成存储空间的浪费。为此,可把多个逻辑记录存放在一个物理块中,当用户需要某个逻辑记录时再从一物理块信息中将其分解出来。
1、 记录的成组
把若干个逻辑记录合成一组存放一物理块的工作称“记录的成组”,每块中的逻辑记录个数称“块因子”。
记录的成组在不同存储介质上进行信息转储是很有用的。
由于信息交换以块为单位,所以,要进行成组操作时必须使用内存的缓冲区。该缓冲区的长度等于要进行成组的最大逻辑记录长度乘以成组的块因子。成组转储操作如图5-22所示。
在进行记录成组时,还应考虑逻辑记录的格式。这是因为在记录式文件中,有“定长记录格式”和“不定长记录格式”。对定长记录格式的文件按记录成组的方式存储到存储介质上,则除最后一块外,每块中存放的逻辑记录个数是相同的。故只要在文件目录中说明逻辑记录的长度和块因子,在需要使用某个记录时就能方便地将其找出。
如果是一个不定长记录格式的文件,各个逻辑记录的长度可能不相等,在进行记录成组操作时,就应在每个逻辑记录前附加说明该记录长度的控制信息。
2、 记录的分解
对应签署记录成组的操作,有必要考虑从一组逻辑记录中把一个逻辑记录分离出来的操作,这种操作称为“记录的分解”。
显然,从事记录的分解操作也要使用内存缓冲区。
当用户请求读一个文件中的某个记录时,文件系统首先找出该记录所在物理块的位置,然后把含有该记录的物理块全部信息读入内存缓冲区,由于读入内存缓冲区的物理块信息中含有多个逻辑记录,所以要再从内存缓冲区中分解出指定的记录,然后传送到用户工作区。
对定长记录格式,只要的逻辑记录的长度就可容易地进行分解。对不定长记录格式,要根据说明逻辑记录长度的控制信息,计算出用户所指定的记录在内存缓冲区中的位置,然后把记录分解出来。图5-23是记录的分解操作示例。
在图5-23中,用户要求读出逻辑记录K4。用户文件中的记录是成组存放在磁盘上的,系统找出含有记录K4的物理块,从中读出了6个逻辑记录K1,K2,K3,K4,K5和K6,并且知道这些逻辑记录的长度为80,块因子为6。该块信息被读入内存缓冲区后,根据逻辑记录的长度和块因子胃,立即就能取出其中的逻辑记录K4,并把K4传送到用户工作区。
从上面的讨论可以看到,为了提高存储空间的利用率和减少启动设备的次数,采用了记录的成组和分解技术。但是上述效果的获得也付出了代价,主要包括:需要设立内存缓冲区,另外操作系统增加了成组和分解的操作的功能。
5.4.4 文件的操作
文件系统是提供给用户使用的,用户可以进行按名存取所需要的文件。在文件系统的实现中,为用户提供使用文件的手段是文件系统的重要任务之一。
1、 建立文件
用户首先调用文件系统的“建立文件”操作,在请求调用该操作时,提供所要创建的文件的文件名及若干参数:用户名、文件名、存取方式、存储设备类型、记录格式、记录长度,等等。
系统依据用户提供的文件名及若干参数,为这一新创建的文件分配一个文件控制块,填写文件控制块中的有关项。
建立文件的实质是建立文件的文件控制块FCB,并建立必要的存储空间,分配空的FCB。从而建立起系统与文件的联系。
建立文件系统调用的一般格式为:CREATE(文件名,访问权限,(最大长度))。
建立文件的具体步骤如下:
⑴检查参数的合法性:
文件名是否符合命名规则,若是,则进行下一步⑵;否则报错,返回。
⑵检查同一目录下有无重名文件:
若没有,则进行下一步⑶;否则报错,返回。
⑶在目录中有无空闲位置;
若有,则进行下一步⑷;否则,不成功返回。
⑷填写目录项内容:
包括:文件名、用户名、存取权限、长度置零,首地址等等。
⑸返回
2、 打开文件
打开文件,是使用文件的第一步,任何一个文件使用前都要先打开,即把文件控制块FCB送到内存。
打开文件系统调用的一般格式为:FD=OPEN(文件路径名,打开方式)。
打开文件时,系统主要完成以下工作:
⑴根据文件路径名查目录,找到FCB主部。
⑵根据打开方式、共享说明和用户身份检查访问合法性。
⑶根据文件号查系统打开文件表,看文件是否已被打开。
如果是,共享计数加1;
否则,将外存中的CB主部等信息填入系统打开文件表空表项,共享计数置为1。
⑷在用户打开文件表中取一空表项,填写打开方式等,并指向系统打开文件表对应表项。
返回信息:FD:文件描述符,是一个非负整数,用于以后读写文件。
3、 读文件
打开文件后,就可以读取文件中的信息。
读文件系统调用的一般格式为:READ(文件名,(文件内位置),要读的长度,内存目的地址)。
隐含参数:文件主。
读写方式可为读、写合既读又写等。
读文件时,系统主要完成以下工作:
⑴检查长度是否为正整数;
若是,则进行下一步;否则,转向(10)。
⑵根据文件名查找目录,确定该文件在目录中的位置。
⑶根据隐含参数中的文件主和目录中该文件的存储权限数据,检查是否有权读:
若是,则进行下一步,否则,转向(10)。
⑷由文件内位置与要读的长度计算最末位置,将其与目录中的文件长度比较,超过否?
若是,则转向(10);否则,进行下一步(5)。也可将参数中的长度修正为目录中的文件长度。
⑸根据参数中的位置、长度和目录中的映射信息,确定物理块号、需要读出的块数等读盘参数。
⑹根据下一块号读块至内存缓冲区。
⑺取出要读的内容,也许要进行成组的分解,将取出的内容送至参数中的内存目的地址。
⑻根据块内长度或起始块号+块数,确定还读下一块吗?同时确定下一块号:
若是,则转向(5);否则,进行下一步(9)。
⑼正常返回。
⑽错误返回,返回响应错误号。
4、 写文件
写文件系统调用的一般格式为:WRITE文件名,记录键,内存位置)。
把内存中指定单元的数据作为指定的一个记录写入指定文件中,系统还将为其分配物理块,以便把记录信息写到外存上。
5、 关闭文件
若文件暂时不用每则应将它关闭。文件关闭后一般不能存取,若要存取,则必须再次打开
6、 删除文件
删除文件文件系统调用的一般格式为:DELETE(文件名)。
7、 指针定位
指针定位的一般格式为:SEEK(FD,新指针的位置)。
指针定位时,系统主要完成以下工作:
⑴由FD检查用户打开文件表,找到对应的入口;
⑵将用户打开文件表中文件读写指针位置设为新指针的位置,供后续读写命令存取该指针处文件内容。
希望对你有帮助
⑶ 简述文件系统与数据库系统的区别与联系。
1、文件系统于数据库系统的区别:
文件系统面向某一应用程序,共享性差,冗余度大,数据独立性差,记录内有结构,整体无结构,由应用程序自己控制。
数据库系统面向现实世界,共享性高,冗余度小,具有较高的物理独立性和一定的逻辑独立性,整体结构化,用数据模型描述,由数据库系统提供数据安全性,完整性,并发控制和恢复力。
2,文件系统与数据库系统的联系:
文件系统于数据库系统都是计算机系统中管理数据库的软件。解析文件系统是操作系统的重要组成部分。
而数据库系统DBMS是独立于操作系统的软件。到时DBMS狮子啊操作系统的基础上实现的。数据库系统的组织和存储是通过操作系统中的文件系统来实现的。
(3)杭电简单文件系统的实现扩展阅读:
文件系统存在的意义:
在计算机中,文件系统是命名文件及放置文件的逻辑存储和恢复的系统。DOS、Windows、OS/2、Macintosh和UNIX-based操作系统都有文件系统,在此系统中文件被放置在分等级的(树状)结构中的某一处。
文件系统指定命名文件的规则。这些规则包括文件名的字符数最大量,哪种字符可以使用,以及某些系统中文件名后缀可以有多长。文件系统还包括通过目录结构找到文件的指定路径的格式。
文件系统是软件系统的一部分,它的存在使得应用可以方便的使用抽象命名的数据对象和大小可变的空间。
参考资料来源:网络-数据库系统
参考资料来源:网络-文件系统
⑷ 文件管理的主要目的是( )。
D 实现文件按名存取。
解析:
简单地说,文件是一段程序或数据的集合,文件系统就是操作系统中与管理文件有关的软件和数据,它负责为用户建立文件,撤销、读写、修改和复制文件,还负责文件的按名存取和存取控制。
文件系统具有以下特点:
(1)友好的用户接口,用户只对文件进行操作而不用管文件的结构和存放的物理位置。
(2)对文件按名存取,对用户透明。这也是文件系统的主要目的。
(4)杭电简单文件系统的实现扩展阅读:
从系统角度来看,文件系统是对文件存储器的存储空间进行组织,分配和回收,负责文件的存储,检索,共享和保护。从用户角度来看,文件系统主要是实现"按名取存",文件系统的用户只要知道所需文件的文件名,就可存取文件中的信息,而无需知道这些文件究竟存放在什么地方。
随着计算机应用的普及,单纯通过制度进行文档安全管理越来越力不从心,企业开始采用专门的保密设备来管理机密文档,如安装专门的涉密计算机、使用认证存储设备等。
为适应信息化工作及无纸化办公的要求,同时随着互联网络技术的发展,为了应对来自互联网络的威胁,很多企业采用堵塞的方式,如内网隔离、封USB口、禁止打印等方式管理内部机密文档。
⑸ 程序员必备知识(操作系统5-文件系统)
本篇与之前的第三篇的内存管理知识点有相似的地方
对于运行的进程来说,内存就像一个纸箱子, 仅仅是一个暂存数据的地方, 而且空间有限。如果我们想要进程结束之后,数据依然能够保存下来,就不能只保存在内存里,而是应该保存在 外部存储 中。就像图书馆这种地方,不仅空间大,而且能够永久保存。
我们最常用的外部存储就是 硬盘 ,数据是以文件的形式保存在硬盘上的。为了管理这些文件,我们在规划文件系统的时候,需要考虑到以下几点。
第一点,文件系统要有严格的组织形式,使得文件能够 以块为单位进行存储 。这就像图书馆里,我们会给设置一排排书架,然后再把书架分成一个个小格子,有的项目存放的资料非常多,一个格子放不下,就需要多个格子来进行存放。我们把这个区域称为存放原始资料的 仓库区 。
第二点,文件系统中也要有 索引区 ,用来方便查找一个文件分成的多个块都存放在了什么位置。这就好比,图书馆的书太多了,为了方便查找,我们需要专门设置一排书架,这里面会写清楚整个档案库有哪些资料,资料在哪个架子的哪个格子上。这样找资料的时候就不用跑遍整个档案库,在这个书架上找到后,直奔目标书架就可以了。
第三点,如果文件系统中有的文件是热点文件,近期经常被读取和写入,文件系统应该有 缓存层 。这就相当于图书馆里面的热门图书区,这里面的书都是畅销书或者是常常被借还的图书。因为借还的次数比较多,那就没必要每次有人还了之后,还放回遥远的货架,我们可以专门开辟一个区域, 放置这些借还频次高的图书。这样借还的效率就会提高。
第四点,文件应该用 文件夹 的形式组织起来,方便管理和查询。这就像在图书馆里面,你可以给这些资料分门别类,比如分成计算机类.文学类.历史类等等。这样你也容易管理,项目组借阅的时候只要在某个类别中去找就可以了。
在文件系统中,每个文件都有一个名字,这样我们访问一个文件,希望通过它的名字就可以找到。文件名就是一个普通的文本。 当然文件名会经常冲突,不同用户取相同的名字的情况还是会经常出现的。
要想把很多的文件有序地组织起来,我们就需要把它们成为 目录 或者文件夹。这样,一个文件夹里可以包含文件夹,也可以包含文件,这样就形成了一种 树形结构 。而我们可以将不同的用户放在不同的用户目录下,就可以一定程度上避免了命名的冲突问题。
第五点,Linux 内核要在自己的内存里面维护一套数据结构,来保存哪些文件被哪些进程打开和使用 。这就好比,图书馆里会有个图书管理系统,记录哪些书被借阅了,被谁借阅了,借阅了多久,什么时候归还。
文件系统是操作系统中负责管理持久数据的子系统,说简单点,就是负责把用户的文件存到磁盘硬件中,因为即使计算机断电了,磁盘里的数据并不会丢失,所以可以持久化的保存文件。
文件系统的基本数据单位是 文件 ,它的目的是对磁盘上的文件进行组织管理,那组织的方式不同,就会形成不同的文件系统。
Linux最经典的一句话是:“一切皆文件”,不仅普通的文件和目录,就连块设备、管道、socket 等,也都是统一交给文件系统管理的。
Linux文件系统会为每个文件分配两个数据结构: 索引节点(index node) 和 目录项(directory entry) ,它们主要用来记录文件的元信息和目录层次结构。
●索引节点,也就是inode, 用来记录文件的元信息,比如inode编号、文件大小访问权限、创建时间、修改时间、 数据在磁盘的位置 等等。 索引节点是文件的唯一标识 ,它们之间一一对应, 也同样都会被 存储在硬盘 中,所以索引节点同样占用磁盘空间。
●目录项,也就是dentry, 用来记录文件的名字、索引节点指针以及与其他目录项的层级关联关系。多个目录项关联起来,就会形成 目录结构 ,但它与索引节点不同的是,目录项是由内核维护的一个数据结构,不存放于磁盘,而是 缓存在内存 。
由于索引节点唯一标识一个文件,而目录项记录着文件的名,所以目录项和索引节点的关系是多对一,也就是说,一个文件可以有多个别字。比如,硬链接的实现就是多个目录项中的索引节点指向同一个文件。
注意,目录也是文件,也是用索引节点唯一标识,和普通文件不同的是,普通文件在磁盘里面保存的是文件数据,而目录文件在磁盘里面保存子目录或文件。
(PS:目录项和目录不是一个东西!你也不是一个东西(^_=), 虽然名字很相近,但目录是个文件。持久化存储在磁盘,而目录项是内核一个数据结构,缓存在内存。
如果查询目录频繁从磁盘读,效率会很低,所以内核会把已经读过的目录用目录项这个数据结构缓存在内存,下次再次读到相同的目录时,只需从内存读就可以,大大提高了 文件系统的效率。
目录项这个数据结构不只是表示目录,也是可以表示文件的。)
磁盘读写的最小单位是 扇区 ,扇区的大小只有512B大小,很明显,如果每次读写都以这么小为单位,那这读写的效率会非常低。
所以,文件系统把多个扇区组成了一个 逻辑块 ,每次读写的最小单位就是逻辑块(数据块) , Linux中的逻辑块大小为4KB,也就是一次性读写 8个扇区,这将大大提高了磁盘的读写的效率。
以上就是索引节点、目录项以及文件数据的关系,下面这个图就很好的展示了它们之间的关系:
索引节点是存储在硬盘上的数据,那么为了加速文件的访问,通常会把索引节点加载到内存中。
另外,磁盘进行格式化的时候,会被分成三个存储区域,分别是超级块、索引节点区和数据块区。
●超级块,用来存储文件系统的详细信息,比如块个数、块大小、空闲块等等。
●索引节点区,用来存储索引节点;
●数据块区,用来存储文件或目录数据;
我们不可能把超级块和索引节点区全部加载到内存,这样内存肯定撑不住,所以只有当需要使用的时候,才将其加载进内存,它们加载进内存的时机是不同的.
●超级块:当文件系统挂载时进入内存;
●索引节点区:当文件被访问时进入内存;
文件系统的种类众多,而操作系统希望 对用户提供一个统一的接口 ,于是在用户层与文件系统层引入了中间层,这个中间层就称为 虚拟文件系统(Virtual File System, VFS) 。
VFS定义了一组所有文件系统都支持的数据结构和标准接口,这样程序员不需要了解文件系统的工作原理,只需要了解VFS提供的统一接口即可。
在Linux文件系统中,用户空间、系统调用、虚拟机文件系统、缓存、文件系统以及存储之间的关系如下图:
Linux支持的文件系统也不少,根据存储位置的不同,可以把文件系统分为三类:
●磁盘的文件系统,它是直接把数据存储在磁盘中,比如Ext 2/3/4. XFS 等都是这类文件系统。
●内存的文件系统,这类文件系统的数据不是存储在硬盘的,而是占用内存空间,我们经常用到的/proc 和/sys文件系统都属于这一类,读写这类文件,实际上是读写内核中相关的数据。
●网络的文件系统,用来访问其他计算机主机数据的文件系统,比如NFS. SMB等等。
文件系统首先要先挂载到某个目录才可以正常使用,比如Linux系统在启动时,会把文件系统挂载到根目录。
在操作系统的辅助之下,磁盘中的数据在计算机中都会呈现为易读的形式,并且我们不需要关心数据到底是如何存放在磁盘中,存放在磁盘的哪个地方等等问题,这些全部都是由操作系统完成的。
那么,文件数据在磁盘中究竟是怎么样的呢?我们来一探究竟!
磁盘中的存储单元会被划分为一个个的“ 块 ”,也被称为 扇区 ,扇区的大小一般都为512byte.这说明即使一块数据不足512byte,那么它也要占用512byte的磁盘空间。
而几乎所有的文件系统都会把文件分割成固定大小的块来存储,通常一个块的大小为4K。如果磁盘中的扇区为512byte,而文件系统的块大小为4K,那么文件系统的存储单元就为8个扇区。这也是前面提到的一个问题,文件大小和占用空间之间有什么区别?文件大小是文件实际的大小,而占用空间则是因为即使它的实际大小没有达到那么大,但是这部分空间实际也被占用,其他文件数据无法使用这部分的空间。所以我们 写入1byte的数据到文本中,但是它占用的空间也会是4K。
这里要注意在Windows下的NTFS文件系统中,如果一开始文件数据小于 1K,那么则不会分配磁盘块来存储,而是存在一个文件表中。但是一旦文件数据大于1K,那么不管以后文件的大小,都会分配以4K为单位的磁盘空间来存储。
与内存管理一样,为了方便对磁盘的管理,文件的逻辑地址也被分为一个个的文件块。于是文件的逻辑地址就是(逻辑块号,块内地址)。用户通过逻辑地址来操作文件,操作系统负责完成逻辑地址与物理地址的映射。
不同的文件系统为文件分配磁盘空间会有不同的方式,这些方式各自都有优缺点。
连续分配要求每个文件在磁盘上有一组连续的块,该分配方式较为简单。
通过上图可以看到,文件的逻辑块号的顺序是与物理块号相同的,这样就可以实现随机存取了,只要知道了第一个逻辑块的物理地址, 那么就可以快速访问到其他逻辑块的物理地址。那么操作系统如何完成逻辑块与物理块之间的映射呢?实际上,文件都是存放在目录下的,而目录是一种有结构文件, 所以在文件目录的记录中会存放目录下所有文件的信息,每一个文件或者目录都是一个记录。 而这些信息就包括文件的起始块号和占有块号的数量。
那么操作系统如何完成逻辑块与物理块之间的映射呢? (逻辑块号, 块内地址) -> (物理块号, 块内地址),只需要知道逻辑块号对应的物理块号即可,块内地址不变。
用户访问一个文件的内容,操作系统通过文件的标识符找到目录项FCB, 物理块号=起始块号+逻辑块号。 当然,还需要检查逻辑块号是否合法,是否超过长度等。因为可以根据逻辑块号直接算出物理块号,所以连续分配支持 顺序访问和随机访问 。
因为读/写文件是需要移动磁头的,如果访问两个相隔很远的磁盘块,移动磁头的时间就会变长。使用连续分配来作为文件的分配方式,会使文件的磁盘块相邻,所以文件的读/写速度最快。
连续空间存放的方式虽然读写效率高,但是有 磁盘空间碎片 和 文件长度不易扩展 的缺陷。
如下图,如果文件B被删除,磁盘上就留下一块空缺,这时,如果新来的文件小于其中的一个空缺,我们就可以将其放在相应空缺里。但如果该文件的大小大于所
有的空缺,但却小于空缺大小之和,则虽然磁盘上有足够的空缺,但该文件还是不能存放。当然了,我们可以通过将现有文件进行挪动来腾出空间以容纳新的文件,但是这个在磁盘挪动文件是非常耗时,所以这种方式不太现实。
另外一个缺陷是文件长度扩展不方便,例如上图中的文件A要想扩大一下,需要更多的磁盘空间,唯一的办法就只能是挪动的方式,前面也说了,这种方式效率是非常低的。
那么有没有更好的方式来解决上面的问题呢?答案当然有,既然连续空间存放的方式不太行,那么我们就改变存放的方式,使用非连续空间存放方式来解决这些缺陷。
非连续空间存放方式分为 链表方式 和 索引方式 。
链式分配采取离散分配的方式,可以为文件分配离散的磁盘块。它有两种分配方式:显示链接和隐式链接。
隐式链接是只目录项中只会记录文件所占磁盘块中的第一块的地址和最后一块磁盘块的地址, 然后通过在每一个磁盘块中存放一个指向下一 磁盘块的指针, 从而可以根据指针找到下一块磁盘块。如果需要分配新的磁盘块,则使用最后一块磁盘块中的指针指向新的磁盘块,然后修改新的磁盘块为最后的磁盘块。
我们来思考一个问题, 采用隐式链接如何将实现逻辑块号转换为物理块号呢?
用户给出需要访问的逻辑块号i,操作系统需要找到所需访问文件的目录项FCB.从目录项中可以知道文件的起始块号,然后将逻辑块号0的数据读入内存,由此知道1号逻辑块的物理块号,然后再读入1号逻辑块的数据进内存,此次类推,最终可以找到用户所需访问的逻辑块号i。访问逻辑块号i,总共需要i+ 1次磁盘1/0操作。
得出结论: 隐式链接分配只能顺序访问,不支持随机访问,查找效率低 。
我们来思考另外一个问题,采用隐式链接是否方便文件拓展?
我们知道目录项中存有结束块号的物理地址,所以我们如果要拓展文件,只需要将新分配的磁盘块挂载到结束块号的后面即可,修改结束块号的指针指向新分配的磁盘块,然后修改目录项。
得出结论: 隐式链接分配很方便文件拓展。所有空闲磁盘块都可以被利用到,无碎片问题,存储利用率高。
显示链接是把用于链接各个物理块的指针显式地存放在一张表中,该表称为文件分配表(FAT, File Allocation Table)。
由于查找记录的过程是在内存中进行的,因而不仅显著地 提高了检索速度 ,而且 大大减少了访问磁盘的次数 。但也正是整个表都存放在内存中的关系,它的主要的缺点是 不适 用于大磁盘 。
比如,对于200GB的磁盘和1KB大小的块,这张表需要有2亿项,每一项对应于这2亿个磁盘块中的一个块,每项如果需要4个字节,那这张表要占用800MB内存,很显然FAT方案对于大磁盘而言不太合适。
一直都在,加油!(*゜Д゜)σ凸←自爆按钮
链表的方式解决了连续分配的磁盘碎片和文件动态打展的问题,但是不能有效支持直接访问(FAT除外) ,索引的方式可以解决这个问题。
索引的实现是为每个文件创建一个 索引数据块 ,里面存放的 是指向文件数据块的指针列表 ,说白了就像书的目录一样,要找哪个章节的内容,看目录查就可以。
另外, 文件头需要包含指向索引数据块的指针 ,这样就可以通过文件头知道索引数据块的位置,再通过索弓|数据块里的索引信息找到对应的数据块。
创建文件时,索引块的所有指针都设为空。当首次写入第i块时,先从空闲空间中取得一个块, 再将其地址写到索引块的第i个条目。
索引的方式优点在于:
●文件的创建、增大、缩小很方便;
●不会有碎片的问题;
●支持顺序读写和随机读写;
由于索引数据也是存放在磁盘块的,如果文件很小,明明只需一块就可以存放的下,但还是需要额外分配一块来存放索引数据,所以缺陷之一就是存储索引带来的开销。
如果文件很大,大到一个索引数据块放不下索引信息,这时又要如何处理大文件的存放呢?我们可以通过组合的方式,来处理大文件的存储。
先来看看 链表+索引 的组合,这种组合称为 链式索引块 ,它的实现方式是在 索引数据块留出一个存放下一个索引数据块的指针 ,于是当一个索引数据块的索引信息用完了,就可以通过指针的方式,找到下一个索引数据块的信息。那这种方式也会出现前面提到的链表方式的问题,万一某个指针损坏了,后面的数据也就会无法读取了。
还有另外一种组合方式是 索引+索引 的方式,这种组合称为多级索引块,实现方式是通过一个索引块来存放多个索引数据块,一层套一层索引, 像极了俄罗斯套娃是吧๑乛◡乛๑
前面说到的文件的存储是针对已经被占用的数据块组织和管理,接下来的问题是,如果我要保存一个数据块, 我应该放在硬盘上的哪个位置呢?难道需要将所有的块扫描一遍,找个空的地方随便放吗?
那这种方式效率就太低了,所以针对磁盘的空闲空间也是要引入管理的机制,接下来介绍几种常见的方法:
●空闲表法
●空闲链表法
●位图法
空闲表法
空闲表法就是为所有空闲空间建立一张表,表内容包括空闲区的第一个块号和该空闲区的块个数,注意,这个方式是连续分配的。如下图:
当请求分配磁盘空间时,系统依次扫描空闲表里的内容,直到找到一个合适的空闲区域为止。当用户撤销一个文件时,系统回收文件空间。这时,也需顺序扫描空闲表,寻找一个空闲表条目并将释放空间的第一个物理块号及它占用的块数填到这个条目中。
这种方法仅当有少量的空闲区时才有较好的效果。因为,如果存储空间中有着大量的小的空闲区,则空闲表变得很大,这样查询效率会很低。另外,这种分配技术适用于建立连续文件。
空闲链表法
我们也可以使用链表的方式来管理空闲空间,每一个空闲块里有一个指针指向下一个空闲块,这样也能很方便的找到空闲块并管理起来。如下图:
当创建文件需要一块或几块时,就从链头上依次取下一块或几块。反之,当回收空间时,把这些空闲块依次接到链头上。
这种技术只要在主存中保存一个指针, 令它指向第一个空闲块。其特点是简单,但不能随机访问,工作效率低,因为每当在链上增加或移动空闲块时需要做很多1/0操作,同时数据块的指针消耗了一定的存储空间。
空闲表法和空闲链表法都不适合用于大型文件系统,因为这会使空闲表或空闲链表太大。
位图法
位图是利用二进制的一位来表示磁盘中一个盘块的使用情况,磁盘上所有的盘块都有一个二进制位与之对应。
当值为0时,表示对应的盘块空闲,值为1时,表示对应的盘块已分配。它形式如下:
在Linux文件系统就采用了位图的方式来管理空闲空间,不仅用于数据空闲块的管理,还用于inode空闲块的管理,因为inode也是存储在磁盘的,自然也要有对其管理。
前面提到Linux是用位图的方式管理空闲空间,用户在创建一个新文件时, Linux 内核会通过inode的位图找到空闲可用的inode,并进行分配。要存储数据时,会通过块的位图找到空闲的块,并分配,但仔细计算一下还是有问题的。
数据块的位图是放在磁盘块里的,假设是放在一个块里,一个块4K,每位表示一个数据块,共可以表示4 * 1024 * 8 = 2^15个空闲块,由于1个数据块是4K大小,那么最大可以表示的空间为2^15 * 4 * 1024 = 2^27个byte,也就是128M。
也就是说按照上面的结构,如果采用(一个块的位图+ 一系列的块),外加一(个块的inode的位图+一系列的inode)的结构能表示的最大空间也就128M,
这太少了,现在很多文件都比这个大。
在Linux文件系统,把这个结构称为一个 块组 ,那么有N多的块组,就能够表示N大的文件。
最终,整个文件系统格式就是下面这个样子。
最前面的第一个块是引导块,在系统启动时用于启用引导,接着后面就是一个一个连续的块组了,块组的内容如下:
● 超级块 ,包含的是文件系统的重要信息,比如inode总个数、块总个数、每个块组的inode个数、每个块组的块个数等等。
● 块组描述符 ,包含文件系统中各个块组的状态,比如块组中空闲块和inode的数目等,每个块组都包含了文件系统中「所有块组的组描述符信息」。
● 数据位图和inode位图 ,用于表示对应的数据块或inode是空闲的,还是被使用中。
● inode 列表 ,包含了块组中所有的inode, inode 用于保存文件系统中与各个文件和目录相关的所有元数据。
● 数据块 ,包含文件的有用数据。
你可以会发现每个块组里有很多重复的信息,比如 超级块和块组描述符表,这两个都是全局信息,而且非常的重要 ,这么做是有两个原因:
●如果系统崩溃破坏了超级块或块组描述符,有关文件系统结构和内容的所有信息都会丢失。如果有冗余的副本,该信息是可能恢复的。
●通过使文件和管理数据尽可能接近,减少了磁头寻道和旋转,这可以提高文件系统的性能。
不过,Ext2 的后续版本采用了稀疏技术。该做法是,超级块和块组描述符表不再存储到文件系统的每个块组中,而是只写入到块组0、块组1和其他ID可以表示为3、5、7的幂的块组中。
在前面,我们知道了一个普通文件是如何存储的,但还有一个特殊的文件,经常用到的目录,它是如何保存的呢?
基于Linux 一切切皆文件的设计思想,目录其实也是个文件,你甚至可以通过vim打开它,它也有inode, inode 里面也是指向一些块。
和普通文件不同的是, 普通文件的块里面保存的是文件数据,而目录文件的块里面保存的是目录里面一项一项的文件信息 。
在目录文件的块中,最简单的保存格式就是 列表 ,就是一项一项地将目录下的文件信息(如文件名、文件inode.文件类型等)列在表里。
列表中每一项就代表该目录下的文件的文件名和对应的inode,通过这个inode,就可以找到真正的文件。
通常,第一项是「则」,表示当前目录,第二项是.,表示上一级目录, 接下来就是一项一项的文件名和inode。
如果一个目录有超级多的文件,我们要想在这个目录下找文件,按照列表一项一项的找,效率就不高了。
于是,保存目录的格式改成 哈希表 ,对文件名进行哈希计算,把哈希值保存起来,如果我们要查找一个目录下面的文件名,可以通过名称取哈希。如果哈希能够匹配上,就说明这个文件的信息在相应的块里面。
Linux系统的ext文件系统就是采用了哈希表,来保存目录的内容,这种方法的优点是查找非常迅速,插入和删除也较简单,不过需要一些预备措施来避免哈希冲突。
目录查询是通过在磁盘上反复搜索完成,需要不断地进行/0操作,开销较大。所以,为了减少/0操作,把当前使用的文件目录缓存在内存,以后要使用该文件时只要在内存中操作,从而降低了磁盘操作次数,提高了文件系统的访问速度。
感谢您的阅读,希望您能摄取到知识!加油!冲冲冲!(发现光,追随光,成为光,散发光!)我是程序员耶耶!有缘再见。<-biubiu-⊂(`ω´∩)
⑹ 用C语言设计一个简单的二级文件系统
简单吗,我这有手机的文件系统最简单功能实现都要上万行
⑺ 基于多级文件目录的文件管理系统设计的设计原理是什么有着什么样的背景知识
Flash 存储器( Flash Memory) 是一种高可靠性、高密度的固态存储器件。 其存储方式是完全非易失性的,掉电后可以保存数据;可以在线写入,并可按页连续字节写入,存取速度快,所以嵌入式系统通常使用Flash 存储器作为存储设备。 但Flash存储器也存在着两个主要缺陷:一是在重写之前必须进行擦除,因为Flash 存储器划分成很多擦除块(SectorOErase) ,对任何一位数据进行修改必须先擦除整个块(Sector) ;二是擦除块的擦除次数有限,当一个块提前达到擦除次数上限时, 将导致整个Flash 存储器无法使用。 所以,目前PC 机上很多成熟的基于磁盘的文件系统在Flash 存储器上使用都存在着不足。
嵌入式系统应具有的特点: 一是高可靠性,在恶劣环境下系统仍能正常工作;二是低消耗,受成本限制系统设计必须量体裁衣,去除冗余;三是高效率,在占用较少资源情况下保证功能需求,这样就要求算法简单,效率高。 而日志文件系统(Log-St ruct ured File System) 在数据更新时无需将数据写入原存储区域,适应Flash 存储器无法进行重写这一特点。 目前,针对Flash 存储器的缺陷而设计的Linux 下的J FFS 文件系统,就是采用简化的日志文件系统。 J FFS 文件系统将磨损均衡集成于清除机制之中,在带来掉电可恢复功能的同时,大大减少了块擦除的次数,提高了文件系统的存取速度和效率。 但是,J FFS 文件系统无法单独使用,或者使用于其它实时操作系统中。 对由于受成本和实时性限制而无法使用Linux 的一些嵌入式系统,也就无法使用J FFS 文件系统。基于上述分析,该嵌入式文件系统适合在开源实时操作系统(如μC/OS-II) 和无操作系统的情况下使用。
嵌入式文件系统原理
在日志文件系统中,一个文件被修改后不是被写入到原来的存储空间,而是被加到所有内容的后面,象日志一样被更新,这就是日志文件系统的基本原理。 由于同一个文件在文件系统中会留下不同的版本,所以系统需要设置一张表标注文件的最新与以前的版本。 在内容不断添加时为不将存储空间占满,系统设计了一种回收机制,回收无效内容占用的空间。
日志文件系统在文件更新时不用将文件写回原来的地址,这对Flash 存储器这种存储介质最为适合。 文中所设计的嵌入式文件系统采用了日志文件系统的设计原理,以及J FFS 文件系统将磨损均衡集成于清除机制之中的方法。 该系统将一个可擦写块平分为多个簇,文件的读写以簇为单位进行。簇的状态有3 种:脏、干净和空。 脏表示所存内容已被置为无效;干净表示所存数据有效;空表示可以写入数据。 文件和目录在该系统中被作为节点,一个节点占用若干个簇,节点中的内容连续存储,但不能越过块边界存储。 该系统设置一个索引节点,保存整个系统的信息,其中包含保存有各簇状态的簇状态表。
每一次文件更新后内容都将被添加至末尾处,索引节点也被更新,总是占用最末尾的干净簇。 回收脏簇时,将所要擦除块中的干净簇重写到空簇中,再进行块擦除。 当内容写至存储体末端,则从头部重新开始循环存储。 所设计的文件系统的操作过程见图1。
ic72新闻中心
嵌入式文件系统设计
Flash 存储器中的存储结构
Flash 存储器中的存储结构见图2。 该存储器中每个簇的第一个字作为簇的状态字,表示此簇是否为一个节点的首簇或空簇。 每个节点的首部存放此节点属性(文件/目录/索引节点) 和节点标识号。
ic72新闻中心
索引节点
索引节点存放该文件系统的大部分信息。 包括32 位的索引节点更新号、一张簇状态表、下一个要被擦除块的块号、给下一个新建节点(文件或目录) 的节点编号、系统根目录信息表。系统每一次更新都会产生新的索引节点,索引节点更新号加1。 按照Flash 存储器的使用寿命10 年计算,需要每秒更新136 次以上,才能达到索引节点更新号的上限,所以认为拥有最大更新号的索引节点为最新的索引节点。 簇状态表中对应每一个簇有两个Bit 位,表示各个簇的状态(干净01 ,脏11 ,空00) 。 根目录信息表存放根目录下的各个目录项,每个目录项包括:属性(文件0x1/目录0x0) 、文件名或目录名、节点编号、此文件(或目录) 对应节点的起始簇地址、根目录表的大小可变。
目录节点
目录节点存放的内容有目录名,目录项个数,及所有目录项信息。 文件节点存放文件名,文件大小,文件属性及文件内容,内存中的目录结构见图3。
ic72新闻中心
内存数据结构及基本操作
该文件系统载入(Mount ) 后,会在内存中建立一个系统的映象。 该映象包括:索引节点中的信息、目录及文件信息、每个可擦写块中包含的节点信息、未存盘的节点信息。 簇状态表、索引节点更新号、新节点编号、下一擦除块号等索引节点中的内容,在内存中均作为不同的变量。 内存中为每个文件和目录都建立了映象,数据结构见图4 和图5。
ic72新闻中心
ic72新闻中心
内存中的文件节点不包含文件真正的数据,而使用指针。 文件被打开时,在内存中创建一块新存储区域存放数据,数据指针便指向此存储区,未被打开时,此指针指向空。 对于每个目录有1 个目录层数,表示此目录的深度,如根目录的目录层数为0 ,根目录的下一级目录则为1 ,依此类推。 存储地址保存文件或目录在Flash 中的地址。 文件和目录都被存在上一级目录下,所属目录指针即指向上一级目录在内存中的数据结构,根目录的所属目录指针即为空。 对于同目录下的不同节点,在内存中使用链表将其串联,同目录文件指针即联成链表。 链表的首指针保存在上一级目录中,首目录项指针即指向链表的首项。 为提高块擦写的效率,存储在同一个可擦写块中的各个节点在内存中也建立一个链表,块队列指针即用于连成此链表。 为标识被修改的节点,利用一个未保存队列,未保存队列指针即用来建立此队列。
该文件系统载入(mount ) 时,首先顺序扫描Flash 中的每个索引节点,查找出最大的索引节点更新号,此更新号对应的索引节点即为最新的索引节点。 查找到最新索引节点后,将簇状态表等信息映射到内存的数据结构中。 依据索引节点中的根目录信息,遍历所有节点,建立内存中的目录文件结构,并将节点添加到对应的擦写块队列中。 对一个文件编辑并保存的过程见图6。
ic72新闻中心
文件打开时,先在内存中分配一块空间作为数据区,将内容写入,并定位文件节点的数据指针指向该内存中的数据区。 如果文件内容被修改,就将文件节点添加到未存盘队列,依次写入Flash 存储器中,并修改簇状态表。 保存时将内存中数据区内容写入F 中,释放申请的内存空间,修改节点中的数据指针和簇状态表,再将文件的所有上级目录重新写入Flash ,最后将更新后的索引节点内容写入Flash。 如果文件未被修改,则只需修改数据指针即可。
节点加入未存盘队列的顺序按照目录层数的大小排列,文件节点排在队列首,目录层数最大的排在其后,目录层数为1 的排在队列末尾,根目录不加入未存盘队列。
嵌入式文件系统特殊处理机制
均衡擦写机制
为了避免任意一个可擦除块因擦写次数过多而过早报废,文件系统对Flash擦写时采用了均衡擦写机制。 考虑到系统的精简性,擦写在整片Flash 的各块中依次进行,一块擦写完后,下一个被擦写的块即为后一个块,在系统的索引节点中保存了下一个要擦除的块号。 当文件系统中的剩余空间减少到设定值时,系统会擦除此块,以回收脏簇占用的空间。 对应每个可擦写块都有一个节点队列,此块中包含的节点都加入其中。块擦除的流程见图7。
ic72新闻中心
首先,将未保存于队列中的节点保存,清未保存队列。 然后将块队列中的所有文件节点转移到空簇中,同时将文件路径上的各级目录加入到未存盘队列中。 对于块队列中的目录节点,则将它和其路径上的各级目录加入未存盘队列中,按照未保存队列的顺序,依次将各个目录写入Flash 中,最后写入最新的索引节点。 因为目录节点加入未存盘队列时,按照目录层数的大小排列,所以按照未保存队列的顺序写入时,可以保证当一个目录要被写入Flash 时,它的所有下级目录已被写入Flash 中。 所有下级目录在Flash 中的存储地址都已确定。当该文件系统的空间将达到存储上限时,可能会出现特殊情况,即废簇回收时,空簇的空间不足,无法将所有干净簇重写。 文件系统为此建立了应急机制,先将文件节点内容存在内存中,这时新建一个临时未保存队列,专门保存文件节点,在块擦写完成后,将剩余的文件节点写入新的空簇中,其算法与图7 所示流程大致相同。 但是,一旦在擦写时断电,会导致该块上的所有数据丢失。
断电错误处理机制
当系统遭遇断电重新启动后,索引节点中的信息会与系统中的状态不符,这时便需要错误处理机制。 错误一般是索引节点中标注的空簇已被写入了数据,错误处理就是将此簇标志为脏簇,并查找下一个空簇重新写入。
多任务处理机制
该文件系统允许同时打开多个文件,在多任务操作系统下,为了避免冲突建立了多任务处理机制。 系统允许打开的多个文件在内存中同时被编辑修改,但是对Flash 写入操作有限制。 处理方法是设立Flash 写入保护区,在此区中只允许当前正在执行的任务执行Flash 写入操作。 实现Flash 写入保护区的方法是建立一个初始值为1 的信号量,当一个节点需要Flash 写入时,首先申请信号量,完成后再释放信号量。 Flash 写入保护区见图6 、图7。在图6 中,空操作语句是用来对多个文件的保存进行同步。 例如,有文件1 和文件2 需要保存,先将文件1 的内容写入Flash 中,文件1 路径下的目录节点被添加到未保存队列中,再将文件2 的内容写入Flash 中,文件2 路径下的目录节点也被添加到未保存队列中,最后将未保存队列中的所有节点都写入Flash 中。 这样,如果同一路径下的两个文件同时存盘,可避免路径下的相同目录节点被写入两次,从而提高了效率。 不足之处在于,如果很多文件同时存盘,会导致索引节点在一段时间内都无法写入Flash 存储器,有断电丢失的危险。 但对于一般嵌入式系统来说,很少会碰到这种情况。 当进行Flash存储器擦写时,在取块队列首节点至索引节点写入完成这段时间内都不允许进行其他Flash 存储器的写入操作,这是为了保证数据的完整性,同时也提高了文件系统的稳定性。
无目录文件系统的优化
许多嵌入式系统设计中虽没有目录管理的要求,但是对执行效率和资源消耗的要求较高。 对于不要求有目录管理的精简文件系统,在设计时也进行了优化。 精简文件系统在Flash 中的存储格式与上述设计相同,文件系统中的所有文件信息都保存在索引节点的根目录信息表中。 精简文件系统在内存中的映象则要简单很多,只包含索引节点中的信息,包括簇状态表、下一个擦除块、下一个新节点的标号和根目录信息,而不用为每个文件都建立内存中的映象,节省大量的内存空间。 文件的编辑存盘过程简化为:打开文件、编辑、将文件写入Flash 存储器、将修改后的索引节点写入Flash 存储器。 擦写则只需通过查询根目录信息表中的各个目录项,将块中的所有文件节点写入空簇即可。在无目录管理的情况下,精简文件系统占用的内存资源可以减少,操作也可便捷,提高了效率。 对于大量只需要按名存取的简单文件管理的小型嵌入式系统而言,针对Flash 存储器的简单文件系统将占用资源少,执行效率高,有很大的应用价值。
嵌入式文件系统实现及性能分析
该文件系统的实现采用了分层方法,分为3 层4 个部分:应用程序接口、文件系统核心、操作系统调用接口、Flash 存储器驱动,实现结构见图8。
ic72新闻中心
实现平台中RTOS 为μC/OSOII 实时操作系统,CPU 使用三星S4510B作为处理器,Flash 存储器芯片为FUJ ITSU 的29LV160 TE。 针对不同的实时操作系统和Flash 存储器芯片需要实现不同的操作系统接口和Flash 存储器驱动。
针对μC/ OSOII 编写操作系统调用接口,包括5个函数: ①系统调用接口初始化FS_Sys_Interface_Init ( ) ,创建互斥信号量和内存分区; ② Flash 写入关闭FS_Sys_Write_Lock ( ) ,禁止Flash 写入操作,调用μC/OS-II 中OSMutePend ( ) ; ③ Flash写入打开FS_Sys_Write_Unlock ( ) ,重新允许Flash 写入操作,调用μC/OS-II 中OSMutePost() ; ④内存空间申请FS_Sys_Mem_Alloc( ) 和内存空间添加FS_Sys_Mem_Add ( ) , 都调用OSMemGet ( ) 来完成; ⑤内存空间释放FS_Sys_Mem_Free ( ) ,调用OSMemPut ( ) 完成,将申请的内存块全部释放。针对29LV160 TE 这款Flash 存储器芯片,定义一个FlashDef 结构体的全局变量, 用于存储Flash 器件信息,并且编写针对此款Flash 的块擦写函数FS_Device_Sector_Erase ( ) 和数据写入函数FJ FS_Device_Write ( ) 。
完成这两部分的实现后,该系统就可运行调试。 测试应用程序接口(API) 。 应该提供的各部分功能,并在突然断电情况下,测试文件系统的恢复情况。无目录管理的精简文件系统的载入,可在2μs内完成,文件写入耗时主要为闪存的等待时间,系统本身只占用不到200 个字节的内存,产生的代码段大小为7 K。 完整的文件系统载入时,需要建立内存中映象,耗时根据文件数量的多少而不同,一般为10μs ,产生的代码段大小为11 K。 系统写入效率较高,在无目录管理的配置下尤其明显。 试验中系统在多次断电的情况下,系统仍能恢复至上次存盘的状态,虽会导致个别文件未更新,但不会导致文件系统崩溃。
间的关系熟悉吗?的和(并)。互斥事件(互不相容事件
⑻ 操作系统-文件系统
人们对信息有存储的需求,早期计算机信息在保存在纸带上,存和读都不方便,且容量很低,而存储信息的需求未能得到满足,到了磁盘存储器的出现,对程序和数据等信息的管理的发展才得到质的飞跃。出现文件系统是需要把信息以一种单元,即文件的形式,存储在磁盘或其它外部存储介质上,导致了文件系统的出现。
文件系统是操作系统中统一管理信息资源的一种软件。它管理文件的存储、检索、更新、提供安全可靠的共享和保护手段,并且方便用户使用。从用户的角度看,文件系统负责为用户建立文件、读写文件、修改文件、复制文件和撤销文件等,还能对文件按名存取。
文件是一组带标识的、在逻辑上有完整意义的信息项的序列,这里的标识就是文件名,”信息项“构成了文件的内容。
外存是相对内存而言,主要用来存储信息,其特点是断电后仍可保存信息,容量大,速度较慢,成本较低等
外存储设备通常由驱动部分和存储介质两部分组成,存储介质又常被卷。
存储介质有:磁带、磁盘、光盘、闪存
其中磁带是顺序存储,只能读了前面磁带的内容才能读后面,存取都一样,不能跳着读取,因此磁带适合存储不经常变化的内容,比如放歌。
磁盘支持随机读取,磁盘由带有读写磁头的机械臂和磁盘组成,磁盘像光盘,上面有磁性材料。系统对磁盘初始化时,会划分出一些同心圆,称为磁道,信息只能存储在磁道上,磁道分会被分成多个弧段,称为扇区,每个磁道有4-32个扇区。使用时,驱动器的马达带去磁盘高速匀速旋转,磁头一直停留在盘面表面上方并可以在不同磁道移动,当找到目标磁道时,碰头不动,磁盘依然转动,这时经过磁头的信息就被读出来可写进去。
光盘是激光作用下材料变化的非磁记录介质。
闪存是电荷擦除,支持随机存取,没有机械运动部件,寿命和可靠性高。
文件可以从不同的维度来进行分类:
按用途的方式分类:
按文件组织形式分类:
文件逻辑结构就是用户所看到的文件组织形式,文件逻辑结构是经过抽象的结构,所描述的是文件中信息组织形式。按逻辑结构可以把文件划分成三类:无结构的字符流式文件(由字节组成)、定长记录文件和不定长记录文件(由记录组成);
文件的物理结构是指文件存储在外储设备上的结构,有三种存储结构:顺序存储、链式存储、索引存储;
顺序存储:文件存在连续的空间上,只要知道到起始地址和长度就可以读取文件。
优点:支持随机存取、
缺点:不支持动态扩充,容易产生碎片。
链式存储:文件存在不连续的物理块中,文件控制块保存第一个物理块的指针,之后每个物理块都有一个指针指向下一个物理块地址,如FAT文件系统
优点:可以动态扩充,提高磁盘利用空间;修改添加快。
缺点:
1.可靠性低。若其中某个物理块出错会导致后面全部块读取不到。
2.存取速度慢,不适于随机存取文件,需要从首个物理块一直读取到物理块;
索引存储:使用一张表来存储索引,每个索引指向逻辑文件的信息块。
优点:可以动态扩充,支持随机存取;
缺点:较多的寻道次数和寻道时间;索引表本身增加了存储空间的开销。
文件目录主要是用途是为了管理和索引文件,其结构简单说是一张表,表中存储着文件名、文件控制块、物理地址,通过文件名可以快速的读取到对应的文件。
一级目录是一张线性表,优点是:结构简单、实现简单;缺点:无法解决不同用户的文件名相同;文件多时查找慢。
二级目录是分为主目录和用户目录,主目录给出所有用户目录所在物理位置; 而用户目录则给出所有文件的FCB;优点:不同用户文件可以重名、查找速度比一级目录快、能实现文件共享
多级目录(树形目录)除了最低一级物理块装有文件信息外,其它每一级的目录存储的都是下一级的目录或文件说明信息,多级目录存在唯一的概目录。优点是层次清楚、解决文件重名问题、查找速度快。
目录是指文件路径。
目录项是是文件控制块以一条记录的形式存储在目录文件中。
目录文件是多个文件控制块集中在一起形成的文件。
参考:《操作系统》机械工业出版社 2017年版