为什么“ls *”比“ls”花费的时间长得多?

为什么“ls *”比“ls”花费的时间长得多?

我的目录中有几个文件:

$ ls | wc -l
9376

ls *有人能解释为什么使用and时有这么大的时间差异吗ls

$ time ls > /dev/null
real    0m0.118s
user    0m0.106s
sys     0m0.011s

$ time ls * > /dev/null
real    1m32.602s
user    0m0.233s
sys     0m0.438s

好的,这是一个激烈的示例,并且可能会得到增强,因为该目录位于通用并行文件系统(GPFS)上。但我也可以看到本地文件系统的速度显着下降。

编辑:

$ time ls -l > /dev/null
real    0m58.772s
user    0m0.113s
sys     0m0.452s
$ time ls -l * > /dev/null
real    1m19.538s
user    0m0.252s
sys     0m0.461s

我应该补充一点,在我的示例中没有子目录:

$ diff <(ls) <(ls *)
$

答案1

当您ls不带参数运行时,它只会打开一个目录,读取所有内容,对它们进行排序并打印出来。

当您运行时ls *,首先 shell 展开*,这实际上与简单的操作相同ls,使用当前目录中的所有文件构建一个参数向量并调用ls.ls然后必须处理该参数向量和每个参数,并调用access(2)该文件来检查它是否存在。然后它将打印出与第一个(简单)相同的输出ls。 shell 对大参数向量和ls的处理都可能涉及大量小块的内存分配,这可能需要一些时间。但是,由于时间很少sysuser而且时间很多real,所以大部分时间都花在等待磁盘上,而不是使用CPU进行内存分配。

每次调用access(2)都需要读取文件的索引节点以获取权限信息。这意味着比简单地读取目录要进行更多的磁盘读取和查找。我不知道这些操作在您的 GPFS 上有多昂贵,但正如您所显示的ls -l与通配符情况具有相似运行时间的比较一样,检索 inode 信息所需的时间似乎占主导地位。如果 GPFS 在每次读取操作上的延迟比本地文件系统稍高,我们预计在这些情况下延迟会更加明显。

通配符大小写和ls -l50% 之间的差异可以通过磁盘上 inode 的排序来解释。如果索引节点按照与目录中文件名相同的顺序连续排列,并ls -l在排序之前按目录顺序对文件进行 stat(2) 处理,ls -l则可能会一次扫描读取大部分索引节点。使用通配符,shell 在将文件名传递给 之前会对文件名进行排序ls,因此ls可能会以不同的顺序读取 inode,从而增加更多的磁盘头移动。

应该注意的是,您的time输出将不包括 shell 扩展通配符所花费的时间。

如果您确实想查看发生了什么,请使用strace(1)

strace -o /tmp/ls-star.trace ls *
strace -o /tmp/ls-l-star.trace ls -l *

并查看每种情况下正在执行哪些系统调用。

¹ 我不知道是否access(2)实际使用过,或者是其他东西,例如stat(2).但两者都可能需要索引节点查找(我不确定是否access(file, 0)会绕过索引节点查找。)

相关内容