Почему «ls *» занимает гораздо больше времени, чем «ls»?

У меня есть несколько файлов в каталоге:

$ 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 *) $ 

Когда вы запустите ls без аргументов, он просто откроет каталог, прочитает все содержимое, отсортирует и распечатает.

Когда вы запускаете ls * , сначала оболочка расширяет * , что фактически совпадает с тем, что делали простые ls , строит вектор аргумента со всеми файлами в текущем каталоге и вызывает ls . Затем ls обрабатывает этот вектор аргумента и для каждого аргумента и вызывает access(2) ¹ файла, чтобы проверить его существование. Затем он выведет тот же результат, что и первый (простой) ls . Как обработка оболочки большого вектора аргументов, так и ls 's, скорее всего, потребует значительного выделения памяти из небольших блоков, что может занять некоторое время. Однако, поскольку времени было мало и времени user , но много real времени, большую часть времени было бы потрачено на ожидание диска, вместо того, чтобы использовать CPU для распределения памяти.

Каждому вызову access(2) нужно будет прочитать файл inode для получения информации о разрешении. Это означает, что гораздо больше дисков читается и ищет, чем просто чтение каталога. Я не знаю, насколько дороги эти операции в вашей GPFS, но, как показано в сравнении с ls -l которое имеет аналогичное время выполнения для шаблона, время, необходимое для получения информации об иноземном значении, кажется доминирующим. Если в каждой операции чтения GPFS имеет немного более высокую задержку, чем ваша локальная файловая система, мы ожидаем, что она будет более выраженной в этих случаях.

Разница между корпусом шаблона и ls -l 50% может быть объяснена упорядочением inodes на диске. Если индексы были последовательно размещены в том же порядке, что и имена файлов в каталоге, а ls -l stat (2) отредактировали файлы в порядке каталога перед сортировкой, ls -l , возможно, прочитал большую часть inodes в развертке. С помощью шаблона оболочка будет сортировать имена файлов, прежде чем передавать их в ls , поэтому ls , скорее всего, прочитает inodes в другом порядке, добавив больше движения головки диска.

Следует отметить, что ваш вывод time не будет включать время, затрачиваемое оболочкой для расширения шаблона.

Если вы действительно хотите посмотреть, что происходит, используйте 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.)