关于linux:文件过多时ls命令为什么会卡住

54次阅读

共计 2868 个字符,预计需要花费 8 分钟才能阅读完成。

不晓得你有没有遇到过当一个文件夹下文件特地多,在上面执行 ls 命令的时候要等好长时间能力展示进去的问题?如果有,你有想过这是为什么吗,咱们该如何解决?
要想深刻了解这个的问题产生的起因,咱们就须要从文件夹占用的磁盘空间开始探讨了。

inode 耗费验证

在《新建一个空文件占用多少磁盘空间?》中我提到了每一个文件会耗费其所在文件夹中的一点空间。文件夹呢,其实也一样会耗费 inode 的。咱们先看一下以后 inode 的占用状况

# 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 命令卡主的首恶。

block 耗费验证

文件夹的名字存在哪儿了呢?嗯,和《新建一个空文件占用多少磁盘空间?》里的文件相似,会耗费一个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    .

貌似,没有什么变动。这是因为

  • 第一、新的空文件不占用 block,所以这里显示的依然是目录占用的 block。
  • 第二、之前文件夹创立时候调配的 4KB 外面闲暇空间还有,够放的下这两个文件项

那么我再多创立一些试试,动用脚本创立 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 字节大一点点,根本对上了。这里咱们也领会到一个事实,文件名越长,在其父目录中耗费的空间也会越大。

本文论断

一个文件夹当然也是要耗费磁盘空间的。

  • 首先要消耗掉一个 inode,我的机器上它是 256 字节
  • 须要耗费其父目录下的一个目录项ext4_dir_entry_2,保留本人 inode 号,目录名。
  • 其上面如果创立文件夹或者文件的话,它就须要在本人的 block 里 ext4_dir_entry_2 数组

目录下的文件 / 子目录越多,目录就须要申请越多的 block。另外 ext4_dir_entry_2 大小不是固定的,文件名 / 子目录名越长,单个目录项耗费的空间也就越大。

对于开篇的问题,我想你当初应该明确为什么了, 问题出在文件夹的 block 身上。
这就是当你的文件夹上面文件特地多,尤其是文件名也比拟长的时候,它会消耗掉十分多的 block。当你遍历文件夹的时候,如果 Page Cache 中没有命中你要拜访的 block,就会穿透到磁盘上进行理论的 IO。在你的角度来看,就是你执行完 ls 后,卡住了。

那么你必定会问,我的确要保留许许多多的文件,我该怎么办? 其实也很简略,多创立一些文件夹就好了,一个目录下别存太多,就不会有这个问题了。工程实际中,个别的做法就是通过一级甚至是二级 hash 把文件散列到多个目录中,把单目录文件数量管制在十万或万以下。

ext 的 bug

貌似明天的实际应该完结了,当初让咱们把刚刚创立的文件全副删掉,再看一下。

# 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 文件系统如同曾经没有这个小问题了,但具体咋解决的,临时没有深入研究,如果你有答案,欢送留言!



开发内功修炼之硬盘篇专辑:

  • 1. 磁盘开篇:扒开机械硬盘坚挺的外衣!
  • 2. 磁盘分区也是隐含了技术技巧的
  • 3. 咱们怎么解决机械硬盘既慢又容易坏的问题?
  • 4. 拆解固态硬盘构造
  • 5. 新建一个空文件占用多少磁盘空间?
  • 6. 只有 1 个字节的文件理论占用多少磁盘空间
  • 7. 文件过多时 ls 命令为什么会卡住?
  • 8. 了解格式化原理
  • 9.read 文件一个字节理论会产生多大的磁盘 IO?
  • 10.write 文件一个字节后何时发动写磁盘 IO?
  • 11. 机械硬盘随机 IO 慢的超乎你的设想
  • 12. 搭载固态硬盘的服务器到底比搭机械硬盘快多少?

我的公众号是「开发内功修炼」,在这里我不是单纯介绍技术实践,也不只介绍实践经验。而是把实践与实际联合起来,用实际加深对实践的了解、用实践进步你的技术实际能力。欢送你来关注我的公众号,也请分享给你的好友~~~

正文完
 0