У меня есть каталог с более чем 400 ГиБ данных в нем. Я хотел проверить, что все файлы могут быть прочитаны без ошибок, поэтому я подумал о том, как tar
это сделать /dev/null
. Но вместо этого я вижу следующее поведение:
$ time tar cf /dev/null .
real 0m4.387s
user 0m3.462s
sys 0m0.185s
$ time tar cf - . > /dev/null
real 0m3.130s
user 0m3.091s
sys 0m0.035s
$ time tar cf - . | cat > /dev/null
^C
real 10m32.985s
user 0m1.942s
sys 0m33.764s
Третья команда, приведенная выше, была принудительно остановлена Ctrl+ Cпосле достаточно долгого выполнения. Более того, в то время как первые две команды работали, индикатор активности устройства хранения .
почти всегда был бездействующим. С третьей командой индикатор постоянно горит, что означает чрезвычайную занятость.
Таким образом, кажется, что, когда tar
он может определить, что его выходной файл есть /dev/null
, то есть, когда /dev/null
он непосредственно открывается, чтобы получить дескриптор файла, в который tar
записывается, тело файла оказывается пропущенным. (При добавлении v
опции tar
все файлы в каталоге будут напечатаны tar
красным цветом.)
Интересно, а почему это так? Это какая-то оптимизация? Если да, то зачем tar
вообще делать такую сомнительную оптимизацию для такого особого случая?
Я использую GNU tar 1.26 с glibc 2.27 в Linux 4.14.105 amd64.
find . -type f -exec shasum -a256 -b '{}' +
. Мало того, что он фактически читает и проверяет все данные, но если вы сохраняете вывод, вы можете запустить его позже, чтобы убедиться, что содержимое файлов не изменилось.pv
:tar -cf - | pv >/dev/null
. Это обходит проблему и дает вам информацию о прогрессе (различныеpv
варианты)gtar -cf /dev/zero ...
чтобы получить то, что вам нравится.Ответы:
Это является документированной оптимизация :
источник
info tar
вместо этого ...info
в браузере или в виде HTML.Это может случиться с различными программами, например, у меня такое поведение было когда-то при использовании
cp file /dev/null
; вместо оценки скорости чтения с моего диска команда вернулась через несколько миллисекунд.Насколько я помню, это было в Solaris или AIX, но этот принцип применим ко всем видам Unix-Y систем.
В старые времена, когда программа копировала файл куда-то, она чередовалась между
read
вызовами, которые получают данные с диска (или с тем, на что ссылается дескриптор файла) в память (с гарантией, что все будет там, когдаread
вернется) иwrite
вызовами (которые берут кусок памяти и отправляют контент по назначению).Тем не менее, есть как минимум два новых способа достижения того же:
В Linux есть системные вызовы
copy_file_range
(вообще не переносимые на другие юниксы) иsendfile
(несколько переносимые; изначально предназначенные для отправки файла в сеть, но теперь они могут использовать любой пункт назначения). Они предназначены для оптимизации переводов; если программа использует один из них, вполне возможно, что ядро распознает цель/dev/null
и превращает системный вызов в неоперативныйПрограммы могут использовать
mmap
для получения содержимого файла вместо этогоread
, это в основном означает «убедиться, что данные есть, когда я пытаюсь получить доступ к этой части памяти» вместо «убедиться, что данные есть, когда системный вызов вернется». Таким образом, программа может получитьmmap
исходный файл, а затем вызватьwrite
этот фрагмент отображенной памяти. Однако, поскольку запись/dev/null
не требует доступа к записанным данным, условие «убедитесь, что оно есть» никогда не запускается, в результате чего файл также не читается.Не уверен , что если гну деготь использует любой, и которые из этих двух механизмов , когда он обнаруживает , что это пишет
/dev/null
, но они почему любая программа, когда используется для проверки чтения , скорости , должна работать с| cat > /dev/null
вместо> /dev/null
- и почему| cat > /dev/null
должен следует избегать во всех остальных случаях.источник
tar
информационной странице GNU (см. Другой ответ) состоит в том, что у него есть специальный режим для этого, который, по-видимому, просто статистика файлов без их открытия. На самом деле я только что проверилtar cf /dev/null foo*
пару файлов, и да, простоnewfstatat(..., AT_SYMLINK_NOFOLLOW)
системные вызовы, даже не те,open()
которые могут обновить atime. Но +1 для описания механизмов, где это может произойти, без необходимости специально обнаруживать это.splice(2)
о Linux. На самом деле, заменаcat > /dev/null
наpv -q > /dev/null
(которая используетсяsplice()
в Linux), скорее всего, уменьшит накладные расходы. Илиdd bs=65536 skip=9999999999 2> /dev/null
, илиwc -c > /dev/null
илиtail -c1 > /dev/null
...