在查看同一个文件的大小的时候,可以用 ls 或者 du 指令。细心的使用者有时候会发现它们显示出来的大小是不一样的,比如:
造成这种现象主要有两个原因。
1、du 跟 ls 默认情况下显示的大小含义不同
先要准备个知识:在文件系统中,对磁盘的操作并不会具体到字节,而是操作文件块(Block)。
想象一下如果不分块而是直接按字节读取(实际上会退化成按扇区逻辑块,这里为了方便,用字节来举例),那么,要么就按照连续存储而导致碎片问题,要么就只能按照字节粒度来对硬盘发出指令读取而导致效率问题。
块(Block)的提出可以折中上面的两个优缺点。自己最明显的缺点是会造成一定的磁盘浪费,比如文件系统规定块大小是 4K,那么存储 1Byte 的文件也需要占用一个块, 4K 的磁盘空间。不过这个缺点在存储单价越来越低的时代下,对比优点就不足为道了,因此几乎所有现代的文件系统都已经采用了此种策略。
Linux 系统下的 EXT4 文件系统默认 Block 大小是 4K,Windows 下 NTFS 的 Block 叫 Cluster(簇),其实也是一个意思,默认大小一般也是 4K(如果总大小更大比如超过 16T,默认策略也会变化,变得更大等等)。
回到 du 跟 ls 上,du 默认显示出来的含义,是实际的磁盘占用,所以上面的 config.js 是 4k(占了一个 Block),而 ls 默认显示的是文件实际的大小,也就是 2.3k(不够一个 Block)。
当然 ls 也可以通过参数来改变显示的含义,加上 -s 参数即可显示实际占用了。如下图:
Windows 也能感受到这种差异。对文件进行右键即可看到两种不同的大小。
2、稀疏文件的存在
稀疏文件的稀疏,类似于稀疏矩阵的稀疏。也就是中间有大量的 0 (空洞)。
稀疏文件其实就是对有大量的0的文件的文件系统级压缩。可以这么不严谨地理解:对于有100个0的文件,00000…0000 ,如果是稀疏文件格式,直接表示为 100x0 。
目前 Windows 跟 Linux 都有对稀疏文件的支持,macOS 在 2016年开始好像也升级了支持。
Windows 下创建稀疏文件参考:稀疏文件文档
Linux 下,可以用 dd 指令:
[admin@Test ~]$ dd if=/dev/null of=mysparse seek=1G count=0
记录了0+0 的读入
记录了0+0 的写出
0字节(0 B)已复制,0.000725152 秒,0.0 kB/秒
[admin@Test ~]$ du -h mysparse
0 mysparse
也可以用C写入:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char *argv[])
{
if(argc < 2)
{
printf("输入错误,请重新输入!");
}
//打开文件
int fw = open(argv[1],O_WRONLY | O_CREAT,0664);
if(fw<0)
{
perror("open error");
return -1;
}
//操作文件
lseek(fw,1024*1024*1024,SEEK_SET);
write(fw,"\0",1);
//关闭文件
close(fw);
return 0;
}
执行
sparse mysparse
即可生成 mysparse 空洞文件。上面两种方式都用了 seek 方法,也就是文件操作指针偏移。这样的操作会让文件系统识别文件为空洞文件(中间跳过了数据进行写入)。
空洞文件的用处很多,比如说在用迅雷下载的时候,可以立即开辟出指定大小的文件。这样多线程下载文件的不同部位的时候再慢慢写入就可以了。而如果没有空洞文件的设定,就只能一点点下载、追加。
另外还有在块设备文件里的应用,也非常经典。参考写的另一篇文章:PVE存储