Linux系统文件显示及实际大小与文件洞问题

本文发布时间: 2019-Mar-22
前言:我们经常用ls命令,du命令,可是看到的结果不同,他们之间有什么关系呢?:如 [root@syslab filetest]# du -h * 0 file_0 4.0K file_1 4.0K file_2 4.0K file_4096 8.0K file_4097 4.0K hole [root@syslab filetest]# ll -h total 24K -rw-r--r--. 1 root root 0 Jan 10 16:15 file_0 -rw-r--r--. 1 root root 1 Jan 10 16:15 file_1 -rw-r--r--. 1 root root 2 Jan 10 16:15 file_2 -rwxr-xr-x. 1 root root 4.0K Jan 10 16:15 file_4096 -rw-r--r--. 1 root root 4.1K Jan 10 16:15 file_4097 -rw-r--r--. 1 root root 25K Jan 10 17:30 hole:du命令和ll(ll相当于ls -l)命令看到的大小不一样啊!我如何知道文件到底是多大呢?我们接下来讨论这个问题环境,ext3,块大小格式化为4096byte本文将要说明的问题文件显示大小(ls命令): ext3_inode中的i_size 字段(这个字段是我们以为这个文件应该是多大,比如我们写了一个字节,那么我们就以为这个文件是1个字节大)文件实际大小(du命令):ext3_inode中的i_blocks字段(这里的block大小固定(与文件系统的块大小不同),固定为512byte,即使文件系统创建块大小为4096byte,这里的block计算始终是512byte),比如我们写了一个字节到这个文件,实际这个文件占用了1个文件系统创建时候的一个块大小。文件中的洞是为了节约实际的存储空间,洞不占用实际的磁盘块,isize的显示大小包含洞的大小(比如10M的洞,这10M也会加到文件的大小里面去),磁盘的实际物理块没有存储洞大小(实际没有使用块来存这10M的洞) 细节说明 echo 1 >/aa #echo时写入1还会写入一个换行符 echo -n 1 >/bb #echo 时不会写入末尾换行符做以下游戏来观察这个问题我们建立5个不同内容的文件 for((i=1;i<=4096;i++))do echo -n a>>file_4096;done for((i=1;i<=4097;i++))do echo -n a>>file_4097;done for((i=1;i<=1;i++))do echo -n a>>file_1;done for((i=1;i<=1;i++))do echo a>>file_2;done touch file_0ll查看这5个文件 ls -lh -rw-r--r--. 1 root root 0 Jan 10 16:15 file_0 -rw-r--r--. 1 root root 1 Jan 10 16:15 file_1 -rw-r--r--. 1 root root 2 Jan 10 16:15 file_2 -rw-r--r--. 1 root root 4096 Jan 10 16:15 file_4096 -rw-r--r--. 1 root root 4097 Jan 10 16:15 file_4097现在,file_4096有4096byte,file_2有2个byte大小了,5个文件大小如命名du 查看这5个文件 du -h * [root@syslab filetest]# du -h * 0 file_0 4.0K file_1 4.0K file_2 4.0K file_4096 8.0K file_4097 4.0K holeext3_inode的磁盘存储格式 struct ext3_inode { __le16 i_mode; /* File mode */ __le16 i_uid; /* Low 16 bits of Owner Uid */ __le32 i_size; /* Size in bytes */ __le32 i_atime; /* Access time */ __le32 i_ctime; /* Creation time */ __le32 i_mtime; /* Modification time */ __le32 i_dtime; /* Deletion Time */ __le16 i_gid; /* Low 16 bits of Group Id */ __le16 i_links_count; /* Links count */ __le32 i_blocks; /* Blocks count */ .. __le32 i_block[EXT3_N_BLOCKS];/* Pointers to blocks */ .. }这是ext3具体文件对应inode在磁盘上的实际存储格式 其中,i_size表示文件的显示大小,即我们以为的文件大小。 i_blocks表示实际此文件占用多少个blocks(这里的block大小固定(与文件系统的块大小不同),固定为512byte,不随格式化时候块大小的变化而变化)。查出i_size和i_blocks在磁盘上到底存储的值为多少找出这5个文件的inode编号 [root@syslab filetest]# ll -i |sort -k 1 273974 -rw-r--r--. 1 root root 4096 Jan 10 16:15 file_4096 273975 -rw-r--r--. 1 root root 4097 Jan 10 16:15 file_4097 273976 -rw-r--r--. 1 root root 1 Jan 10 16:15 file_1 273977 -rw-r--r--. 1 root root 2 Jan 10 16:15 file_2 273978 -rw-r--r--. 1 root root 0 Jan 10 16:15 file_0 第一列就是inode的编号找出这5个inode在磁盘上的存储位置和内容(即具体存在哪个块,内容是什么) 根据ext3磁盘划分格式,我们使用debugfs来查看这个inode所在磁盘具体位置 debugfs -R 'stats' /dev/sda2 ... Block size: 4096 ... Inodes per group: 8192 ... Inode size: 256 ... Group 32: block bitmap at 1048576, inode bitmap at 1048592, inode table at 1048608 23336 free blocks, 0 free inodes, 968 used directories, 0 unused inodes [Checksum 0x0cdd] Group 33: block bitmap at 1048577, inode bitmap at 1048593, inode table at 1049120 0 free blocks, 4470 free inodes, 217 used directories, 4470 unused inodes [Checksum 0xd341] ...其中,我们看到Inodes per group: 8192,我们得知这5个inode存储的块组为(inode在ls显示中的编号是从1开始的,内核里面是从0开始的) 273974/8192=33;块组中inode下标为余数3638;(数组下标从0开始) 273975/8192=33;块组中inode下标为余数3639;(数组下标从0开始) 273976/8192=33;块组中inode下标为余数3640;(数组下标从0开始) 273977/8192=33;块组中inode下标为余数3641;(数组下标从0开始) 273978/8192=33;块组中inode下标为余数3642;(数组下标从0开始 同时,我们从debugfs的输出中得知,块组33的inodetable在块号为1049120的数据块中 那么上面5个inode结构体在磁盘上面的具体内容是什么呢 直接去看磁盘上面存的是什么 由于磁盘块大小为4096byte(格式化的时候指定的),inodetable的起始位置为1049120*4096(块号*块大小),所以这5个inode的位置为 file_4096对应的inode在磁盘的 1049120*4096+(3638-1)*256到1049120*4096+3638*256存放着(单位:字节) 我们读取这个inode的磁盘数据 1049120*4096+3637*256=4298126592 我们读写磁盘上面的裸数,看到底存的是什么值 [root@syslab filetest]# dd if=/dev/sda2 bs=1 count=256 skip=4298126592 |od -t x4 -Ax 000000 000081a4 00001000 50ee78a8 50ee78a8 000010 50ee78a8 00000000 00010000 00000008 000020 00080000 00000001 0001f30a 00000004 000030 00000000 00000000 00000001 001125c7我们先简单确认一下这段数据是不是file_4096结构体在磁盘上面的数据 我们知道 struct ext3_inode { __le16 i_mode; /* File mode */前面2字节是访问模式,所以 [root@syslab ~]# dd if=/dev/sda2 bs=1 count=256 skip=4298126592|od -t x2 -Ax 000000 81a4 0000 1000 0000 78a8 50ee cdbd 50ef得到前面两字节为0x81a4等于八进制100644,为rw,r,r模式,ll查看一下 -rw-r--r--. 1 root root 4096 Jan 10 16:15 file_4096确实正确,然后我们chmod +x file_4096看结果,ls –l -rwxr-xr-x. 1 root root 4096 Jan 10 16:15 file_4096模式后几位为755才对 [root@syslab filetest]# dd if=/dev/sda2 bs=1 count=256 skip=4298126592 |od -t x2 -Ax 000000 81ed 0000 1000 0000 78a8 50ee cde7 50ef前面两字节为0x81ed等于八进制100755,为755!就是这个文件 其实我们还可以看时间 创建时间0x50ee78a8=1357805736 [root@syslab filetest]# date -d "@1357805736" Thu Jan 10 16:15:36 CST 2013 或者修改文件来看修改实际等等来确认根据i_size,i_blocks在ext3_inode中的偏移量查看其具体数据由于我们知道i_size,i_blocks在ext3_inode中的具体偏移量 __le32 i_size;的位置为相对于struct ext3_inode结构体的偏移量为0x4, __le32 i_blocks的偏移量为0x1c且则两个值都是4字节长度,所以我们下看一下file_4096的i_size和i_blocks到底是多少 [root@syslab filetest]# dd if=/dev/sda2 bs=1 count=256 skip=4298126592 |od -t x4 -Ax 000000 000081ed 00001000 50ee78a8 50efcde7 000010 50ee78a8 00000000 00010000 00000008 000020 00080000 00000001 0001f30a 00000004 000030 00000000 00000000 00000001 001125c7 000040 00000000 00000000 00000000 00000000 ..所以根据偏移量我们可以得到这两个值 i_size=0x00001000=4096,确实为ls显示大小 i_blocks=0x00000008=8,8个块(上面我们已经指出,这里块固定为512字节,与文件系统层次块大小不同),8*512=4096,正好为一个块 所以,file_4096正好为一个块,显示大小为4096 byte,实际大小也为4096byte 同样的方法我们看其余4个file,得到结果如下 file_4097, i_size=0x00001001=4097byte i_blocks=0x00000010=16 (16*512=4096*2,即实际占两个块!) file_1 i_size=0x00000001=1byte i_blocks=0x00000008=8 (8*512=4096,即1byte也占用1个块) file_2 i_size=0x00000002=2byte i_blocks=0x00000008=8 (8*512=4096,即2byte也占用1个块) file_0 i_size=0x00000000=0byte i_blocks=0x00000000=0 (0byte不占用任何数据块)然后我们把i_size和i_blocks的值和ls -l命令和du -h *命令结果对比得知 i_size和ls命令显示大小相同,所以这是我们以为的大小,比如我写了一个字符到一个文件,我们会以为这个文件大小为1个字节 i_blocks和du命令显示大小相同,所以这个是我们磁盘存储的实际大小,比如我写了一个字符到一个文件,但实际磁盘占据了一个块(一般是4096byte)大小带洞的文件如何存储先科普一下文件的洞文件的洞是普通文件的一部分,它是一些空字符但没有存放在磁盘的任何数据块中。洞是Unix文件一直存在的一个特点。引入文件的洞是为了避免磁盘空间的浪费。文件洞在Ext2的实现是基于动态数据块的分配:只有当进程需要向一个块写数据时,才真正把这个块分配给文件。磁盘实际存储中,与文件关联的inode的i_block数组(inode关联的具体物理块号数组)仅存放已分配块的逻辑块号,而数组中的其它元素都为空。我们来看一下具体的磁盘上文件的洞如何存储 echo -n "a" | dd of=/root/filetest/hole bs=4096 seek=6表示对于文件/root/filetest,前面4096*6byte全部跳过,然后写入一个字符“a”,我们用上面的方法看一下ls -a命令查看 273979 -rw-r--r--. 1 root root 24577 Jan 10 17:30 hole,这个文件显示占用了24577byte,可是实际占用多少磁盘呢?du -h hole命令查看 4.0K holedump磁盘上面的16进制数据来看 i_size=0x00006001=24577 i_blocks=0x00000008 (8*512=4096byte,占1个块大小!空洞不占用实际的数据块!)总结ls命令看到的是文件的显示大小 即磁盘上inode结构体中的i_size值,单位bytedu命令看到的是文件的实际磁盘存储大小 即磁盘上inode结构体中的i_blocks*512,单位byte i_blocks固定为512byte,这和文件系统格式化的时候的块大小不同且不随文件系统格式化块大小的变化而变化文件的洞不会再磁盘上面占用实际的存储空间块 即文件如果包含200M的洞,这200M不占用时间的磁盘块文件的洞在ls显示的时候包含文件的洞 即文件如果包含200M的洞,ls就会加上这200M的空洞 注:我这里是从磁盘的实际存储数据来对比ls,du显示结果,并没有直接查看ls,du命令的源码,如果有错误,欢迎指正~


(以上内容不代表本站观点。)
---------------------------------
本网站以及域名有仲裁协议。
本網站以及域名有仲裁協議。

2024-Mar-04 02:11pm
栏目列表