Сжатие потока «на лету», которое не распространяется на аппаратные ресурсы?

23

У меня есть 200 ГБ свободного дискового пространства, 16 ГБ ОЗУ (из которых ~ 1 ГБ занято рабочим столом и ядром) и 6 ГБ подкачки.

У меня есть внешний SSD на 240 ГБ, из которых 1 используется 70 ГБ, а остальное свободно, и мне нужно сделать резервную копию на моем диске.

Обычно я dd if=/dev/sdb of=Desktop/disk.imgсначала выполняю диск, а затем сжимаю его, но создание образа сначала не вариант, так как для этого потребуется гораздо больше места на диске, чем у меня, даже несмотря на то, что этап сжатия приведет к сжатию свободного пространства, поэтому Конечный архив легко помещается на моем диске.

ddзаписывает в STDOUT по умолчанию и gzipможет читать из STDIN, поэтому теоретически я могу писать dd if=/dev/sdb | gzip -9 -, но gzipчтение байтов занимает значительно больше времени, чем ddможет их произвести.

От man pipe:

Данные, записанные в конец записи канала, буферизуются ядром до тех пор, пока они не будут прочитаны из конца чтения канала.

Я представляю себя |как настоящий канал - одно приложение помещает данные, а другое - как можно быстрее выводит данные из очереди канала.

Что, когда программа на левой стороне записывает больше данных быстрее, чем другая сторона канала, может рассчитывать на их обработку? Будет ли это вызывать чрезмерное использование памяти или подкачки, или ядро ​​попытается создать FIFO на диске, заполнив тем самым диск? Или он просто потерпит неудачу, SIGPIPE Broken pipeесли буфер слишком велик?

По сути, это сводится к двум вопросам:

  1. Каковы последствия и результаты добавления большего количества данных в канал, чем считывается за раз?
  2. Какой надежный способ сжать поток данных на диск, не помещая весь несжатый поток данных на диск?

Примечание 1: я не могу просто скопировать точно первые 70 использованных ГБ и ожидать получить работающую систему или файловую систему из-за фрагментации и других вещей, которые потребуют целостности всего содержимого.

Кот
источник
Зачем вам делать резервные копии всей файловой системы, а не просто пользовательских каталогов и, возможно, списка установленных нестандартных программ?
jamesqf
5
@jamesqf Например. потому что намного легче восстановить ...
deviantfan
4
@jamesqf Потому что тогда у меня также есть загрузочный сектор и раздел подкачки, так что я могу воссоздать диск точно вместо того, чтобы иметь миллиард раздражающих файлов.
кошка
3
Случайный совет: посмотрите lzopвместо gzip; он сжимается намного быстрее, с чуть более низкой степенью сжатия. Я нахожу это идеальным для образов дисков, где скорость сжатия может быть реальным узким местом.
Марсель
1
«Что, когда программа на левой стороне записывает больше данных быстрее, чем другая сторона канала, может рассчитывать на их обработку?» Ядро заставит процесс записи спать, пока в канале не появится больше места.
Тавиан Барнс

Ответы:

16

Технически вам даже не нужно dd:

gzip < /dev/drive > drive.img.gz

Если вы используете dd, вы всегда должны идти с большим, чем по умолчанию, размером блока, таким как адский вызов системного вызова dd bs=1Mили страдать от него (по ddумолчанию размер блока составляет 512 байт, так как это read()s и write()s, то есть 4096syscalls per MiB, слишком много накладных расходов).

gzip -9использует гораздо больше процессора с очень мало, чтобы показать это. Если gzipэто замедляет работу, снизьте уровень сжатия или используйте другой (более быстрый) метод сжатия.

