ILD

fallocate and hole
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2025-3-19 站点:Inside Linux Development

我们通常了解过sparse file



fallcoate

fallocate - preallocate or deallocate space to a file

删除空间是一个杀手锏,可以像内存一样动态的分配和释放空间。像数据库文件等存储动态数据的文件,都需要可以删除文件的空间。


int fallocate(int fd, int mode, off_t offset, off_t len);


mode有5种模式,声明在 linux/falloc.h 头文件。

0,(the default operation, mode is zero)

FALLOC_FL_PUNCH_HOLE

FALLOC_FL_COLLAPSE_RANGE

FALLOC_FL_ZERO_RANGE

FALLOC_FL_INSERT_RANGE


还有两个flag

FALLOC_FL_KEEP_SIZE

FALLOC_FL_UNSHARE_RANGE


Allocating disk space

    mode为0,分配磁盘空间,如果指定FALLOC_FL_KEEP_SIZE,那么stat统计的文件大小不变。

    如果指定:FALLOC_FL_UNSHARE,那么offset,len指定的区域应该是一个共享的区域(btrfs支持cow),此时会取消共享。

    函数执行成功后,可以保证后续写入不会出现磁盘空间不足的错误。


Deallocating file space

    mode为FALLOC_FL_PUNCH_HOLE,释放offset,len指定的区域,相当于打了一个洞。这个mode必须指定FALLOC_FL_KEEP_SIZE。

    即使区域在文件的末尾。


Collapsing file space

    mode为FALLOC_FL_COLLAPSE_RANGE,释放offset,len指定的区域,然后将区域后面的内容往前移,填补释放的区域。

    这样就不会留下一个洞。


Increasing file space

    mode为:FALLOC_FL_INSERT_RANGE,在文件中间打个洞,然后将洞后面的数据往后移。不可指定FALLOC_FL_KEEP_SIZE。


Zeroing file space

    mode为:FALLOC_FL_ZERO_RANGE,将区域offset,len置0,

    个人理解:如果该区域已经有数据,则这段数据被标记为0(可能不会实际擦除)。如果该区域是一个sparse区域,则会实际分配内存。


ftruncate

fallocate创建的不是sparse file,它会预留空间。但是ftruncate创建的是sparse file,它不占用磁盘空间,只是改变文件的EOF位置。


# df .
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/sdc2      112196608  3984 110081744   1% /nas/mnt/pa0
#
# truncate -s 10G a
#
# df .
Filesystem     1K-blocks  Used Available Use% Mounted on
/dev/sdc2      112196608  3984 110081744   1% /nas/mnt/pa0
#
# stat a
  File: a
  Size: 10737418240     Blocks: 0          IO Block: 4096   regular file
Device: 30h/48d Inode: 319         Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2025-03-19 10:07:44.485833000 +0800
Modify: 2025-03-19 10:07:44.485833000 +0800
Change: 2025-03-19 10:07:44.485833000 +0800
 Birth: -


使用truncate 创建一个10G的文件,可以看到磁盘空间没有变化。stat显示blocks为0。


如果使用fallocate分配一个10G的文件,则df发生了变化,stat显示blocks也有空间。

# fallocate -l 10G a
#
# df .
Filesystem     1K-blocks     Used Available Use% Mounted on
/dev/sdc2      112196608 10492304  99593424  10% /nas/mnt/pa0
#
# stat a
  File: a
  Size: 10737418240     Blocks: 20971520   IO Block: 4096   regular file
Device: 30h/48d Inode: 320         Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2025-03-19 10:12:56.588091391 +0800
Modify: 2025-03-19 10:12:56.588091391 +0800
Change: 2025-03-19 10:


lseek SEEK_HOLE

off_t lseek(int fd, off_t offset, int whence);


3.1开始,内核添加内核两个whence,用来查找hole和data。SEEK_DATA、SEEK_HOLE。


打印data和hole的代码:

#define _GNU_SOURCE
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
        char *path = argv[1];
        int fd;
        off_t off = 0;

        fd = open(path, O_RDONLY);
        if (fd < 0) {
                fprintf(stderr, "open failed: %m\n");
                return 1;
        }

        while (1) {
                off_t next_data, next_hole;

                next_data = lseek(fd, off, SEEK_DATA);
                next_hole = lseek(fd, off, SEEK_HOLE);

                if (next_data == off) {
                        printf("data [%ld - %ld)\n", (long)off, (long)next_hole);
                        off = next_hole;
                }
                else if (next_hole == off) {
                        printf("hole [%ld - %ld)\n", (long)off, (long)next_data);
                        if (next_data == (off_t)-1)
                                break;
                        off = next_data;
                }
                else
                        break;
        }

        close(fd);
        return 0;
}


分配一个1G的文件,然后追加写入2个字节,可以打印出一个hole,一个data

# fallocate -l 1G a
# echo 1 >> a
# ./dump_hole a
hole [0 - 1073741824)
data [1073741824 - 1073741826)


对于keep size的文件打印不出尾巴的hole。


参考

man 2 fallocate, fallocate系统调用

man 1 fallocate,fallcoate工具

https://stackoverflow.com/questions/67031801/how-can-we-know-there-is-hole-in-a-file-in-c


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.