不知道大家有没有遇到过一个文件夹里面的文件太多,执行下面的ls命令需要很长时间才能显示的问题?如果是这样,你有没有想过这是为什么,我们该如何解决?要深入了解这个问题的原因,我们需要从文件夹占用的磁盘空间开始讨论。Inode消耗验证在《新建一个空文件占用多少磁盘空间?》中我提到过每个文件在它所在的文件夹中都会消耗一点空间。事实上,文件夹也会消耗inode。看看当前inode占用情况#df-iFilesystemInodesIUsedIFreeIUse%Mountedon....../dev/sdb121473619841278502021345769641%/search创建一个空文件夹#mkdirtemp#df-iFilesystemInodesIUsedIFreeIUse%Mountedon....../dev/sdb121473619841278502121345769631%/search通过IUsed可以看出,和空文件一样,空文件夹也会消耗一个inode。但是这个很小,在我的机器上只有256字节,应该不是导致ls命令卡住的罪魁祸首。块消费验证文件夹的名字在哪?嗯,和《新建一个空文件占用多少磁盘空间?》中的文件类似,会消费一个ext4_dir_entry_2(今天以ext4为例,定义在linux源码的fs/ext4/ex4.h文件中),放到其父目录的块。基于此,相信你很快就能想象到,如果它在自己的节点下创建了一堆文件,就会占据自己的block。让我们手动验证一下:#mkdirtest#cdtest#du-h4.0K。这里的4KB表示已经消费了一个block。空文件不消耗块。为什么一个空目录在开始时会消耗块?那是因为它必须有两个目录项“.”。和“..”默认情况下。另外这个4K在你的机器上不一定有那么大,它其实是一个块大小,你格式化的时候就确定了。让我们创建两个新的空文件并再次检查它们:#touchaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab#touchaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa#du-h4.0K。似乎没有任何变化。这是因为首先新的空文件不占用block,所以这里显示的还是目录占用的block。其次,之前创建文件夹时分配的4KB还有空闲空间,足够存放这两个文件项。然后我会尝试创建更多,并使用脚本创建100个文件名长度为32Byte的空文件。#!/bin/bashfor((i=1;i<=100;i++));dofile="tempDir/"$(echo$i|awk'{printf("%032d",$0)}')回显$filetouch$filedone#du-h12K。哈哈,这时候我们发现目录占用的磁盘空间变大了,变成了3Blocks。当我们创建10,000个文件时,#du-h548K。在每个ext4_dir_entry_2中,除了记录文件名外,还记录了inode号等信息。详细定义如下:structext4_dir_entry_2{__le32inode;/*索引节点号*/__le16rec_len;/*目录条目长度*/__u8name_len;/*名称长度*/__u8file_type;字符名称[EXT4_NAME_LEN];/*文件名*/};算一下,每个文件平均占用的空间=548K/10000=54字节。也就是说比我们32字节的文件名大了一点点,基本是一样的。在这里我们也体会到,文件名越长,它在其父目录中占用的空间就越多。这篇文章的结论是,文件夹当然也很占磁盘空间。首先需要消耗一个inode。它在我的机器上是256字节,需要消耗其父目录下的目录条目ext4_dir_entry_2来保存自己的inode编号和目录名称。如果在其下创建文件夹或文件,则需要其自身block中ext4_dir_entry_2数组目录下的文件/子目录越多,目录申请的block越多。另外,ext4_dir_entry_2的大小不固定,文件名/子目录名越长,单个目录项占用的空间越大。关于一开始的问题,我想你现在应该明白为什么了,问题出在文件夹的block上。这是当你的文件夹下的文件太多的时候,尤其是文件名比较长的时候,会消耗很多块。当你遍历文件夹时,如果pagecache没有命中你要访问的block,它就会穿透到磁盘进行实际IO。从你的角度来看,它在你执行ls后卡住了。那你肯定会问,我好想保存很多文件,怎么办?其实很简单,多建几个文件夹就行了,一个目录不要放太多文件,就不会出现这个问题。在工程实践中,一般的做法是通过primary甚至secondaryhash将文件hash到多个目录,将单个目录的文件数控制在10万以内或更少。extbug好像今天的练习就到此为止了。现在让我们删除刚刚创建的所有文件并再次查看。#rm-f*#du-h72K。等等,这是怎么回事?文件夹中的所有文件都被删除了,为什么这个文件夹还占用72K的磁盘空间?这个疑惑一直伴随着我,后来才解开了疑惑。问题的关键在于ext4_dir_entry_2中的rec_len。这个变量存储了整个ext4_dir_entry_2对象的当前长度,这样当操作系统遍历文件夹时,可以通过将长度加到当前指针来找到文件夹中下一个文件的dir_entry。这样做的好处是遍历起来很方便,有点像链表,一个一个穿。但是,如果要删除一个文件,就有点麻烦了。当前文件结构变量不能直接删除,否则链表会断。linux的做法是在删除一个文件时,只是将其目录下的inode设为0拉下来,并不回收整个ext4_dir_entry_2对象。其实和大家做项目时经常用到的误删是一样的道理。现在的xfs文件系统好像没有这个小问题,但是怎么解决暂时没有深入研究。如果您有答案,请留言!练内功练硬盘特技:1、开盘:剥去机械硬盘的硬外衣!2.磁盘分区也暗示技术水平。3、机械硬盘速度慢,容易坏,如何解决?4.拆解SSD结构5.一个新的空文件占用多少磁盘空间?6.一个只有1字节的文件实际占用多少磁盘空间?7、为什么文件太多时ls命令会卡住?8.理解格式化原理9.读取文件一个字节实际会发生多少磁盘IO?10.文件写入一个字节后什么时候开始写磁盘IO?11、机械硬盘的随机IO比你想象的要慢。12、服务器配置固态硬盘比机械硬盘快多少?我的是《练内功》。在这里我不是简单地介绍技术理论,也不是只是介绍实践经验。而是理论联系实际,用实践加深对理论的理解,用理论提高技术实践能力。欢迎关注我的,分享给你的朋友吧~~~
