不知道你有没有遇到过当一个文件夹下文件特别多,在下面执行ls
命令的时候要等好长时间才能展示出来的问题?若是有,你有想过这是为何吗,咱们该如何解决?
要想深刻理解这个的问题产生的缘由,咱们就须要从文件夹占用的磁盘空间开始讨论了。node
在《新建一个空文件占用多少磁盘空间?》中我提到了每个文件会消耗其所在文件夹中的一点空间。文件夹呢,其实也同样会消耗inode的。 咱们先看一下当前inode的占用状况linux
# df -i Filesystem Inodes IUsed IFree IUse% Mounted on ...... /dev/sdb1 2147361984 12785020 2134576964 1% /search
再建立一个空文件夹数组
# mkdir temp # df -i Filesystem Inodes IUsed IFree IUse% Mounted on ...... /dev/sdb1 2147361984 12785021 2134576963 1% /search
经过IUsed能够看到,和空文件同样,空的文件夹也会消耗掉一个inode。不过这个很小,个人机器上才是256字节而已,应当不是形成ls
命令卡主的元凶。bash
文件夹的名字存在哪儿了呢?嗯,和《新建一个空文件占用多少磁盘空间?》里的文件相似,会消耗一个ext4_dir_entry_2
(今天用ext4举例,它在linux源码的fs/ext4/ex4.h文件里定义),放到其父目录的block里了。根据这个,相信你也很快能想到,若是它本身节点下建立一堆文件的话,就会占用它本身的block。咱们来动手验证一下:服务器
# mkdir test # cd test # du -h 4.0K .
这里的4KB就表示消耗掉了一个block。 空文件不消耗block,空目录为啥一开始就消耗block了呢,那是由于其必须默认带两个目录项"."和".."。另外这个4K在你的机器上不必定是这么大,它实际上是一个block size,在你格式化的时候决定的。操作系统
咱们再新建两个空的文件,再查看一下:指针
# touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab # touch aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa # du -h 4.0K .
貌似,没有什么变化。这是由于code
那么我再多建立一些试试,动用脚本建立100个文件名长度为32Byte的空文件。对象
#!/bin/bash for((i=1;i<=100;i++)); do file="tempDir/"$(echo $i|awk '{printf("%032d",$0)}') echo $file touch $file done
# du -h 12K .
哈哈,这时咱们发现目录占用的磁盘空间变大了,成了3个Block了。当咱们建立到10000个文件的时候,开发
# du -h 548K .
在每个ext4_dir_entry_2
里都除了文件名之外,还记录着inode号等信息,详细定义以下:
struct ext4_dir_entry_2 { __le32 inode; /* Inode number */ __le16 rec_len; /* Directory entry length */ __u8 name_len; /* Name length */ __u8 file_type; char name[EXT4_NAME_LEN]; /* File name */ };
咱们计算一下,平均每一个文件占用的空间=548K/10000=54字节。也就是说,比咱们的文件名32字节大一点点,基本对上了。 这里咱们也领会到一个事实,文件名越长,在其父目录中消耗的空间也会越大。
一个文件夹固然也是要消耗磁盘空间的。
ext4_dir_entry_2
,保存本身inode号,目录名。ext4_dir_entry_2
数组目录下的文件/子目录越多,目录就须要申请越多的block。另外ext4_dir_entry_2
大小不是固定的,文件名/子目录名越长,单个目录项消耗的空间也就越大。
对于开篇的问题,我想你如今应该明白为何了,问题出在文件夹的block身上。
这就是当你的文件夹下面文件特别多,尤为是文件名也比较长的时候,它会消耗掉很是多的block。当你遍历文件夹的时候,若是Page Cache中没有命中你要访问的block,就会穿透到磁盘上进行实际的IO。在你的角度来看,就是你执行完ls
后,卡住了。
那么你确定会问,我确实要保存许许多多的文件,我该怎么办? 其实也很简单,多建立一些文件夹就行了,一个目录下别存太多,就不会有这个问题了。工程实践中,通常的作法就是经过一级甚至是二级hash把文件散列到多个目录中,把单目录文件数量控制在十万或万如下。
貌似今天的实践应该结束了,如今让咱们把刚刚建立的文件所有删掉,再看一下。
# rm -f * # du -h 72K .
等等,什么状况?文件夹下的文件都已经删了,该文件夹为何还占用72K的磁盘空间?
这个疑惑也伴随了我很长时间,后来才算是解惑。问题关键在于ext4_dir_entry_2
中的rec_len
。这个变量存储了当前整个ext4_dir_entry_2
对象的长度,这样操做系统在遍历文件夹的时候,就能够经过当前的指针,加上这个长度就能够找到文件夹中下一个文件的dir_entry
了。这样的优点是遍历起来很是方便,有点像是一个链表,一个一个穿起来的。
可是,若是要删除一个文件的话,就有点小麻烦了,当前文件结构体变量不能直接删,不然链表就断了。
Linux的作法是在删除文件的时候,在其目录中只是把inode设置为0就拉倒,并无回收整个ext4_dir_entry_2
对象。其实和你们作工程的时候常常用到的假删除是一个道理。如今的xfs文件系统好像已经没有这个小问题了,但具体咋解决的,暂时没有深刻研究,若是你有答案,欢迎留言!
开发内功修炼之硬盘篇专辑:
个人公众号是「开发内功修炼」,在这里我不是单纯介绍技术理论,也不仅介绍实践经验。而是把理论与实践结合起来,用实践加深对理论的理解、用理论提升你的技术实践能力。欢迎你来关注个人公众号,也请分享给你的好友~~~