Linux文件空洞与稀疏文件


From:http://www.topjishu.com/8277.html

From:http://blog.csdn.net/clamercoder/article/details/38361815

Linux_File_Hole_And_Sparse_Files

参考unix环境高级编程第三版 54页和90页!!! ( 文件I/O章节  lseek函数 。文件和目录章节 文件长度)


1、Linux文件空洞与稀疏文件
2、文件系统数据存储
3、文件系统调试

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

20140913144536_1

 

稀疏文件(Sparse File)

  1. 稀疏文件与其他普通文件基本相同,区别在于文件中的部分数据是全0,且这部分数据不占用磁盘
    空间。
    下面是稀疏文件的创建与查看
[root@localhost ~]# dd if=/dev/zeroof=sparse-file bs=1 count=1 seek=1024k
[root@localhost ~]# ls -l sparse-file
-rw-r--r-- 1 root root 1048577 Oct 15 17:50 sparse-file
[root@localhost ~]# du -sh sparse-file
8.0K sparse-file
[root@localhost ~]# cat anaconda-ks.cfg >> sparse-file
[root@localhost ~]# du -sh sparse-file
12K sparse-file
[root@localhost ~]# du -sh anaconda-ks.cfg
12K anaconda-ks.cfg
[root@localhost ~]#

Linux文件系统inode数据块存储

索引节点采用了多重索引结构,主要体现在直接指针和3个间接指针。直接指针包含12个直接指
针块,它们直接指向包含文件数据的数据块,紧接在后面的3个间接指针是为了适应文件的大小
变化而设计。

20140913144536_2

Linux稀疏文件inode数据块存储

文件系统存储稀疏文件时,inode索引节点中,只给出实际占用磁盘空间的Block 号,
数据全零且不占用磁盘空间的文件Block并没有物理磁盘Block号。

20140913144536_3

Linux稀疏文件inode数据块存储

  • 文件空洞部分不占用磁盘空间
  • 文件所占用的磁盘空间仍然是连续的

20140913144536_4

实例:

[root@localhost mnt]# du -sh sparse-file
20K sparse-file
[root@localhost mnt]# ls -lh sparse-file
-rw-r--r-- 1 root root 1.1G Oct 15 10:36 sparse-file
[root@localhost mnt]#
debugfs: stat sparse-file
Inode: 49153 Type: regular Mode: 0644 Flags:
0x0 Generation: 3068382963
User: 0 Group: 0 Size: 1073742848
File ACL: 0 Directory ACL: 0
Links: 1 Blockcount: 40
Fragment: Address: 0 Number: 0 Size: 0
ctime: 0x507b76af -- Mon Oct 15 10:36:31 2012
atime: 0x507b765f -- Mon Oct 15 10:35:11 2012
mtime: 0x507b76af -- Mon Oct 15 10:36:31 2012
BLOCKS:
(IND):106496, (256):106497, (DIND):106504,
(IND):106505, (262144):106506
TOTAL: 5

Linux文件系统数据块存储多重索引

  • Linux文件系统数据存放采用inode多
    重索引结构,有直接指针和3个间接指
    针。
    类似于编程中的变量定义:
    unsigned long blk;
    unsigned long *blk;
    unsigned long **blk;
    unsigned long ***blk;
  • 直接指针直接指向保存数据的Block
    号。
  • 一级指针指向一个Block,该Block中
    的数据是Block指针,指向真正保存数
    据的Block。
    二级三级指针以此类推。

20140913144536_5

  1. 前12个直接指针,直接指向存储的数据区域
    如Blocks大小为4096,则前12个直接指针就可以保存48KB文件。
  2. 一级指针可存储文件大小计算
    假设每个指针占用4个字节,则一级指针指向的Block可保存4096/4个
    指针,可指向1024个Blocks。一级指针可存储文件数据大小为1024*4096 =
    4MB。
  3. 二级指针可存储文件大小计算
    同样按照Blocks大小为4096,则二级指针可保存的Block指针数量为(4096/4) *
    (4096/4) = 1024*1024。则二级指针可保存的文件数量大小为(1024*1024)*4096
    = 4GB。
  4. 三级指针可存储文件大小计算
    以一级、二级指针计算方法类推,三级指针可存储的文件数据大小为
    (1024*1024*1024)*4096 = 4TB。

