说明:该系类文章更多的是从从哲学视角看 操作系统 这门学科。同时也是 操作系统的学习笔记总结。因为博主 这些年主要是以研究安卓系统和 嵌入式Linux为主,因此这个系类文章也是这两个领域不可或缺的基石之一,尤其是对操作系统感兴趣的伙伴可特别关注。
18 文件系统性能
18.1 文件授权管理
文件系统需要达到的两个目标,一个是地址如何独立,一个是地址保护如何实现。上一节 文件系统的实现 主要是地址如何独立的实现,而这里主要说明地址保护如何实现。对文件里面的数据进行保护,使得文件数据不能被随意访问就是文件系统必须解决的一个问题,而文件系统解决这个问题的方法就是文件系统的访问控制(Access Control)。访问控制是如何实现?
人类社会对贵重设施的保护方式与此类似;例如,军事基地或关键设施的保护通常可以分为两种模式:在军事禁区设置哨兵。欲访问军事禁区的人必须经过哨兵的检查。即哨兵根据某种规则来确认某个人是否叮以访问禁区。另外一种办法是设置安全门,有权利访问的人片备有进人安全门的钥匙。如果某个人需要进人禁区,他只需要拿出自己的钥匙将安全门打开即可。如果打不开,则不能访问;在这种情况下,一个人可以有多把钥匙,用来访问不同的受保护设施。
如果对上述情况进行提炼,发现这两类保护措施是从两个不同的角度来实施的:
- 哨兵是从被保护的设施角度出发,即控制措施附在设施上。(事实上,采取的措施就是基于此)
- 使用钥匙则是从用户角度来实施的,即访问控制在用户身上。
因此,我们对文件的保护也从两个角度出发
- 文件角度:将访问附在每个个体文件上。
- 用户角度:将访问控制附在用户身上。
18.2 主动控制:访问控制表
@1 主动控制将保护措施构建在被保护者身上。对于文件系统来说,我们为每个文件设置一个哨兵。这个哨兵对每个试图访问该文件的用户进行检查,看其是否能够访问。而这个检查的规则存放在一张表里面。这个表我们称为访问控制表(ACL)。这个表存放在内核空间,对于每个具有访问权限的用户设置一个记录。该记录记载着用户ID和具体的访问权限。凡是不在这张表上的用户将不具备访问资格;3个用户进程和3个文件的访问控制表如图所示:
这样,在每次访问前,操作系统将检查这个访问控制表来确认一个用户是否有权执行其所要求的操作。如果该用户ID在访问控制表里,其权限与其所要求的操作匹配,则操作将被允许。否则,该操作将被终止。(这里需要注意的是访问控制表是存放在内核空间,由操作系统进行设置与访问。用户不能直接修改访问控制表)
注意:访问控制表里的用户可以是个体用户,还可以是集体用户(用户组);这里的文件也可以是文件,也可以是文件夹。
@2 访问控制表的优缺点:
- 优点:容易理解,容易实现,操作简单
- 缺点:效率低(每次访问文件,都需要访问整个控制表,对于不支持组的系统来讲,浙江十分繁琐)、不可靠(容易被攻破,只要让系统误认为他是某个具有访问权限的用户)
18.3 能力表
@1 另外一种访问控制手段是将控制措施附在用户身上。即每个用户手上拿一把钥匙,这把钥匙就是用户其有的能力(capability)。由于一个用户可以拥有对于多个对象的访问能力,这些能力放在一起就形成一张表,我们称为能力表。这个表为所有该用户具有访问权限的文件设置一个记录。该记录记载着文件名和具体的访问权限(读、写、执行、复制等)。而凡是不在这张表上的文件该用户将不具备访问资格。如图所示,显示的是3个用户和3个文件情况下系统维护的3张能力表。
这样,在每次访问前,操作系统将检查这个能力表来确认一个用户是否有权执行其所要求的操作。如果该用户所要访问的文件在其能力表单,且其权限与所要求的操作匹配,则操作将被允许。否则,操作将被终止。
注意:能力表里的用户可以是个体用户,还可以是集体用户(用户组);这里的文件也可以是文件,也可以是文件夹。
@2 能力表的优缺点:
优点:
- 效率高(如果用户要访问的文件是能力表所指向的文件,则无需进行任何检查);
- 能力表有较好的封装。用户和他所能够访问的文件存放在同一个列表里面;
缺点:
- 删除文件对象困难。如果要禁止所有用户对某个文件的访问,则需要对所有用户的能力表进行遍历。
- 不可靠(容易被攻破,只要让系统误认为他是某个具有访问权限的用户);
解决方法:使用tagged结构(在每个内存字设置一个额外的字位,用来表示该内存字是否包括能力),如果该标志被设置,则对该字的写操作只能由操作系统进行(为了保护能力表,我们可以将其置于操作系统的管辖内。显然,这种实现需要硬件的支持)。可以对能力表进行加密,这样可以将能力表置于用户空间,但用户需要解密钥匙才能使用能力表,从而防止篡改。
18.4 访问控制的实施
当访问控制表和能力表很大的时候,执行访问时间的成本将加大,这时就需要优化时间,而优化时间的方法就是保护域。访问控制表和能力表的共同缺点:针对个体的文件需要设置个体的访问控制。如果某些文件或对象的访问控制权限一样,则这种个体区别的记录方式就显得有点浪费了。这个时候解决问题的方法:使用保护域。
保护域:将访问控制权限一样的文件和对象组织成同一个域;而访问控制权限与每个域直接相关。每个域的控制独立于其他域,每个进程都运行在一定的域里,从而具有访问该域里面文件和对象的权限,如图所示,描述的是具有3个保护域的系统:
一个进程在运行时必须处于某个域;且在运行中可以改变保护域(例如从用户空间切换到内核空间),系统跟踪保护域的方式就是使用矩阵,如图所示:
- 使用保护域的优点:将保护权限相同的对象组成一个集合来进行处理;
- 使用保护域的缺点:实现方式可能浪费空间。(图中有许多的空白域;如果这种浪费不可以,那就只能用表了)
注意:访问控制不止是对文件,对数据对象同样有效。
18.5 其他文件安全措施
访问控制能够保证文件不被未授权的用户/进程访问,但是这种方式容易被攻破因此需要采取其他的措施:
- 加密:对数据进行加密,转换,使得转换后的数据无法辨认,只有解密才可以。
- 数据隐写:将重要数据嵌入到其他类型文件中而达到隐身的目的。
- 数据隐藏:挂载文件系统外边,使得入侵者找不到数据。
18.6 文件系统性能
在满足了地址独立和地址保护后,需要做的就是满足用户在性能上的需求(主要体现在可靠性和速度上)。
18.6.1 文件系统可靠性
主要体现在持久性和一致性上:
- 持久性:数据经久耐用,很长一段时间里不会变化
- 一致性:数据的有效性,不与客观事实相悖。
18.6.2 文件系统持久性
对于操作系统,对文件的持久性采取的措施就是数据备份,这样源出现问题时就可以利用备份;数据备份分为两类:逻辑备份和物理备份。
- 逻辑备份:备份用户选择好的文件/文件夹,基本单位是文件。
- 数据备份:备份整个磁盘,备份的单位是字节/数据块。(这些操作是绕过文件系统的,即通过裸读和裸写来实现)
相比之下,逻辑备份更容易被人接受,原因是:
- 物理备份是通过裸读和裸写来实现,而这比在文件系统上实现要困难。而且物理备份不知道语义,纠错困难。(物理备份会造成卷标相同,这将造成备份后磁盘无法工作)
- 逻辑备份是使用文件来读写,无需与磁盘驱动器这些东西打交道。逻辑备份知道语义,纠错方便。
18.6.3 文件系统一致性
一致性要求数据有效,不与客观事实相悖,这主要涉及文件系统上的数据同步,而保持数据同步的机制有以下几种:
- 日志:将文件操作全部记录下来,但是注意,这里记录的是操作结果而不是操作过程。
- 事务:一组操作,确保一组操作要么全部发生,要么一个都不发生,即是原子操作;而这也需要硬件的支持(类似于锁)
- 随影:前面2种方式实现了数据一致性的保障,但是效率不高(例如文件在拷贝的过程中不能有其他进程对其进行访问);保持两个数据版本,通过指针告诉用户/应用软件当前使用的是哪一个版本的数据,在更新数据时,将更新写到非当前使用的版本上;更新完成后修改指针指向新的版本。
- 文件系统和用户文件:前面3种方式都需要使用额外的空间和时间,最好的方法是只对一部分重要文件也就是涉及文件系统本身进行数据一致性保护。
- 文件一致性检查。
文件系统一致性包括以下几个方面:
- 没有魅影文件。
- 没有孤儿文件。
- 文件I_NODE里面的链接计数与其出现在文件系统中不同位里的次数一致。
- 没有消失的空间。
- 没有额外的空间。
因此,对文件系统一致性进行检查就是对上述5个方面进行确认。而要对这些进行确认,当然需要保持足够的信息。对于前三点来说 这些信息保存在文件目录和I_NODE里面。后面两点的意思就是一个磁盘块要么在闲置空间里,要么被占用,而不能同时处于两种状态。为了对后面两点进行判定,我们需要维护两张表:一张闲置空间表,一张占用空间表。通过两种表进行相互比对来判断是否存在消失或额外的空间。如图所示:
上图表示一切正常。磁盘块只存在于一张表里面,即要么处于闲置状态,要么被分配给某个文件使用。如果不是以上这种状态则说明空间表出现问题。如果使用一张表则操作系统无法进行对比判断是否出错。
18.7 文件系统效率性能
对于操作系统而言,提高磁盘的访问速度有以下几种方式:
- 将经常访问的内容置于缓存里
- 减少磁臂访问时磁臂移动的距离
- 提前将数据块导入内存
18.7.1 文件缓存
文件缓存就是将文件内容的部分置于缓存,从而以从缓存满足用户的文件访问请求,而无需每次都到磁盘里去读写。这样可以大大提高访问效率。而放入缓存的数据既可以是用户数据,也可以是元数据。文件缓存的实现有两种方式:
- 缓存在虚地址空间:将缓存映射到虚拟内存地址空间,即将工作交给MMU。
- 缓存在实地址空间:由文件系统自己管理,可以确保需要的数据总是在缓存里,但是文件系统将变得复杂。
18.7.2 虚拟内存和文件缓存
虚拟内存与文件缓存的异同:
- 虚拟内存的目的:提供一个速度高,容量大的,但是实际上并不存在的内存空间。
- 文件缓存的目的:提高文件的访问效率,即从磁盘出发,为了提高访问效率而将文件置于缓存。
18.7.3 提前读取
提前读取:每次访问磁盘时,多读一些数据。主要依据是空局域性的原则来满足用户的需求;但事实上这只在顺序访问/准顺序房访问时效果最好;对于随即访问,效果并不显著。
18.7.4 减少磁臂运动
磁盘访问的瓶颈是其机械运动部分。即磁臂移动和磁盘旋转。而磁臂移动又占主导地位。由于受运动精度限制,提高磁臂移动速度的空间有限。那么剩下的节省时间的办法就是缩短磁臂移动距离。而缩短磁臂移动距离有两种办法:
- 将文件头(I_NODE)和数据块放置在一起:这样可以提高读取数据的速度,但这对文件一致性检查和清空磁盘的操作造成麻烦,因此未被采用。
- 将文件读写操作进行适当排序:利用磁盘的访问调度算法,即磁臂最小移动原则来排序。
18.8 文件系统设计分析:日志结构的文件系统(Log-Structure File System,LFS)
要提高磁盘访问效率,就需要提高磁盘的写操作效率,那么磁盘的写操作效率的瓶颈:寻找适当的写位置。改善此处的效率呢:显然,如果我们在磁盘写操作时不用寻找写的位置,则磁盘写操作效率将大大提高。
UCB正是看到了这点而设计出了日志结构的文件系统。在该系统下,整个磁盘被当作一个巨大的日志看待。所有的磁盘写操作从日志的尾端开始。这样就避免了寻道操作。磁盘访问主要是写操作,而每次写从上次写毕的位置直接开始。
当写到磁盘末尾时,我们再从磁盘头接着写下去。即将磁盘看作是一个循环日志。而为了进一步提高写的效率,我们还结合缓存来进行。即不是将所有的写操作随时进行。而是将写操作在内存里缓存起来。然后在系统闲暇时,或者周期性地,将缓存起来的写操作写入到磁盘日志上。
由于每次都写在新的地方,我们需要将I_NODE和文件内容一起写入。那么LFS有没有提高文件系统的效率呢?从表面上看,文件系统效率是提高了。读从缓存中读;写不需要寻道,从而使得读写效率提高但是LFS效率的提高依赖于多个条件的满足:
- 绝大部分读操作可以从内存满足(只有当读的80%都可以从内存满足时,LFS的效率会非常显著)。
- 连续的写之间不会插人磁盘读操作(事实上无法保证)。
- 磁盘的自由空间为连续的(事实上无法保证)。
因此,日志文件系统有利有弊,是否选择页意味着 否能接受这种系统的缺陷。
18.9 海量数据文件系统
@1 海量文件系统最大的挑战就是性能;数据量的增长突破了某一阈值,则将导致文件系统的失效。
@2 海量数据文件的特点:
- 数据量大:由干数据量巨大,在巨大的目录里面迅速找到所要的文件(文件头及其数据块)不是一件简单的事情。
- 分布环境广:海虽文件系统通常构建在分布式环境下,由于用来提供文件系统存储介质的可能是多个磁盘,且可能位于不同的协点上,如何判断所需文件位于哪个点并迅速获取数据就变得很必要。这与在单一磁盘(阵列)上构建的普通文件系统相差甚远。
@3 基于上述两个因素,海量文件系统在性能上面临巨大挑战。如果仍然使用普通文件系统那种将元数据与用户数据一起放置的办法,则搜索起来将费时费力。而避免这种窘况的办法就是将元数据与平而数据分开存放,从而减少搜索的数据量,提升寻找所需文件的效率。如果元数据本身也很大.则还需要将元数据分拆到多个节点存放。这种存放元数据的节点就称为元数据服务器。而存放平面数据的节点就是所谓的数据服务器。
@4 如果上述手段还没有达到效率目标.或者我们还需要进一步提升处理的效率,则可以将目录数据从元数据中分离,构建专门的目录服务器,从而形成目录服务器、元数据服务器、平面数据服务器的三层架构。海量数据文件系统由于数据量变化锁带来的挑战有很多,如何构建合理的海量文件系统还未得到统一的认识。