Если вы делаете резервные копии на основе файлов, а не ddизображений, у вас может быть логика, которая решает, сжимать ли вообще или нет (делать это бессмысленно для разных типов файлов). dar( tarальтернатива`) - один из примеров, у которого есть варианты сделать это.

Если у вас свободное пространство ZERO (потому что это твердотельный накопитель, который надежно возвращает ноль после TRIM, и вы запустили fstrimи сбросили кэши), вы также можете использовать ddс conv=sparseфлагом, чтобы создать несжатый, монтируемый на петлю, разреженный образ, который использует нулевое дисковое пространство для нулевых областей. , Требует, чтобы файл образа был поддержан файловой системой, которая поддерживает разреженные файлы.

В качестве альтернативы для некоторых файловых систем существуют программы, способные отображать только используемые области.

frostschutz
источник
1
«Если вы используете dd, вы всегда должны использовать больший размер блока, чем по умолчанию dd bs=1M», - можете, но не ожидайте слишком многого. На моем ПК ddбудет работать около 2 ГБ / с с 512-байтовыми блоками. Это не будет узким местом; gzipбудет.
marcelm
@marcelm Мы никогда не знаем, какие машины используют люди. Если вы используете dd2 ГБ / с с 512-байтовыми блоками, я был бы удивлен, если бы в этом процессе не использовалось 100% одного ядра процессора. Теперь, если ваша коробка - это четырехъядерный процессор, который все равно бездействует, вы можете не заметить разницы. Все остальные все еще делают, хотя.
frostschutz
9
Вздох. Каждый раз, ddкогда упоминается размер блока, люди придираются. gzipинтенсивность работы процессора также была частью моего ответа, хорошо? И извините, я не согласен с "незначительным". Он может добавить только 1-2 с на концерт gzip -9(но это все равно составляет минуты при обработке сотен гигов), но прислушивайтесь к совету lzop -1: 1 с на концерт против 4 с на концерт. Испытано на картофеле (одноядерный всервер). Добавление нормального размера блока ddничего не стоит и имеет ноль недостатков. Не придирайся. Просто сделай это. ymmv
frostschutz
19

ddчитает и записывает данные по одному блоку за раз, и у него только один ожидающий блок. Так

valgrind dd if=/dev/zero status=progress of=/dev/null bs=1M

показывает, что ddиспользует примерно 1 МБ памяти. Вы можете поиграть с размером блока и сбросить valgrind, чтобы увидеть влияние на ddскорость.

Когда вы переходите на канал gzip, ddпросто замедляетесь, чтобы соответствовать gzipскорости. Его использование памяти не увеличивается и не заставляет ядро ​​хранить буферы на диске (ядро не знает, как это сделать, кроме как через swap). Разорванная труба случается только тогда, когда один из концов трубы умирает; смотрите signal(7)и write(2)для деталей.

таким образом

dd if=... iconv=fullblock bs=1M | gzip -9 > ...

это безопасный способ сделать то, что вы после.

При передаче по трубопроводу процесс записи блокируется ядром, если процесс чтения не идет в ногу. Вы можете увидеть это, запустив

strace dd if=/dev/zero bs=1M | (sleep 60; cat > /dev/null)

Вы увидите, что ddчитает 1MB, а затем выдает, write()который сидит в ожидании одной минуты, пока sleepработает. Вот как уравновешиваются обе стороны канала: ядро ​​блокирует запись, если процесс записи идет слишком быстро, и блокирует чтение, если процесс чтения слишком быстрый.

Стивен Китт
источник
1
Это круто. По какому механизму ddизвестно, чтобы замедлиться, чтобы соответствовать gzipскорости? Он автоматический, как ядро, или он рассчитывает по метаданным о своем дескрипторе выходного файла?
кошка
9
@cat Это автоматически; ddпризывает write()положить данные в трубу. write()фактически передает управление ядру, чтобы оно могло манипулировать памятью канала. Если ядро ​​увидит, что канал заполнен, оно будет ждать («заблокировать»), пока каналу не будет достаточно места. Только после этого write()вызов завершится и вернет управление обратно dd, после чего данные снова будут записаны в канал.
marcelm
9

Нет никаких отрицательных последствий, кроме производительности: канал имеет буфер, который обычно составляет 64 КБ, и после этого запись в канал просто блокируется, пока не будет gzipпрочитано еще несколько данных.

Ульрих Шварц
источник
8

Отвечая на реальный вопрос о том, как это работает: «что если программа на левой стороне записывает больше данных быстрее, чем другая сторона канала может рассчитывать на их обработку?»

Этого не происходит В канале имеется довольно маленький буфер ограниченного размера; Посмотрите, насколько большой буфер трубы?

Когда буфер канала заполнен, программа-отправитель блокируется . Когда он выполняет вызов записи, ядро ​​не вернет управление программе, пока данные не будут записаны в буфер. Это дает процессору ЦП время, в течение которого необходимо очистить буфер.

pjc50
источник