空洞文件1

ls -l file  查看文件逻辑大小

du -c file     查看文件实际占用的存储块多少

od -c file  查看文件存储的内容


空洞文件是个啥玩意?就是有空洞的文件,在日常的常识中,我们使用的文件存放在硬盘分区上的时候,有多大的内容就会占用多大的空间,比如这个文本文件里面写有1000个asc字符,那么就会占用磁盘上1000B的存储空间,为了便于管理文件,文件系统都是按块大小来分配给文件的,假如这个文件系统一个块是4096的话,那么这个文件就会占用一个块的,无论实际的内容是1B还是4000B.如果我们有一个4MB的文件,那么它会在分区中占用:4MB/4096B=1000个块.

现在我们先做一个实际的无空洞文件来看看:

#dd if=/dev/urandom of=testfile1 bs=4096 count=1000

这个命令会从/dev/urandom文件复制1000个块,每块大小4096,到testfile1文件去.

好了,我们已经有了testfile1这么一个4M的文件了,里面填充了一些随机的内容,你可以more一下.

然后用ls -l查看这个文件的大小是4096000,用du -h testfile1来查看的话,文件占用的磁盘大小是4M,两者是一样的.

下来是我们的重点,空洞文件,假如我们有一个文件,它有4M的大小,但是它里边很大一部分都是没有存放数据的,这样可不可以呢?试一下:

#dd if=/dev/urandom of=testfile2 bs=4096 seek=999 count=1

这个命令跟前一个命令相似,不同的是,它其实复制了1个块的内容,前面的999个块都跳过了.

我们ls -l一下,发现文件的大小还是4096000,用du -h testfile2查看,占用的块大小是4K

我们发现,虽然文件是4M,但是实际在磁盘上只占用了4K的大小,这就是空洞文件的神奇之处.

实际中的空洞文件会在哪里用到呢?常见的场景有两个:

一是在下载电影的时候,发现刚开始下载,文件的大小就已经到几百M了.

二是在创建虚拟机的磁盘镜像的时候,你创建了一个100G的磁盘镜像,但是其实装起来系统之后,开始也不过只占用了3,4G的磁盘空间,如果一开始把100G都分配出去的话,无疑是很大的浪费.

然后讲一下底层的实现吧,其实这个功能关键得文件系统支持,貌似FAT就不可以吧,linux下一直都很好的支持这一特性,我们举个最简单的ext的例子吧,ext中记录文件实际内容的对应信息的东东是一个叫索引表的东西,里面有十几个条目,每个条目存放对应文件内容块的块号,这样就可以顺序找到对应的文件内容了,大家可能说,几M的一个文件,十几个项哪够啊,不必担心,一般索引表前面几个项目是直接指向文件内容的,如果这几个不够的话,往后的第一个项目不会指向文件内容块,而会指向一个存放项目的块,这样一下多出N个项目来,如果这样还不够,下面的那个是存放指向指向的项目,不好意思,我也绕晕了,总之,前面的是直接指向,下面这个是二级指向,再下面的是二级指向,以此类推,这样,文件系统就可以处理T数量级别的文件,看下图:


到了空洞文件这里呢,我们只需要把指向没有文件内容部分的索引项目置NULL就好了,这样就不会指向实际的数据块了,也不会占用磁盘空间了,就这么easy~

至于btrfs这些新一代文件系统呢,在空洞文件这里的原理跟ext还是类似的.

最后介绍一下linux对空洞文件的处理,经过我最近的一些测试所得:

在同一文件系统ext4下,cat一个空洞文件到新文件,新文件不再是空洞文件,cp一个空洞文件到新文件,新文件仍然是空洞文件.

在btrfs跟ext4之间做的结果同上面是一致的,但是在不同文件系统之间cp,因为不同文件系统分配的最小单元不同,所以du结果会不同.

在nfs的客户端下,在nfs目录下去cp,新文件仍然是空洞文件!!!但是cp会逐个的去比较文件的内容,所以,受网络状况搞得影响,过程有时候会很慢.



  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以利用lseek函数的空洞文件原理来实现对文件的分片拷贝。空洞文件是指文件中存在一些数据块没有被实际占用,这些数据块被称为空洞。lseek函数可以用来改变文件的读写位置,并且当设置的位置超过文件的大小时,文件会被自动扩展,并在扩展的空间中填充0。 下面是一个使用lseek函数进行分片拷贝的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> void copyFile(char* srcPath, char* destPath, off_t start, off_t size) { int srcFd = open(srcPath, O_RDONLY); int destFd = open(destPath, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (srcFd == -1 || destFd == -1) { perror("Failed to open file"); exit(1); } off_t fileSize = lseek(srcFd, 0, SEEK_END); lseek(srcFd, start, SEEK_SET); char buffer[1024]; off_t remaining = size; while (remaining > 0) { ssize_t bytesRead = read(srcFd, buffer, sizeof(buffer)); if (bytesRead > remaining) { bytesRead = remaining; } ssize_t bytesWritten = write(destFd, buffer, bytesRead); if (bytesWritten != bytesRead) { perror("Failed to write file"); exit(1); } remaining -= bytesRead; } close(srcFd); close(destFd); } int main() { char* srcPath = "source_file.txt"; char* destPath = "destination_file.txt"; off_t start = 100; // 起始位置 off_t size = 200; // 拷贝的大小 copyFile(srcPath, destPath, start, size); return 0; } ``` 上述代码中,copyFile函数用于实现文件的分片拷贝。它首先打开源文件和目标文件,然后使用lseek函数将源文件的读写位置移动到指定的起始位置start。接下来,使用read函数从源文件中读取数据到缓冲区buffer中,然后使用write函数将缓冲区中的数据写入目标文件。这个过程会循环执行,直到拷贝指定大小的数据或者源文件的数据读取完毕。最后,关闭源文件和目标文件文件描述符。 你可以根据实际需求修改代码中的文件路径、起始位置和拷贝大小等参数。注意,这里的起始位置和拷贝大小都是以字节为单位的偏移量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值