问题:原来的存储系统都是基于慢的,块设备接口。但是新的存储介质是基于字节,永久性存储技术,提供快的,细粒度的访问。
解决方案:基于新的存储介质,我们设计了一个文件系统BPFS,以及硬件结构。文件系统的设计是基于新介质的字节寻址和持久性。
具体实施:文件系统使用了短周期的影子分页法去提供原子性,更细粒度的写。在硬件上,我们的硬件结构强制实施了原子性和顺序性原语。
(影子分页法:当用户对一个页进行写时,系统会为其分配一个影子页,由于影子页不被其他页面引用,所以它能随意修改,不会对数据一致性产生影响。当该页面修改完成,所有指向原页面的引用都被更新到新页。影子分页法保证了操作的原子性和持久性。)
结论:BPFS为系统提供了更强的可靠性保证(短周期影子分页法提供了原子性和持久性)以及更好的性能(基于新介质按字节寻址和持久性)。
实验:测试BPFS在DRAM上(因为新存储介质是按字节寻址,跟DRAM一样),与NTFS在RAMDISK上和disk上做对比。然后我们使用我们自制的微结构仿真器去评估BPFS在PCM上的性能。实验发现,运行在DRAM上的BPFS通常比RAMDISK上的NTFS快2倍,比磁盘上的NTFS快4~10倍。
(NTFS:new technology file system,就我的理解层面,最大的好处就是能单次存储超过4G的文件(为什么能支持更大的文件).
具体:1.NTFS可以支持的分区大小达到2TB,而FAT32支持32GB
2.NTFS通过事务处理日志和恢复技术来保证分区的一致性
3.更好的安全性,NTFS分区上,共享资源,文件夹,文件设置访问许可权限,ACL:访问控制列表,哪些用户,哪些组可以获得访问,以及获取访问权限的级别
4.磁盘空间的管理:FAT32,分区为2GB~8GB时,簇大小为4KB,8~16GB,簇大小为8KB,16~32GB,簇大小为16KB。而NTFS在大于2GB时,簇大小都是4KB,所以NTFS可以最大限度的避免磁盘空间浪费)
1.INTRODUCTION
For decades,computer systems have been faced with a trade-off between volatile and non-volatile storage.All persistent data must eventually be stored on non-volatile media such as disk or flash.but since these devices support only slow,bulk data transfers,persistent data must be temporarily buffered in fast,byte-addressable DRAM.Unfortunately,data that resides only in volatile memory can be lost during a crash or a power failure,which means that existing storage systems often sacrifice durability,consistency,or performance in balancing their use of these two types of storage media.
文件系统是存储的主要抽象。BPFS become durable on persistent storage in the time it takes to flush the cache(safety),and that each file system operation is performed atomically and in program order(consistency).
BPFS使用新技术,叫做短周期影子分页法,提供原子性和持久性保证。在传统的影子分页文件系统中,文件系统的更新要触发从修改位置到文件系统树根位置的联级写时复制操作,当文件系统的根位置被更新,改变才被提交
。(
论文Concurrent Shadow Paging in the Flask Architecture 有影子分页算法的详细描述,其中提到after-look shadow paging/before-look shadow paging:after-look的影子分页法是当内存中的页面产生修改时,系统为其在disk上分配一个影子页,之后内存对应页面的修改将刷到影子页中。在memory和disk上都存有paging table,记录页面的对应关系。当被修改的页完全刷到磁盘,在memory中的页表也会刷新到disk中。before-look的影子分页,当内存页面产生的修改刷到disk上之前,disk就分配一个影子页存放原来页面的数据,内存的修改直接刷到原页面中。当系统crush,影子页必须用原始页面代替修改的页面,丢弃原影子页面。)短周期的影子分页法能够更细粒度地使用影子分页法,在文件系统任意一级
原子性地提交一些小的改变
。事实上,BPFS通常可以避免完全拷贝,因为其是不牺牲性能的就地写更新。、
短周期影子分页法是由两个简单的硬件原语来实现的:8bytes的原子写和epoch barriers。
BPFS不同于传统文件系统的三个特性:
- 从架构的角度来说,它与内存放在同一级,使用内存总线,内存不用在缓存数据,可以free内存做别的用途
- 从技术角度,对细粒度原子写做了优化(短周期影子分页法)
- cache buffer代替了DRAM buffer
2.设计原理
2.1 BPRAM直接与cpu连接
因为,1.BPRAM的读写延迟大概是几百纳秒,而SSD,FLASH的读写延迟大概是十几毫秒,若把其放在IO总线上,会浪费其性能好处。2.IObus 阻止我们使用按字节寻址的访问方式,只能使用基于块的访问方式。所以BPRAM直接放在内存总线上,与DRAM放在一级。64位物理地址空间被分割给易失内存和非易失存储使用,使得CPU可以直接寻址BPRAM,进行普通的存取操作。这种结构使得我们可以使用按字节寻址,同时保证了低的访问延迟,很好发挥了BPRAM的性能。
坏处:
1.BRRAM的数据传输可能会对DRAM的访问造成影响,从而损害系统性能。
2.在一台机器里,可用的BPRAM的大小,受到了BPRAM的密度以及空闲的DIMM插槽个数的限制
3.放置持久存储在内存总线会造成更多的写不一致?
2.2 在硬件上执行顺序和原子性
为了保障安全性和一致性,文件系统必须知道什么时间以什么样的顺序写是耐用的。然而,现存的为易失存储设计的cache缓存和内存控制器都有可能重新排序写去提高性能,同时现在大多数的结构(包括x86)都没有提供阻止重排序的机制。
取而代之,我们建议一个软件机制去声明硬件约束的顺序。在我们的计划中,软件能够发行写障碍,划定一组写叫做epoch,硬件将保证每一个epoch按顺序写回到主存中,即使在一个epoch中的单独写都会被重新排序。这种方法将排序和耐用性之间去耦合。我们的方法允许我们去执行排序,当脏数据一直留在内存中。我们的方法要求相对简单的硬件修改,同时提供一个强大的原语,使得我们可以建立高效可靠的软件。
除了排序约束,文件系统一般不得不面对一个简单但是又难懂的原语缺乏:失败原子性,或者在断电情况下,写到持久存储的原子性。随着排序问题的发展,现存系统只为易失存储设计,这儿有大量的机制去执行多线程或者多核的原子性,但是没有执行断电原子性的机制。因此,如果一个到持久存储的写被断电中断,内存可能遗留在中间状态,违背一致性原则。一些日志文件系统在事务记录上使用checksums来实现原子性;然而,在BPRAM上,我们能直接在硬件上提供一个简单的原子性写原语。我们之后会讨论,实施这种失败原子性要求有至少300ns的可用能源在电容器中。值得注意的是,除非特别说明,在这篇文章中,主要参考失败原子性。
在我们的试验中,这种原子性和排序的方法有一个有用的分工,在硬件和软件之间。在原子性方面,硬件实施是极其简单的,它极大地简化了在BPFS上实施一致性的任务。对于排序,epoch barriers允许软件去降低约束在抽象自然层,这种信息是充足的,对于硬件来说,去缓存写到持久数据。其实,我们相信这些原语将找到使用,在很多超出BPFS的软件应用方面。
2.3.使用短路影子分页法
大多数存储系统保证可靠性,通过使用这两种技术中的一种:写前日志或者影子分页法。写前日志,存储系统写操作,它打算执行一个单独位置(通常是顺序文件),在更新原存储位置之前。虽然,很多写被完成两次:一次是写到日志,然后写到最后位置。这个方法的好处是第一次写到日志是很快完成的,不需要覆盖旧数据。然而,开销在于很多写必须被执行两次。事实上,对于所有文件系统的数据来说,使用这个技术的开销是巨大的,以至于大多数文件系统缺省条件下只对元数据进行日志。
(WAL:预写式日志,用于提供原子性和持久性的技术,在使用WAL的系统中,所有的修改在提交之前都要先写入log文件中,log文件中通常包含redo,undo信息。假设一个程序在执行过程中断电了,在重新启动的时候,程序可能需要知道当时执行的操作是成功还是部分成功或者失败,如果使用了WAL,程序就可以检查log文件,并对突然掉电时计划执行的操作内容跟实际执行的操作内容比较,比较的过程中就可以知道是该撤销已做操作,还是继续完成已做操作。在文件系统中,WAL成为日志)
相反,影子分页法使用写时复制去执行所有更新。以至于原始数据不被接触当更新数据写到持久存储。数据通常被存储在一棵树上,同时新数据通过cow写,父块也必须通过cow更新。当更新传播到树顶,对树的根路径的单个写提交所有更新到实时存储。更新通常被不频繁提交,批提交,为了分摊复制开销。
总结,很多可靠的存储系统使用两种中的一种:快速更新到日志,但是需要说明的是很多写被完成两次,或者写时复制更新,必须批处理为了分摊复制的开销。基于disk的文件系统通常利用日志,因为影子分页的复制开销超过了日志开销。
在BPRAM情况下,虽然字节寻址,速度快,但是随机写使得影子分页是一个有吸引力的方法对于文件系统的设计。事实上,BPFS超出了传统影子分页系统,通过实施一个新的技术,我们叫做短周期影子分页法(SCSP)。SCSP允许BPFS去提交更新在任何位置在文件系统树上,避免了传播复制到文件系统根路径的开销。BPFS能够就地执行小的更新,不需要执行全部复制,即使对于大量写复制被需要,他们能限制在一个小的文件系统子树中,只复制老数据的一部分。SCSP使得在硬件上的原子性写变得可能,同时使得使用我们的cpu缓存的epoch感知变得更快。
3 BPFS的设计和实施
BPFS的数据结构是固定空间大小的块组成的简单树。有两个好处,1.从根节点到任意被给定的节点都只有一条路径,我们能够使用原子指针写更新树的任意位置。2.所有的块都是一样大小的,分配和重新分配是简单的。
文件系统包含3种类型的文件:1.inode文件2.目录文件3.数据文件
inode文件是由一组固定大小的inode结点组成,每一个都独一无二地代表了文件系统中的一个文件或者一个目录。根节点文件代表了整个文件系统的根。inodes包含了文件元数据,包括根指针和对应文件的大小。目录文件包含了一组目录项,目录项由inode号和对应文件的名称组成。当其包含非零的inode号时,目录项就是有效的。数据文件只包含用户的数据。
文件系统的整体架构如图所示,三种文件都使用了同样的树形结构。块大小是4KB。树的叶子节点代表了文件数据(用户数据,目录项,inodes)。每颗树中间的节点包含了512个64位的指针指向树的下一层。
文件系统能指示的文件大小由inode树的高度决定:当树的高度为0时,根指针直接指向数据块,包含4KB的文件数据。当树的高度为1时,根指针指向一个512个指针的块,包含512*4KB=2MB的文件数据。树的高度为2时,包含1GB的数据,为3时,包含256GB的数据,为4时,包含256TB的数据。
为了简化写到一个文件中间部分的写任务,在树的任意层我们都使用空指针来代表指针指示的文件范围都为0.比如,文件的根指针是空指针,树的高度是4,它代表一个256TB的空文件。空指针同样能够出现在中间节点,所以对这个256TB文件末端的写不会使我们写256TB的0.
文件大小可以比树本身表示的数据量更大也可以更小。如果文件更大,则文件尾部都变为0.如果文件更小,则超出文件部分的数据都被忽略。比如,如果我们有一颗高度为1的树,存储文件大小为2MB,一个文件的大小为1MB,那么前256个指针的节点指向有效数据,后256个指针被忽略,可以包含任何信息位。这种设计使得我们不需要升级树就能改变文件大小,同时允许就地附加。
3.1 持久型数据更新
短周期影子分页法包含了三种方式来更新持久型数据:1.就地更新 2.就地附加 3.部分写时复制。使用按字节寻址,8bytes写的原子性原语在保证一致性的前提下对文件系统提供快的更新。
就地更新是可用的方式里最有效的。对于数据文件,就地更新只能在写64bits或者更少bits时被执行(原子性原语提供8bytes的原子性写)。
就地附加:文件的大小被记录在根指针结点里。因为所有超出文件系统大小的数据都将被忽略,所以我们能够放心地就地写,一旦所有的数据都被写入,我们能原子性地更新根节点记录的文件系统的大小去扩展文件系统的有效数据范围。例子:我们首先写超出文件末尾的数据,然后我们更新文件系统的大小。如果在更新文件大小之前发生了冲突,任何未完成的附加操作都将被忽略。
部分写时复制, 这是用来更新持久性数据的最通用技术。例如,用户想要写数据的内容跨越了第三和第四数据块。为了实现这种写操作,我们在BPRAM中动态分配新块,复制现有的数据(从第三个数据块开始),然后更新新块的数据。我们一样复制更新不能被原子性更新的任何指针块。只有当更新全部完成,我们通过执行文件树这部分指针的原子性更新来提交这一改变。
实际上,这些写时复制操作室非常有效的。一个原因是我们只复制不被重写的那部分数据。比如下图,我们不需要将第四个数据块上的数据复制到新分配的块上。
3.2 易失数据结构
我们存储简单,不冗余的数据结构在persistent memory中,我们以更有效的易失数据结构缓存这些数据,对于性能而言,这是必要的。
首先,我们存储free BPRAM块列表和free和allocated inode号列表在DRAM 中,这些数据结构在文件系统启动的时候,从文件系统元数据当中初始化。(但是由于这个扫描是在BPRAM中执行的,所以时间是几分之几秒)我们避免存储这些数据结构在BPRAM中,因为对于BPRAM而言,提交小的修改到文件系统是困难的,而这些全局表都是需要原子性提交的。
其次,我们在BPRAM中存储写时复制时释放和已分配的块列表。例如,当执行写操作时,我们保持追踪任何新分配的块和在写操作成功之后将被释放的块。当写操作成功,我们遍历释放列表和已分配列表,将这些块加入到全局块free列表中。
第三,我们存储每个被用户打开的目录的目录项缓存在DRAM 中。每个目录项被同时存在列表和hash表中(HASH表用于快速判读目录是否存在,列表用于查找对应目录的目录项),使其能够支持快速和有序的目录列表,和快速的目录项查找。任何对于目录的修改都直接反应到persistent memory。
因为这些数据结构都只被存储在易失内存中,我们不需要使用原子性写去更新他们。他们通过传统文件锁的方式进行同步。BPFS的原子性操作也需要传统的文件锁。若没有文件锁,如果线程A开始写时复制操作,而线程B执行8BYTES的就地写到原页面上,当A线程提交之后,写数据可能被丢失。
3.2 文件系统操作
因为文件系统都使用BPFS树数据结构,我们的实施方案由一组核心例程,我们为其命名为crawler,它能够遍历结点和文件数据树,对三种文件执行读或者写操作。
为了实现文件系统的操作,crawler被给一个根指针(任何类型的文件),树的高度,文件偏移的范围,和回调函数。
打开,读,写,关闭(删除)的流程如visio文件所述。
3.3 BPFS的局限性
BPFS的其中一个局限性在于:在写方面,写时间是不能原子性更新。
第二个局限性在日志方面,跨越树大部分区域的原子性操作要求大量的额外副本,典型例子是move操作。
第三个局限性,BPRAM的接口。我们提供给程序员的是a persistent heap,而不是文件系统的设计。
4 硬件支持
4.1 相变存储
本文中,我们假设基于PCM的存储系统有一组PCM芯片组成,PCM芯片放置在兼容DDR的DIMM上。这么做的局限性在于,PCM的容量被DIMM上芯片的密度限制。比如,2008三星的PCM原型芯片容量为512MB,所以一个高容量的DIMM有16个芯片,现在每个DIMM可以达到1GB。如果容量不够,我们除了把PCM放在DRAM总线上,还能够把PCM放在PCI-E的总线上。但是在这篇文章里,我们还是假设,由内存总线访问PCM。
4.2 写均衡或者写失败
虽然PCM有比NAND更高的写寿命,但是对同一个cell大量写,一样会造成该cell的磨损。2007年工业界的共识是,到2009年,PCM单元的写次数为10^8,到2012年将上升到10^12.虽然PCM的写次数是高于其他非易失存储介质的,但是本文将PCM放置在内存总线上,可能会使PCM遭受更大量写行为,因此要求写均衡。这个过程是,分布写到整个设备上以降低任何单个位置上的写磨损。虽然我们的文件系统并不特别集中地更新一个位置,但是对一些负载还是存在热点问题的。
幸运的是,有几种写均衡方法可以被我们的文件系统独立使用。第一,我们能以最小化写的方式设计PCM数组,把设备寿命从525小时延迟到49000小时(5,6年)。第二,PCM各种写均衡机制已经被提出了。总之,有效的写均衡策略能够用以下两种技术实现:1.磨损被均衡通过在内存控制器和页间旋转bits,2.通过阶段性地交换虚拟页到物理页的映射来实现磨损均衡。这些工作表明,独立于BPFS设计合理的写均衡策略是可行的。
当出现写失败的故障的时候,我们希望通过校验码来探测他们。我们能够利用现有的flash校验码。当PCM页产生写失败,操作系统能够回收PCM页,复制数据到新的物理页,然后更新页表。当然,如果PCM页上有足够多bits写失败,数据将被丢弃。在本文中,我们假设PCM硬件有足够的冗余设计,去确保写失败可以忽略。
5 评估方法
我们的评估要回答以下几个问题:
1.
BPFS
的执行是否比基于磁盘的文件系统好
2.
我们建议的硬件修改有什么性能好处
3.
由PCM支持的BPFS是否表现得比使用DRAM cache+disk系统更好
评估步骤:
ps:文件系统吞吐量测试:创建100文件,执行5000读写事务,删除所有文件。
计算密集型 (CPU-bound)
在多重程序系统中,大部份时间用来做计算、逻辑判断等CPU动作的程序称之CPU bound。例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中
绝大部份时间用在三角函数和开根号的计算,便是属于CPU bound的程序。
It is because the performance characteristic of most protocol codec implementations is CPU-bound, which is the same with I/O processor threads.
根据以上分析,可以认为通常情况下,大部分程序针对某个特定的性能metric而言
都可分为CPU bound 和 I/O bound两类。
CPU bound的程序一般而言CPU占用率相当高。这可能是因为任务本身不太需要访问I/O设备,也可能是因为程序是多线程实现因此屏蔽掉了等待I/O的时间。
而I/O bound的程序一般在达到性能极限时,CPU占用率仍然较低。这可能是因为任务本身需要大量I/O操作,而pipeline做得不是很好,没有充分利用处理器能力
不理解的问题:
1.在2.1部分,把BPRAM与DRAM放在一起,有三个缺陷,第三个缺陷不是太理解(写不一致?)
2.在3.1部分,为什么中间节点不使用空指针,写文件末尾就要把文件都写完。
3.在3.3部分,为什么不允许存储一些复杂的数据结构在persistent memory?
4.在3.4,3.5部分的多核CPU上的顺序执行和BPFS的局限性都不是很理解。
5.PCM写均衡的两个重要技术查一查