前段时间在开发一个使用SSD做缓存的系统,在高速写入数据时会出现大量的磁盘缓存。太多的磁盘缓存如果没有及时的写入磁盘中,在机器出现问题时是非常危险的,这样会导致很多的数据丢失,但是如果实时的将数据刷入磁盘中,这样写入效率有太低了。为了弄明白Linux系统的这种磁盘写入特性,最近深入的学习了一下。
VFS(Virtual File System)的存在使得Linux可以兼容不同的文件系统,例如ext3、ext4、xfs、ntfs等等,其不仅具有为所有的文件系统实现一个通用的外接口的作用,还具有另一个与系统性能相关的重要作用——缓存。VFS中引入了高速磁盘缓存的机制,这属于一种软件机制,允许内核将原本存在磁盘上的某些信息保存在RAM中,以便对这些数据的进一步访问能快速进行,而不必慢速访问磁盘本身。高速磁盘缓存可大致分为以下三种:
目录项高速缓存——主要存放的是描述文件系统路径名的目录项对象
索引节点高速缓存——主要存放的是描述磁盘索引节点的索引节点对象
页高速缓存——主要存放的是完整的数据页对象,每个页所包含的数据一定属于某个文件,同时,所有的文件读写操作都依赖于页高速缓存。其是Linux内核所使用的主要磁盘高速缓存。
正是由于缓存的引入,所以VFS文件系统采用了文件数据延迟写的技术,因此,如果在调用系统接口写入数据时没有使用同步写模式,那么大多数据将会先保存在缓存中,待等到满足某些条件时才将数据刷入磁盘里。
内核是如何将数据刷入磁盘的呢?在看完以下两点后就能得到答案。
1. 把脏页写入磁盘
正如我们所了解的,内核不断用包含块设备数据的页填充页高速缓存。只要进程修改了数据,相应的页就被标记为脏页,即把它的PG_dirty标志位置。
Unix系统允许把脏缓冲区写入块设备的操作延迟执行,因为这种策略可以显著地提高系统的性能。对高速缓存中的页的几次写操作可能只需对相应的磁盘块进行一次缓慢的物理更新就可以满足。此外,写操作没有读操作那么紧迫,因为进程通常是不会因为延迟写而挂起,而大部分情况都因为延迟读而挂起。正是由于延迟写,使得任一物理块设备平均为读请求提供服务将多于写请求。
一个脏页可能直到最后一刻(即直到系统关闭时)都一直逗留在主存中。然而,从延迟写策略的局限性来看,它有两个主要的缺点:
一、如果发生了硬件错误或者电源掉电的情况,那么就无法再获得RAM的内容,因此,从系统启动以来对文件进行的很多修改就丢失了。
二、页高速缓存的大小(由此存放它所需的RAM的大小)就可要很大——至少要与所访问块设备的大小不同。
因此,在下列条件下把脏页刷新(写入)到磁盘:
页高速缓存变得太满,但还需要更多的页,或者脏页的数量已经太多。
自从页变成脏页以来已过去太长时间。
进程请求对块设备或者特定文件任何待定的变化都进行刷新。通过调用sync()、fsync()或者fdatasync()系统调用来实现。
缓冲区页的引入是问题更加复杂。与每个缓冲区页相关的缓冲区首部使内核能够了解每个独立块缓冲区的状态。如果至少有一个缓冲区首部的PG_Dirty标志被置位,就应该设置相应缓冲区页的PG_dirty标志。当内核选择要刷新的缓冲区时,它扫描相应的缓冲区首部,并只把脏块的内容有效的写到磁盘。一旦内核把缓冲区的所有脏页刷新到磁盘,就把页的PG_dirty标志清0。
2. pdflush内核线程
早期版本的Linux使用bdfllush内核线程系统地扫描页高速缓存以搜索要刷新的脏页,并且使用另一个内核线程kupdate来保证所有的页不会“脏”太长时间。Linux 2.6用一组通用内核线程pdflush替代上述两个线程。
这些内核线程结构灵活,它们作用于两个参数:一个指向线程要执行的函数的指针和一个函数要用的参数。系统中pdflush内核线程的数量是要动态调整的:pdflush线程太少时就创建,太多时就杀死。因为这些内核线程所执行的函数可以阻塞,所以创建多个而不是一个pdflush内核线程可以改善系统性能。
根据下面的原则控制pdflush线程的产生和消亡:
必须有至少两个,最多八个pdflush内核线程
如果到最近的1s期间没有空闲pdflush,就应该创建新的pdflush线程
如果最近一次pdflush变为空闲的时间超过了1s,就应该删除一个pdflush线程
所有的pdflush内核线程都有pdflush_work描述符,其数据结构如下:
当系统没有要刷新的脏页时,pdflush线程会自动处于睡眠状态,最后由pdflush_operation()函数来唤醒。那么在这个过程中pdflush内核线程主要完成了哪些工作呢?其中一些工作与脏数据的刷新有关。尤其是pdflush通常执行下面的回调函数之一:
1. background_writeout(): 系统地扫描页高速缓存以搜索要刷新的脏页。
为了得到需要刷新的脏页,就要彻底的搜索与在磁盘上有映像的索引节点相应的所有address_space对象(是一棵搜索树)。由于页高速缓存可能有大量的页,如果用一个单独的执行流来扫描整个高速缓存,会令CPU和磁盘长时间繁忙,因此,Linux使用一种复杂的机制把对页高速缓存的扫描划分为几个执行流。当内存不足或者用户显式的(用户态进程发出sync()系统调用等)调用请求刷新操作时会执行wakeup_bdflush()函数。wakeup_bdflush()函数会调用pdflush_operation()唤醒pdflush内核线程,并委托它执行回调函数background_writeout()。background_writeout()函数有效的从页高速缓存中获得指定数量的脏页,并把它写回磁盘。此外,执行background_writeout()函数的pdflush内核线程只有在满足以下两个条件下才能被唤醒:一是对页高速缓存中的页内容进行了修改,二是引起脏页部分增加到超过某个脏背景阈值。背景阈值通常设置为系统中所有页的10%,不过可以通过修改文件/proc/sys/vm/dirty_background_ratio来调整该值。
2. wb_kupdate():检查页高速缓存中是否有“脏”了很久时间的页,避免当一些页很久没有被刷新时发生饥饿危险。
内核在初始化期间会建立wb_timer动态定时器,其的定时间距为dirty_writeback_centisecs文件中所规定的几百分之一秒(通常是500分之一秒,不过可以通过修改/proc/sys/vm/dirty_writeback_centisecs文件调整该值)。定时器函数会调用pdflush_operation()函数,然后将wb_kupdate()函数的地址传入。wb_kupdate()函数遍历页高速缓存搜索陈旧的脏索引节点,把已保持脏状态时间超过30秒的页都写到磁盘,之后重置定时器。
PS:关于SSD的写入放大问题
现在固态硬盘正在越来越多地被作为服务器磁盘使用。之前在SSD(Solid State Drive)上设计并实现缓存系统用于存储数据块时遇到了一些问题,比如在磁盘写满后,如果老化掉一些最久未使用的数据块后,继续大量写入新的数据,随着时间的推移,写入速度变得比刚开始时慢了许多。为了弄清楚为什么会出现这样的情况,于是在网上搜索了一些关于SSD的资料,原来这种情况是由于SSD硬件设计本身决定的,最终映射到应用程序上该中现象称为写入放大现象(WA: Write amplification),WA是闪存及SSD相关的一个极为重要的属性,这个术语在2008年被Intel公司和SiliconSystems公司(于2009 年被西部数据收购)第一次提出并在公开稿件里使用。下面简单讲解一下为什么会出现这种情况以及是怎样一个过程。
SSD的设计完全不同于传统的机械磁盘,其是一个完整的电子设备,没有传统机械盘的读写磁头。因此,在读写数据的时候由于少了磁头在磁道之间的寻道过程所以SSD能提供较高的IOPS性能。也正因为其少了磁头的调度,所以SSD还能减少电量的使用,在数据中心中使用对企业来说是非常有益的。
SSD与传统磁盘相比其有了很大的性能优势,以及较多的优点,但是事物总是有两面性的,其也自身也存在一些问题,写入在SSD中的数据是不可以直接更新的,只能通过扇区覆盖重写,在覆盖重写之前需要先擦除,而且擦除操作又是不能在扇区上做的,只能在磁盘的块上来完成,擦除块之前需要将原有的还有效的数据先读出,然后在与新来的数据一起写入,这些重复的操作不单会增加写入的数据量 ,还会减少闪存的寿命,更吃光闪存的可用带宽而间接影响随机写入性能。
写入放大的解决办法
在实际操作中我们很难完全解决掉SSD写入放大的问题,只能通过某些方法来更有效的减少放大的倍数。一个很简单的办法就是将一块大的SSD硬盘只使用其的一部分容量,比如128GB你只是用64GB,那么最坏的情况就是写入放大能减少约3倍。当然这种方法有点过于浪费资源了。另外还可以在写数据时采用顺序写入,当SSD被顺序写入时,其写入放大一般为1,但某些因素会影响到该值。
除了上面的方法外,现阶段公认的比较好的方法是TRIM。TRIM位于操作系统层。操作系统使用TRIM命令来通知SSD某个page的数据不需要了,则可以回收。 支持TRIM的操作系统和以往的主要区别是删除一个Page的操作不同。在磁盘时期,删除一个page之后在文件系统的记录信息里将该page的标志位设置为可用,但是并没有将数据删除。使用SSD且支持TRIM的操作系统,在删除一个page时,会同时通知SSD这个page的数据不需要了,SSD内部有一个空闲时刻的垃圾收集进程,在空闲时刻SSD会将一些空闲的数据集中到一起,然后一起Erase。这样每次写操作,就在已经Erase好了的Page上写入新的数据。
虽然其存在写入放大的问题,但是这并没有让我们拒绝使用它。用其来做缓存加速已在很多项目中得到使用,尤其是在数据库缓存项目中,在这些项目中SSD高效的读性能得到了充分的利用。随着Facebook的开源项目Flash Cache的发布,以及Facebook内部的大量使用,Flash Cache已经成为了一个比较成熟的技术方案,这使得更多的公司选择了SSD来做存储或者缓存。
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新动态
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]