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


28

我在目录中有几个文件:

$ ls | wc -l
9376

谁能解释为什么使用ls *和会有如此大的时差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 *)
$

Answers:


47

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

运行时ls *,首先外壳扩展*(实际上与简单扩展一样),ls并使用当前目录中的所有文件构建一个参数向量并调用lsls然后必须处理该参数向量并针对每个参数进行处理,并调用access(2)“文件”以检查其存在。然后,它将输出与第一个(简单)相同的输出ls。Shell对大自变量向量的处理和ls对S 的处理都可能会涉及大量的小块内存分配,这可能需要一些时间。但是,由于时间sysuser精力很少,而时间却很多real,因此大部分时间将花费在等待磁盘上,而不是使用CPU进行内存分配。

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

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

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

如果您真的想看看发生了什么,请使用strace(1)

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

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

¹我不知道是否access(2)实际使用过,或诸如之类的其他东西stat(2)。但是两者都可能需要inode查找(我不确定是否access(file, 0)会绕过inode查找。)


2
好的答案,我正要发布一个类似的答案:)但是,是的,这是正确的,这全都是关于循环效率的问题,ls它可以询问文件系统“ inode的子代是什么pwd”在哪里ls *它必须询问“ inode的子代(以及文件是什么)a”,后跟b,c,d等。一个查询与很多查询。
新泽西州

到目前为止,@ NJ 一个查询与许多查询是一个很好的总结。@camh:感谢您的详细回答。我也发布了输出ls -l(仍比少30秒ls *
塞巴斯蒂安

@Sebastian作为CAMH表示,ls -l将采取长于ls因为它有stat(2)每个文件,以获取有关时间戳/所有者信息/权限等信息
新泽西州

6
别忘了,*遍历当前目录中所有不以句点开头的条目-包括子目录的名称。然后将其ls编辑。
2011年

@camh:我测试多一点(见我的编辑),并发现:ls< ls -l< ls -l *< ls *(我一直跑了三次)。你的解释,我不明白为什么ls -l *比快ls *
塞巴斯蒂安
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.