预分配——fallocate的前世今生
最近比较懒,还是加班写点东西吧,不然过段时间又把这些整理的东西弄丢了。
写什么呢?写一些跟工作相关的吧!因为笔者从事多媒体录像相关的开发工作,因此常常涉及到优化写卡策略、提升写卡性能相关的方面的事情。此话怎讲呢?如行车记录仪类的录像产品,录像可能持续多日,越往后写卡速度会越来越慢,直观感受是取出视频文件进行回放时,时间约往后的视频文件卡顿越来越严重。
怎样解决呢?一种方案从硬件解决,换一张好卡!但是这不能一劳永逸解决问题,因为录着录着写卡速度又掉下来了。另外一种方案从软件层面解决,就是卡速变慢了后,将卡格式化,但是这种方案对于用户来讲不太友好(有些用户可能不知道这个功能,或者文件删除前备份不方便)。还有一种方案,也是从软件层面解决问题,就是优化写卡策略。优化写卡策略,有一些可行的方案,例如文件预分配、待写数据进行缓冲写、编码与封装解耦,直写(DirectIO)。下面内容介绍预分配的内容。
1. fallocate介绍
linux man手册说明:
fallocate即预分配,英文为preallocate。什么意思呢?还往文件中没写数据,但是已经给文件分配了足额的物理空间来存储数据。创建了文件,再调用这个接口预分配了一定量的空间后,后续就可以往这个文件中写数据了。
另外一点需要注意,这个接口需要文件系统的支持。常用TF卡录像,而卡的文件系统类型一般为fat32,就需要fat32文件系统相关的实现才能使用该功能。
再有,这是一个不可移植的linux专用系统调用,用于确保文件空间被提前分配,成功执行后,可以确保写卡速度较快,也能保证不会因为磁盘空间不足而出现写失败。
2 . 接口声明
函数原型 |
int fallocate(int fd, int mode, off_t offset, off_t len); |
fd |
文件句柄 |
mode |
创建模式 |
offset |
偏移 |
len |
文件大小 |
其中,在创建了文件后和写数据前,需要调用该接口进行预分配,第二个参数mode一般设置为1,第三个参数设置为0,第四个参数填上期望预分配值。
3. 应用场景及目标
应用场景:持续写卡场景,例如行车记录仪、运动相机。
目标:减少磁盘碎片化,提高写卡速度。
其他说明:录像设备的瓶颈常常是写卡,因为要随时将视频文件记录下来。并且,对持续写卡速度要求较高,因为录像设备工作周期可能是以day为单位,不仅要求录像刚启动时写卡正常,而且要求工作了几天写卡速度也不能掉太多。至于每秒钟写入的数据量,视编码器输出码率和几路录像而定,对于单路1080p录制,视频码率设置为10mbps,那么卡速至少要保证2MB/s,这里面还不包括写log以及录像中拍照所用的。
虽然目前时间节点上(2019年末),市面上卡都是C10(10MB/s)及其以上,但是如果写策略不合理或卡中太多零碎文件,写速度可能很低。很常见的一个例子,拷贝一个视频文件到T卡的速度,要远远大于拷贝同样大小的源文件包。另一个例子是,一个刚格式化的T卡与一个内部已经存在了很多文件的T卡(卡品牌、容量、速度等参数都一样),拷贝同样大小的文件,刚格式化的那张卡速度更快。
4. 实现原理
TF卡(TransCard)和SSD(SolidStateDisk)作为常见的存储设备,内部组成非常类似,都主要由controler和nand flash组成。对于任何存储设备,我们都最关心三个参数:容量、读/写速度、寿命。
“容量”这个参数勿用介绍,“读速度”也不介绍,下面主要说下“寿命”和“写速度”这两个参数。介绍这两个参数后,再来介绍预分配。
4.1 寿命相关:
寿命主要由存储介质决定,即nand flash这种介质的可擦写次数,nand flash介质类型的发展经历了slc、mlc、tlc、qlc(目前市面上还较少)几个阶段,单位面积的容量也越来越大,因为介质类型反映了存储密度。小小的TF卡,就目前2019年末的这个时间节点上,市面上已经出现了512GB容量的TF卡,存储多个图书馆书籍的文字信息应该毫无压力!但是,凡事有利有弊,随着容量的提升,TF内部的最小存储单元的可擦写次数也越来越少。
SLC(SingleLevelCell)出现最早,可擦写次数10多万次;后来出现的MLC(MultiLevelCell)可擦写次数3000-10000次左右,目前主流的TLC的可擦写次数在500-1000次左右。在某东上随便查看了lexar的某款500GB 容量的SSD,其参数如下:
从中看到闪存类型为TLC,还有TBW=250T这个参数,这个是什么以及怎么得来的呢?
TBW,即TeraBytesWritten,以TB为单位的写入的数据量。这个值这样算:总容量*可写次数,即500GB*500 = 250TB。其中的500代表平均可写次数为500,是根据闪存类型TLC来估算的。一般企业级的用的sdd,价格较民用的高不少,例如编译/数据库服务器,相同容量的TBW值通常是以PBW(=1024TBW)为单位的,不太追求读写速度,但非常看重寿命和可靠性,毕竟数据是无价的。
4.2 速度相关:
写速度是个比较玄乎的东西,由许多因素综合导致,例如,闪存类型、主控算法(固件磨损平衡算法)、文件系统写策略、卡的碎片化程度、卡的文件系统类型和block大小、内部是否带Cache以及其大小,等等诸多因素。
但是,针对确定下来的一张卡,我们需要找到一些方法,来提高写卡速度。其中一种方法就是预分配——fallocate。
接下来先介绍文件存储相关的内容后,再来介绍这个预分配接口的作用。
对于fat32的文件系统,存储设备中的某文件,其内容主要包括两部分:一部分是属性信息metadata(创建/修改时间、文件名称、文件大小等),另一部分是真正的数据内容。常用的fdatasync操作只会强制将真正的数据内容刷新到存储设备中,而fsync会将两部分内容都刷新到设备中。对于真正的数据内容那部分,有一个链表来管理各个块内容所在的SectorId,即以sector链表的形式来完整表述数据内容。因此,某文件的存储物理地址可能是某连续sector区所在的一整片区域,也可能分布于多个不连续的物理区域。
存储设备的碎片化与内存碎片化非常类似,即某文件希望尽可能利用连续的物理存储空间来存储数据,但是由于卡已处于高度碎片化状态,当真正写入完这个文件时,这个文件在物理空间上是“支离破碎”的。即使是一个刚刚格式化的卡,当两个线程同时分别写两个不同文件时,在物理空间上(内部连续的物理block或sector),这两个文件可能处于交织状态(交错),英文为interleave。做过音频开发的同事也可以回想一下alsa-lib在打开设备进行参数配置时,针对双声道pcm数据采集,有interleave和non-interleave的配置,这个选择决定了左右声道pcm数据在一个period内如何排列,类似对比,卡中存储的多个文件,对于物理block就是这个意思。
设想一种写文件场景,使用正常fopen-fwrite-fclose的操作流程,只写一路,当每次将kernel cache中的数据刷到卡中前,需要现场去找(类似于写磁盘时的寻道)哪个物理sector是available的,当发现某个block中的某个sector是可用的,但是其他sector是其他文件占用的,那么接下来的策略就是copy-modify-write,即出现了“写放大”(WriteAmplification)。
为什么出现这个状况,需要了解闪存的基本组成:页page(也称sector,大小4KB) -> 块block(通常64或128个page组成一个block) -> 面plane(多个block组成) –> die(plane就是一个die) -> 闪存片(多个die组成) –> SSD或TF(多颗闪存片组成)。
下面描述下写放大过程:先把整个block中的数据完全拷贝到ddr,再将某个sector中的数据修改为期望写入的数据,擦掉ssd中这个block的内容,然后再整体将ddr中的已修改好的数据写入到ssd中这个block位置。为什么要这样做?因为写入是按block为最基本单位进行的。所以写入一笔数据,涉及了多次基本操作,不仅减慢了写速度,而且减少了寿命。然而,当进行了预分配后,提前为某文件划分了“势力范围”,标定某些位置已经被占用,可以减少后续的写放大和寻找可用空间的过程。
4.3 预分配原理:
介绍了文件存储结构的相关内容后,对于预分配的功能我们就有了大致的猜测!fallocate这个接口,其要实现的目的,就是在数据内容还未写入到设备前,提前为文件分配好若干大小的空间,并且使这个空间尽可能是物理连续的,这样可以减少后续写放大的出现频率,以及不需在写入过程中寻找可用空间,更不会出现写数据时磁盘空间不足的问题!
5. 其他问题
使用预分配一个最大的问题是——磁盘空间利用率不高!这个如何说起?文件刚创建还未写入数据,我们就抢先为文件设置了文件的大小并占用了固定大小的物理空间,但通常可能未写入那么大size的数据量就fclose了这个文件,那么这个文件内未写入的空间就不能被其他文件利用了。一个文件预分配了100MB,即使只写入1MB就关闭,那么就有99MB的空间浪费。但是,使用预分配对于行车记录仪类产品是个较优的选择,因为文件切换是定时切换的,如果编码器输出码率是相对稳定的,就可以预估最终文件大小,预分配的大小再留些余量就可以了。
预分配——fallocate的前世今生的更多相关文章
- 用fallocate进行"文件预留"或"文件打洞"【转】
转自uestc-leon的博客 内容作了一些修改,查看原文请访问uestc-leon 1. 什么是空洞文件? "在UNIX文件操作中,文件位移量可以大于文件的当前长度,在这种情况下,对该文件 ...
- linux(centos8):用fallocate快速生成大文件
一,fallocate的用途? 1,用途 我们有时需要用大文件来测试下载速度, 有时需要用大文件来覆盖磁盘空间, 如果在网上搜索,很多文章讲的是使用dd等工具, 事实上linux系统已经内置了生成大文 ...
- 【调侃】IOC前世今生
前些天,参与了公司内部小组的一次技术交流,主要是针对<IOC与AOP>,本着学而时习之的态度及积极分享的精神,我就结合一个小故事来初浅地剖析一下我眼中的“IOC前世今生”,以方便初学者能更 ...
- [C#] 回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性
回眸 C# 的前世今生 - 见证 C# 6.0 的新语法特性 序 目前最新的版本是 C# 7.0,VS 的最新版本为 Visual Studio 2017 RC,两者都尚未进入正式阶段.C# 6.0 ...
- docker4dotnet #1 – 前世今生 & 世界你好
作为一名.NET Developer,这几年看着docker的流行实在是有些眼馋.可惜的是,Docker是基于Linux环境的,眼瞧着那些 java, python, node.js, go 甚至连p ...
- Atitit 智能云网络摄像机的前世今生与历史 优点 密码默认888888
Atitit 智能云网络摄像机的前世今生与历史 优点 密码默认888888 用户名admin 密码aaaaaa 网络摄像机是一种结合传统摄像机与网络技术所产生的新一代摄像机,它可以将影像通过网络传 ...
- 阿里开源消息中间件RocketMQ的前世今生-转自阿里中间件
昨天,我们将分布式消息中间件RocketMQ捐赠给了开源软件基金会Apache. 孵化成功后,RocketMQ或将成为国内首个互联网中间件在Apache上的顶级项目. 消息一出,本以为群众的反应是这样 ...
- JavaScript的前世今生
和CSS一样,JavaScript在各浏览器下并非完全一致,它所带来的兼容性问题时常困扰着我们,以至于现在“能否处理流行浏览器的兼容性问题”成为了检验一个程序员是否合格的标准之一.了解JavaScri ...
- 主成分分析PCA的前世今生
这篇博客会以攻略形式介绍PCA在前世今生. 其实,主成分分析知识一种分析算法,他的前生:应用场景:后世:输出结果的去向,在网上的博客都没有详细的提示.这里,我将从应用场景开始,介绍到得出PCA结果后, ...
随机推荐
- Android Studio配置Socks5代理后Gradle运行不正常
第一次在Mac上运行Android Studio,遇到了一系列问题,其中一个代理问题搞得我是不知所措,在此记录. 如果你遇到了如下图的问题,那么恭喜你你可能已经找到了解决方案也就是本文: 一般,我们为 ...
- 纯HTML+JS实现轮播
<!DOCTYPE html> <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> ...
- UVA12433 【Rent a Car】
这题应该算是比较难的一道网络流的题,(但却在我校OJ考试上出现了),但是大家只要能理解此图的建边方式就行. 假设有5天的租车需求,虚拟出2*n+2 即 12个节点,0为源点,12为汇点. 1,源点到1 ...
- 学习笔记33_EF跨数据库
在App.Config中,可以: (1)自定义类 public xxxxDbContext() { public XXXXDbContext():base("name=xxxxContain ...
- Appium的加载过程
appium运行流程 Appium的加载过程如上图. 1)调用Android adb完成基本的系统操作: 2)向Android上部署bootstrap.jar: 3)Bootstrap.jar For ...
- [考试反思]1109csp-s模拟测试107:低能
诶一看这不是水题AK场吗?然后80分钟就拿到了285分. 然后,对拍?还是卡T2常数?还是想T2正解? 于是上述三项我依次进行了. 前两项让我的分数丝毫不变但是吃掉了我一个多小时的时间. 卡常卡的也不 ...
- [考试反思]1006csp-s模拟测试62:隔断
本来说好的好一场烂一场. 那样的日子结束了,连着烂了两场...幸亏T3傻逼了救我一命不算太惨... T1树上的特殊性质会做但是没有继续想下去就死在错贪心上了还没有过那个点... T2迭代至稳定被我错误 ...
- 史上最全的excel读写技术分享
目录 简介 导出excel常用的几种方法 POI CSV jxl jxls easyexcel 快速入门 代码解读 总结 常用API 单元格样式 合并单元格 数据样式 多sheet设置 单元格添加超链 ...
- 使用requests实现人人网登录,并做cookie维持
import requests import re,time s = requests.Session() def doLogin(): login_url = 'http://www.renren. ...
- Dockerfile介绍及指令详情
Dockerfile简介: 镜像的定制实际上就是定制每一层所添加的配置.文件.如果我们可以把每一层修改.安装.构建.操作的命令都写入一个脚本,用这个脚本来构建.定制镜像,那么哪些无法重复的问题.镜 ...