20140913144536_6




1.什么是空洞文件?

在UNIX文件操作中,文件位移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。位于文件中但没有写过的字节都被设为 0。”      --摘自“百度百科”

从上面的描述可以将空洞文件的特点表述为:offset > 实际文件大小。那这又有什么表现和意义呢?我们下面慢慢分析。


( http://blog.csdn.net/shenlanzifa/article/details/44016537 )

文件空洞

我们知道lseek()系统调用可以改变文件的偏移量,但如果程序调用使得文件偏移量跨越了文件结尾,然后再执行I/O操作,将会发生什么情况? read()调用将会返回0,表示文件结尾。令人惊讶的是,write()函数可以在文件结尾后的任意位置写入数据。在这种情况下,对该文件的下一次写将延长该文件,并在文件中构成一个空洞,这一点是允许的。从原来的文件结尾到新写入数据间的这段空间被成为文件空洞。调用write后文件结尾的位置已经发生变化。


在Linux系统之中,文件结束符EOF根本不是一个字符,而是当系统读取到文件结尾,所返回的一个信号值(也就是-1),至于系统怎么知道文件的结尾,资料上说是通过比较文件的长度。


文件空洞占用任何磁盘空间,直到后续某个时点,在文件空洞中写入了数据,文件系统才会为之分配磁盘块。空洞的存在意味着一个文件名义上的大小可能要比其占用的磁盘存储总量要大(有时大出许多)。向文件空洞中写入字节,内核需要为其分配存储单元,即使文件大小不变,系统的可用磁盘空间也将减少。这种情况并不常见,但也需要了解。


下面看一个例子:(转自http://blog.csdn.net/wangxiaoqin00007/article/details/6617801)

ls -l file        查看文件逻辑大小 ( 即文件的实际大小 )

du -c file     查看文件实际占用的存储块多少 (即文件实际占用的磁盘空间大小)

od -c file     查看文件存储的内容 (od命令:http://blog.csdn.net/freeking101/article/details/78182731


空洞文件就是有空洞的文件,在日常的常识中,我们使用的文件存放在硬盘分区上的时候,有多大的内容就会占用多大的空间,比如这个文本文件里面写有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.怎么获得一个空洞文件?

在linux下,利用lseek人为的修改offset可以获得一个空洞文件。

[cpp] view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/stat.h>  
  5. #include <sys/types.h>  
  6. #include <fcntl.h>  
  7. #include <string.h>  
  8.   
  9. #define OFFSET_LENGTH 15000  
  10.   
  11. char buff1[] = "abcdefg";  
  12. char buff2[] = "ABCDEFG";  
  13.   
  14. int  
  15. main(int argc,char** argv)  
  16. {  
  17.     int fd = 0;  
  18.       
  19.     int buff1Length = strlen(buff1);  
  20.     int buff2Length = strlen(buff2);  
  21.       
  22.     char* buff3 = (char*)malloc(buff1Length + buff2Length + OFFSET_LENGTH);  
  23.     memset(buff3,2,buff1Length + buff2Length + OFFSET_LENGTH);  
  24.       
  25.     //create hole file  
  26.     if((fd = creat("./hole.f",S_IREAD|S_IWRITE)) < 0)  
  27.     {  
  28.         perror("create file error!");  
  29.     }  
  30.       
  31.     printf("fd:%d\n",fd);  
  32.     if(write(fd,buff1,buff1Length) != buff1Length)  
  33.     {  
  34.         perror("write error!");  
  35.     }  
  36.       
  37.     if(lseek(fd,OFFSET_LENGTH,SEEK_CUR) == -1)  
  38.     {  
  39.         perror("lseek error!");  
  40.     }  
  41.       
  42.     if(write(fd,buff2,buff2Length) != buff2Length)  
  43.     {  
  44.         perror("write error!");  
  45.     }  
  46.       
  47.     //create nohole file  
  48.     if((fd = creat("./nohole.f",S_IREAD|S_IWRITE)) < 0)  
  49.     {  
  50.         perror("create file error!");  
  51.     }  
  52.       
  53.     if(write(fd,buff3,strlen(buff3)) != strlen(buff3))  
  54.     {  
  55.         perror("write error!");  
  56.     }  
  57.       
  58.     free(buff3);  
  59.       
  60.     return 0;  
  61. }  

上面的程序创建了一个空洞文件和一个同样大小的非空洞文件,接下来我们将以这两个文件作为基础进行空洞文件的分析。


3.空洞文件的表现

空洞文件特点就是offset大于实际大小,也就是说一个文件的两头有数据而中间为空,以‘\0‘填充。那文件系统会不会不做任何处理的将其存放在硬盘上呢?答案是否定的,文件系统没有傻到这种程度,因为这实际是中浪费,也是一种威胁,因为一旦黑客利用这个漏洞不断侵蚀磁盘资源,计算机就崩溃了。所以说,文件系统肯定会做相应的处理,下面我就来验证一下。

用ls来展现两个文件:


我们在用du来展现两个文件(du命令用于报告文件所使用的磁盘空间总量):


可以看到,用ls展现的空洞和非空洞的大小完全相同,而用du命令展现的则有差别,一个占用了8个1024的字节块,而一个占用了16个1024的字节块。这里有个问题,文件大小为15014,算下来最多就15个block,为什么是16个呢?在《APUE》中有这样的解释:"文件系统使用了若干块以存放指向实际数据块的各个指针"。

为什么会这样呢?原因是ls展现的文件的逻辑大小,也就是文件在文件系统表现出来的大小,而du展现的是文件物理大小,也就是文件在磁盘上实际所占的block数。所以说,空洞文件在文件系统表现的还是和普通文件一样的,但是实际上文件系统并没有给他分配所表现出来的那么多空间,只是存放了有用的信息。

接下俩我们再来看一个现象:

首先我们用cat来输出空洞文件中内容,然后重定向到一个新的文件中:


我们再来用cp去复制一个文件:


可以看到,用cat得到的文件,文件实际占用的block增加了,而cp的没有。那是因为cat在复制空洞文件时会将空洞补齐,将空洞填以0,因为cat命令就是简单的read和write的操作,read在遇到空洞时读出0,write则写入0,这时文件就变成了非空洞文件,而cp在复制文件时不会,cp命令会去判断文件是否有空洞,如果有,则会调用lseek进行空洞的模拟,所以还是会保持和源文件的一致性。


4.空洞文件有什么用?

空洞文件作用很大,例如迅雷下载文件,在未下载完成时就已经占据了全部文件大小的空间,这时候就是空洞文件。下载时如果没有空洞文件,多线程下载时文件就都只能从一个地方写入,这就不是多线程了。如果有了空洞文件,可以从不同的地址写入,就完成了多线程的优势任务。

感觉这并不是空洞文件的全部作用,后续将进行补充。。。。





本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/496393.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

西人马聂泳忠:打造机器神经系统,成为中国的特斯拉

作者&#xff1a;于绍洋 来源&#xff1a;投资家网经常穿梭于几个城市&#xff0c;一手拉着行李箱&#xff0c;一手忙于回复工作上的事务&#xff0c;这可能已经成为西人马FATRI&#xff08;下称&#xff0c;西人马&#xff09;创始人聂泳忠博士的日常状态。他给人的第一印象…

Linux dd 命令

From&#xff1a;http://www.cnblogs.com/jikexianfeng/p/6103500.html Linux/UNIX: 使用 dd 命令创建 1GB 大小的二进制&#xff1a;http://www.linuxidc.com/Linux/2014-12/110147.htm 菜鸟教程 Linux dd命令&#xff1a;http://www.runoob.com/linux/linux-comm-dd.html …

Nature 首度揭示大脑传递信息的真正逻辑

我们对于大脑的理解还停留在极为初步的阶段&#xff08;图片来源&#xff1a;Pixabay&#xff09;来源&#xff1a;生物360摘要&#xff1a;我们过去对于大脑处理信息的理解&#xff0c;其实是非常片面和不准确的。今日&#xff0c;一项重量级的研究刊登在了最新一期的《自然》…

Linux 用户 和 用户组 管理 (添加、删除、修改)及说明

From&#xff1a;http://www.cnblogs.com/xd502djj/archive/2011/11/23/2260094.html 鸟哥官网 Linux 帐号管理与 ACL 权限设定&#xff1a;http://linux.vbird.org/linux_basic/0410accountmanager.php 鸟哥官网&#xff08;简体中文&#xff09;&#xff1a;http://cn.linux.…

IBM Watson将成为失败的投资?分析师眼里, IBM AI过度乐观, 夸大宣传

来源&#xff1a;36Kr 作者&#xff1a;石筱玉IBM Watson是在医疗领域最早布局的AI之一。在36Kr此前的盘点中&#xff0c;我们也知道Watson希望参与患者诊疗中的每一个步骤&#xff1a;导医用智能音箱、Watson诊断工具、住院看护辅助&#xff0c;还有病患心理疏导……Watson已…

linux 文件系统详解

From&#xff1a;http://soysauce93.blog.51cto.com/7589461/1715655 From&#xff1a;http://blog.csdn.net/new0801/article/details/63687127 Linux 的虚拟文件系统(强烈推荐)&#xff1a;http://blog.csdn.net/heikefangxian23/article/details/51579971 鸟哥 Linux 磁盘…

国际互联网协会(ISOC)提出未来互联网十项原则

来源&#xff1a;腾讯研究院此前&#xff0c;国际互联网协会&#xff08;Internet Society&#xff0c;简称ISOC&#xff09;发布了题为《通往数字化未来之路&#xff08;Paths to Our Digital Future&#xff09;》的报告&#xff0c;就数字化未来的道路进行了探索。ISOC认为&…

Spring Data JPA 从入门到精通~javax.persistence概况介绍

虽然 Spring Data JPA 已经对数据的操作封装的很好了&#xff0c;约定大于配置的思想&#xff0c;帮我们默认了很多东西。JPA&#xff08;Java 持久性 API&#xff09;是存储业务实体关联的实体的来源&#xff0c;它显示了如何定义一个面向普通 Java 对象&#xff08;POJO&…

Linux安装配置类似mac下的docky

百度经验&#xff1a;ubuntu安装配置类似mac下的docky 亲手打造自己的Linux桌面环境&#xff1a;http://os.51cto.com/art/201510/493896_all.htm Dock是一种图形用户界面元素&#xff0c;允许用户一键访问常用的应用程序&#xff0c;在应用程序之间快速切换&#xff0c;以及…

埃森哲:2018年科技愿景

来源&#xff1a;199IT互联网数据中心埃森哲近日发布了新报告“2018年科技愿景”&#xff0c;分析了五大技术趋势让企业能够挖掘潜力&#xff0c;创造新的商业机会并帮助改变我们所知道的世界。1公民AI&#xff08;人工智能&#xff09;AI随处可见&#xff0c;在整个社会范围内…

java 导出excel教程_Java导出Excel表格

Java导出Excel表格 导出Excel表格需要一个poi-3.9.jar的包&#xff0c;该包在网上可以找到。第一步&#xff0c;创建Excel对象。 HSSFWorkbook workbook new HSSFWorkbook();创建一个工作表。 HSSFSheet sheet workbook.createSheet("日常收入报表");创建合并单元格…

大数据竞赛平台——Kaggle 入门

From&#xff1a;http://blog.csdn.net/u012162613/article/details/41929171 大数据竞赛平台——Kaggle 入门篇 这篇文章适合那些刚接触Kaggle、想尽快熟悉Kaggle并且独立完成一个竞赛项目的网友&#xff0c;对于已经在Kaggle上参赛过的网友来说&#xff0c;大可不必耗费时间…

俄罗斯智库 | 人工智能在军事领域的发展现状及应用前景

本文来自&#xff1a;知远战略与防务研究所原文来自&#xff1a;俄罗斯外交和国防政策委员会网站人工智能在军事领域的发展现状及应用前景本文主要介绍了当今世界及俄罗斯本国人工智能技术发展的现状以及未来该技术在军事领域的应用前景&#xff0c;文章还重点分析了人工智能技…

Internet Explorer 8 使用技巧(3):安全上网

如何上网更安全&#xff1f; 听说过“网络钓鱼”吗&#xff1f;最简单的例子&#xff1a;有不法分子模仿真实的银行网站做了个一模一样的&#xff0c;然后通过邮件或其他方式骗你登录并输入银行账号和密码&#xff0c;结果信息泄露&#xff0c;钱被盗走——这就是最典型的“钓鱼…

java 常量折叠_深入理解Java虚拟机之早期编译器优化

Javac编译器Javac编译器是一个由Java语言编写的程序Javac的源码与调试从Sun Javac的代码来看&#xff0c;编译器大致分为3个过程&#xff1a;解析与填充符号表的过程插入式注解处理器的注解处理过程分析与字节码生成的过程Javac编译动作的入口为com.sun.tools.javac.main.JavaC…

Spring Data JPA 从入门到精通~JpaRepository介绍

从 JpaRepository 开始的子类&#xff0c;都是 Spring Data 项目对 JPA 实现的封装与扩展。JpaRepository 本身继承 PagingAndSortingRepository 接口&#xff0c;是针对 JPA 技术的接口&#xff0c;提供 flush()、saveAndFlush()、deleteInBatch()、deleteAllInBatch() 等方法…

深入理解程序从编译到运行

From&#xff1a;http://blog.chinaunix.net/uid-22327815-id-3540305.html 从 Hello World 说程序运行机制&#xff1a;http://www.sohu.com/a/132798003_505868 C/C 中如何在 main() 函数之前执行一条语句&#xff1f;&#xff1a;https://www.zhihu.com/question/26031933 …

技术力量 | 社会计算与计算社会: 智慧社会的基础与必然

来源&#xff1a;智慧城市决策参考摘要&#xff1a;基于社会计算&#xff0c;智慧社会可充分利用开放流动的大数据资源, 综合协调人、地、事、物和组织等各种要素, 形成信息对称、权利对等、扁平化组织的社会结构, 推动传统社会管理模式向分布式、集约化、信息化、智能化、全响…

php关闭当前页_php如何直接关闭页面注销SESSION

php如何直接关闭页面注销SESSION发布时间&#xff1a;2020-07-09 09:03:06来源&#xff1a;亿速云阅读&#xff1a;100作者&#xff1a;Leah这篇文章将为大家详细讲解有关php如何直接关闭页面注销SESSION&#xff0c;文章内容质量较高&#xff0c;因此小编分享给大家做个参考&a…

ELF格式文件符号表全解析及readelf命令使用方法

From&#xff1a;http://blog.csdn.net/yasi_xi/article/details/45197583 readelf命令&#xff1a;http://man.linuxde.net/readelf ELF文件格式解析&#xff1a;https://www.2cto.com/kf/201605/511370.html ELF文件格式解析&#xff1a;http://blog.csdn.net/earbao/arti…