ls -l file 查看文件逻辑大小
ls -s file 查看文件实际占用的存储块大小
du -c file 查看文件实际占用的存储块多少(或者du -h file )
du :磁盘使用量,占用的磁盘空间(disk usage)。
ls 参数 -s, --size
: print the allocated size of each file, in blocks
du 参数 -s, --summarize
:display only a total for each argument
背景知识
稀疏文件(Sparse File)
a sparse file is a type of computer file that attempts to use file system space more efficiently when blocks allocated to the file are mostly empty. This is achieved by writing brief information (metadata) representing the empty blocks to disk instead of the actual "empty" space which makes up the block, using less disk space. The full block size is written to disk as the actual size only when the block contains "real" (non-empty) data.
稀疏文件与其他普通文件基本相同,区别在于文件中的部分数据是全0,且这部分数据不占用磁盘空间。
在Linux中,lseek的系统调用是可以改变在文件上面的偏移量(offset)的,而且还允许其超出文件的长度。偏移量一旦超出了文件的长度,下一次进行文件IO写入操作文件的时候便会延续偏移量的位置继续写入,进而在文件中间产生了空洞的部分,这部分会以”\0”填充,而从原来的文件结尾到新写入数据间的这段空间就被称为“文件空洞(hole file)
”。
空洞是否占用磁盘空间由文件系统(file system)决定的。
文件系统存储稀疏文件时,inode索引节点中,只给出实际占用磁盘空间的block号,数据全0,且不占用磁盘空间的文件block并没有物理磁盘block号。
可以使用 truncate 或 dd 命令创建稀疏文件,如创建一个512 MiB的稀疏文件:
$ truncate -s 512M file.img $ dd if=/dev/zero of=file.img bs=1 count=0 seek=512M
复制
为什么 ls 和 du 命令显示文件大小有时会相差如此大呢?
因为这个文件可能是稀疏文件。
ls 获取得到的是文件的逻辑大小,而du获取得到的是文件的实际占用物理块的大小。即du显示的size是文件在硬盘上占据了多少个block计算出来的。
一个文件占用的磁盘空间和一个文件的大小是两码事情。
占用空间取决于文件系统的块(block)的大小,linux一般默认是4k(4096) 。因此,一个大小为1个字节的文件,最小也要占用4k,如果你创建文件系统的时候制定块大小是16K,那么即便一个文件只有1个字节,占用空间也是16K。
通常情况下,ls 显示的文件大小比du显示的磁盘占用空间小,比如文件系统的block是4K,一个13K的文件占用的空间是 13k/4k = 3.25 个block,一个block只能被一个文件占用,因此实际占用空间就是4个block,就是16K。
如果一个文件有比较大的黑洞,那么会出现文件大小比磁盘空间占用大的情况。
如果一个分区上主要放大文件,那么block可以大一些,有利于减少磁盘碎片,如果主要放小文件,那么block设置小一下,否则太浪费磁盘空间。
这里以 redis-shake.log 日志为例
# nohup ./redis-shake -type=sync -conf=redis-shake.conf > /root/redis-shake.log 2>&1
# ll -h redis-shake.log
-rw-r--r-- 1 root root 30M Apr 14 13:46 redis-shake.log
# ls -sh redis-shake.log
952K redis-shake.log
# du -h redis-shake.log
952K redis-shake.log
# > redis-shake.log
# ll -h redis-shake.log
-rw-r--r-- 1 root root 30M Apr 14 13:47 redis-shake.log
# ls -sh redis-shake.log
20K redis-shake.log
# du -h redis-shake.log
20K redis-shake.log复制
可以看到 ls -sh
列出来的大小和 du 出来的大小是一样的,而使用 ls -l
显示文件的大小就明显不同。
可通过命令stat查看文件的inode
# stat redis-shake.log File: ‘redis-shake.log’ Size: 31187216 Blocks: 40 IO Block: 4096 regular file Device: 802h/2050d Inode: 67551822 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2022-04-14 13:43:40.065202676 +0800 Modify: 2022-04-14 13:48:08.548311479 +0800 Change: 2022-04-14 13:48:08.548311479 +0800 Birth: -
复制
使用od命令来查看文件的二进制内容,或者通过vim打开,以二进制形式查看(:%!xxd
)
# od -b redis-shake.log|head 0000000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 000 * 166731240 000 000 062 060 062 062 057 060 064 057 061 064 040 061 063 072 166731260 064 067 072 063 063 040 133 111 116 106 117 135 040 104 142 123 …… 或 # od -c redis-shake.log|head 0000000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 ……
复制
可以看到文件 redis-shake.log 的开始是用许多的”0”来填充的
实验演示
- 创建一个实际的无空洞文件
从 /dev/urandom 中读取 count=1000 个 block(每块大小为4096) 输出到 testfile1 文件中
# dd if=/dev/urandom of=testfile1 bs=4096 count=1000
1000+0 records in
1000+0 records out
4096000 bytes (4.1 MB) copied, 0.0260712 s, 157 MB/s
# ls -lh testfile1 && du -h testfile1
-rw-r--r-- 1 root root 4.0M Apr 14 16:44 testfile1
4.0M testfile1复制
用 ls -lh 和 du -h 查看这个文件的大小,两者是一样的,都显示4Mb。
- 创建一个稀疏文件(也就是有空洞的文件)
跟前一个命令相似,不同的是,它其实复制了1个块的内容,前面的999个块(seek=999)都跳过了。
# dd if=/dev/urandom of=testfile2 bs=4096 seek=999 count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 0.000163704 s, 25.0 MB/s
# ls -lh testfile2 && du -h testfile2
-rw-r--r-- 1 root root 4.0M Apr 14 16:43 testfile2
4.0K testfile2复制
命令 ls -l 看文件testfile2 的大小还是4Mb 而用du -h 查看,占用的块大小是4Kb。即虽然文件是4M,但是实际在磁盘上只占用了4K的大小,这就是空洞文件的神奇之处。
对比普通文件与稀疏文件,区别在于稀疏文件中的部分数据是全0
# od -c testfile1 | head
0000000 210 361 [ c . 253 " 343 g 004 226 . 032 262 R 260
0000020 363 t 317 ] 233 204 301 023 204 323 316 370 266 363 375 275
0000040 ` 255 354 z 240 & ` 313 243 \r 6 225 ' u ] 315
……
# od -c testfile2 | head
0000000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
17470000 / 201 F 200 317 V 313 \v 356 250 325 216 006 : 0 355
……复制
创建一个大的稀疏文件,看磁盘空间和inode消耗
count=1 bs=1G seek=49
创建一个 block块为1G的稀疏文件,偏移49。
也就是这个稀疏文件的“逻辑”文件大小:count (1+49) x bs (1G) = 50GB
实际占用磁盘空间为:count (1) x bs (1G) = 1GB
-- 创建文件前
# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sdb 50G 801M 50G 2% /data
# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sdb 26214400 6 26214394 1% /data
-- 创建文件后
# dd if=/dev/zero of=demofile count=1 bs=1G seek=49
1+0 records in
1+0 records out
1073741824 bytes (1.1 GB) copied, 1.26783 s, 847 MB/s
# ll -th demofile
-rw-r--r-- 1 root root 50G Apr 15 10:45 demofile
# ls -sh demofile && du -h demofile
1.0G demofile
1.0G demofile
# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/sdb 50G 1.8G 49G 4% /data
# df -i
Filesystem Inodes IUsed IFree IUse% Mounted on
/dev/sdb 26214400 7 26214393 1% /data
# stat demofile
File: ‘demofile’
Size: 53687091200 Blocks: 2097152 IO Block: 4096 regular file
Device: 810h/2064d Inode: 68 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2022-04-15 10:45:16.577368478 +0800
Modify: 2022-04-15 10:45:17.083372485 +0800
Change: 2022-04-15 10:45:17.083372485 +0800
Birth: -
# od -c demofile | head
0000000 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0 \0
*
……复制
Linux稀疏文件Inode数据块存储:文件空洞部分不占用磁盘空间,文件所占用的磁盘空间仍然是连续的。
常见的稀疏文件使用场景
- 刚开始下载电影时,文件的大小就已经到GB级别了。
- 创建虚拟机的磁盘镜像时,创建一个100G的磁盘镜像,但是其系统安装完成后也只占用了3GB左右的磁盘空间;如果一开始把100G都分配出去的话,无疑是很大的浪费。