用fallocate进行"文件预留"或"文件打洞"【转】

转自uestc-leon的博客
内容作了一些修改,查看原文请访问uestc-leon

1. 什么是空洞文件?

“在UNIX文件操作中,文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被设为 0。”
如果 offset 比文件的当前长度更大,下一个写操作就会把文件“撑大(extend)”。这就是所谓的在文件里创造“空洞(hole)”。没有被实际写入文件的所有字节由重复的 0 表示。空洞是否占用硬盘空间是由文件系统(file system)决定的。大部分文件系统是不占用的。

2. 怎么获得一个空洞文件?

以Linux来说,使用lseek或truncate到一个固定位置生成的“空洞文件”是不会占据真正的磁盘空间的。
空洞文件特点就是offset大于实际大小,也就是说一个文件的两头有数据而中间为空,以‘‘填充。那文件系统会不会不做任何处理的将其存放在硬盘上呢?大部分文件系统是不会将其存放在硬盘上。

3. 文件预留

为什么需要文件预留
在开发过程中有时候需要为某个文件快速地分配固定大小的磁盘空间,为什么要这样做呢? **
1. 可以让文件尽可能的占用连续的磁盘扇区,减少后续写入和读取文件时的磁盘寻道开销;
2. 迅速占用磁盘空间,防止使用过程中所需空间不足。
3. 后面再追加数据的话,不会需要改变文件大小,所以后面将不涉及metadata的修改

前面提到使用lseek或truncate到一个固定位置生成的“空洞文件”是不会占据真正的磁盘空间的。
快速的为某个文件分配实际的磁盘空间在Linux下可通过fallocate(对应的posix接口为posix_fallocate)系统调用来实现,大部分主流文件系统如ext4,xfs还是支持fallocate

4. fallocate的mode

mode决定了fallocate的行为。
**Allocating disk space **

这是默认的操作,对应mode等于0。它所作的工作是如果分配从offset开始到offset+len的一段空间,这个是真的分配磁盘空间,不是hole,新分配的空间以0填充数据。当然这个操作一般在offset+len大于现有文件长度时才会起到增加文件数据空间的作用。 
一般情况下新增加空间后文件的size也会随着调整,但是有一个特殊情况,就是当FALLOC_FL_KEEP_SIZE出现在mode中时,在增加文件空间后不会改变文件的size。这样的操作算是一种在文件结尾处的预分配,对于后期的append写入操作有优化作用。 

Deallocating file space

释放文件的某段范围的磁盘空间 (文件打洞) 
FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE 
此mode虽然并不会改变文件的大小,但其实却释放了offset和len所在范围的磁盘块,将它们归还给了文件系统。fallocate成功后,后续对offset和len所在的文件范围进行读操作,将会读到0。

5. fallocate命令简单使用

NAME
fallocate - preallocate space to a file

SYNOPSIS

fallocate [-n] [-p] [-o offset] -l length filename
fallocate -x [-o offset] -l length filename

DESCRIPTION

fallocate用于预分配块到文件。对于支持fallocate系统调用的文件系统,这是通过分配块并将它们标记为未初始化的,从而快速完成的,不需要IO到数据块。这比用0填充文件要快得多。
在Linux内核v2.6.31的情况下,fallocate系统调用由btrfs、ext4、ocfs2和xfs filesystems支持。
fallocate返回的退出代码在成功时是0,而失败则是1。

OPTIONS

The length and offset arguments may be followed by the multiplicative suffixes KiB=1024, MiB=1024*1024, and so on for GiB,  TiB,  PiB,  EiB,  ZiB  and  YiB  (the "iB" is optional, e.g. "K" has the same meaning as "KiB") or the suffixes KB=1000, MB=1000*1000, and so on for GB, TB, PB, EB, ZB and YB.

-l, --length length		##分配多少空间
-o, --offset offset		##设置偏移量

EXAMPLE

##创建完成后,可以将它们进行格式化,并当做文件系统进行挂载。我格式化成ext4之后,可以看到superblock、block group等信息,挂载之后使用df -h可以看到这个分区的大小是:前者10G,后者30G。
##而且这个大文件所在目录的文件系统的空间使用率几乎没有增加(只增加了100+M),我认为这是因为这个文件是空洞文件,虽然分配了一堆block的,但是没有使用,使用的时候将bmap标记为使用之后,空间就会增加了。
fallocate -l 10G bigfile				##创建一个10G的文件(使用ll查看,大小为10G)		##挂在之后看到分区,这个分区是10G
fallocate -l 10G -o 20G bigfile2		##创建一个10G的文件,偏移量为20G(使用ll查看,大小为30G;通过df -h看到只占用了10G;通过du看到只占用了100+M的空间)                

6. 创建大文件

但是这样为实际写入硬盘,文件产生速度取决于硬盘读写速度,如果欲产生超大文件,速度很慢

dd if=/dev/zero of=test bs=1M count=1000
会生成一个1000M的test文件,文件内容为全0(因从/dev/zero中读取,/dev/zero为0源)

在某种场景下,我们只想让文件系统认为存在一个超大文件在此,但是并不实际写入硬盘,则可以:

dd if=/dev/zero of=test bs=1M count=0 seek=100000
此时创建的文件在文件系统中的显示大小为100000MB,但是并不实际占用block,因此创建速度与内存速度相当
seek的作用是跳过输出文件中指定大小的部分,这就达到了创建大文件,但是并不实际写入的目的
当然,因为不实际写入硬盘,所以你在容量只有10G的硬盘上创建100G的此类文件都是可以的

fallocate、truncate 以及GNU dd的seek扩展命令都可以实现创建空洞文件的效果

fallocate -l 10G bigfile
truncate -s 10G bigfile
dd of=bigfile bs=1 seek=10G count=0

文件系统对于这样创建的文件有特殊处理,叫稀疏文件

原文地址:https://www.cnblogs.com/jzbgltb/p/9566941.html