Стиснення потокового потоку, яке не переливається на апаратні ресурси?


23

У мене є 200 ГБ вільного місця на диску, 16 ГБ оперативної пам’яті (з яких ~ 1 ГБ займає робочий стіл і ядро) і 6 ГБ свопу.

У мене зовнішній SSD ємністю 240 Гб, 70 ГБ використано 1, а решта - безкоштовна, яку мені потрібно створити для резервного копіювання на диск.

Як правило, я б 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 Тому що тоді я також отримую завантажувальний сектор та розділ swap, щоб я міг відтворити диск саме замість того, щоб мати мільярд набридливих файлів.
кіт

3
Випадкова порада: загляньте lzopзамість gzip; він стискається набагато швидше, лише з дещо нижчим коефіцієнтом стиснення. Я вважаю його ідеальним для зображень на диску, де швидкість стиснення може бути справжнім вузьким місцем.
marcelm

1
"Що робити, коли програма з лівого боку записує більше даних швидше, ніж інша сторона труби може сподіватися обробити її?" Ядро призведе до сну процесу запису, поки в трубці не буде більше місця.
Тавіан Барнс

Відповіді:


16

Технічно вам навіть не потрібно dd:

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

Якщо ви використовуєте dd, ви завжди повинні йти з більше , ніж за замовчуванням , як розміром блоку dd bs=1Mабо страждають від системного виклику пекло ( dd«s по замовчуванням размер_блока становить 512 байт, так як read()S і write()S Те 4096системні виклики на MiB, занадто багато накладних витрат).

gzip -9використовує багато більше процесора і дуже мало, щоб показати його. Якщо gzipви сповільнюєте, знизите рівень стиснення або використовуйте інший (швидший) метод стиснення.

Якщо ви робите резервні копії на основі файлів замість ddзображень, у вас може бути певна логіка, яка вирішує, чи потрібно взагалі стискати чи ні (для різних типів файлів немає сенсу). dar( tarальтернатива`) - один із прикладів, який має можливість це зробити.

Якщо вільний простір ZERO (бо це SSD , який надійно повертає нуль після TRIM і ви запускали fstrimі впали кеша) , ви можете також використовувати ddз conv=sparseпрапором , щоб створити нестислий, зациклення монтируемого, розріджене образ , який використовує нульовий дисковий простір для нульових областей . Потрібно створити резервну копію файлу зображення файловою системою, яка підтримує розріджені файли.

Крім того, для деяких файлових систем існують програми, здатні зображувати лише використовувані області.


1
"Якщо ви використовуєте dd, ви завжди повинні переходити з більшим, ніж за замовчуванням, величиною блоку на зразок dd bs=1M" - Ви можете, але не сподівайтеся занадто багато. На моєму ПК ddбуде робити близько 2 Гб / с з 512-байтовими блоками. Це не буде вузьким місцем; gzipбуде.
marcelm

@marcelm Ми ніколи не знаємо, якою машиною користуються люди. Якщо ви ddзбираєтесь 2 Гб / с з 512-байтовими блоками, я був би здивований, якби він не максимізував один процесорний ядро ​​на 100%. Тепер, якщо у вашій коробці чотирикутник, який просто сидить у режимі очікування, ви можете не помітити різниці. Усі інші все-таки роблять.
frostschutz

9
Зітхнути. Кожного разу, коли ddзгадується блокчейн, люди приїжджають задирки. gzipбути інтенсивним процесором також було частиною моєї відповіді, добре? І вибачте, я не погоджуюся з "незначним". Він може додавати лише 1-2 секунди за концерт gzip -9(але це все одно складає хвилини при обробці сотень концертів), але приймаючи ваші поради, lzop -1це 1s на концерт проти 4s на концерт. Випробуваний на картоплі (одноядерний всервер). Додавання здорового розміру ddнічого не коштує і має нульові мінуси. Не пиляй. Просто зроби це. ymmv
frostschutz

19

ddчитає і записує дані по одному блоку за один раз, і він лише коли-небудь має один блок. Так

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

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

Коли ви потрапляєте в систему gzip, ddпросто сповільнюється, щоб відповідати gzipшвидкості. Його використання пам'яті не збільшується, а також не викликає ядро для зберігання буферів на диск (ядро не знає , як це зробити, за винятком через своп). Поламана труба трапляється лише тоді, коли один з кінців труби відмирає; див. 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читає 1 Мб, а потім видає, write()який сидить там і чекає одну хвилину, поки sleepпрацює. Ось так вирівнюються обидві сторони трубопроводу: блоки ядра записують, якщо процес запису занадто швидкий, і він блокує зчитування, якщо процес читання занадто швидкий.


1
Це досить круто. За яким механізмом можна ddзнати, щоб уповільнити gzipшвидкість? Це автоматично, як ядро, чи він обчислює метадані щодо дескриптора вихідного файлу?
кіт

9
@cat Це автоматично; ddдзвінки write()для передачі даних у трубу. write()насправді передає управління ядру, щоб воно маніпулювало пам'яттю труби. Якщо ядро ​​бачить, що труба заповнена, вона буде чекати ("блокувати"), поки в трубі не буде достатньо місця. Лише тоді write()виклик закінчиться і передасть управління назад на dd, який потім знову запише дані в трубу.
marcelm

9

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


8

Відповідаючи на власне запитання щодо того, як це працює: "що робити, якщо програма з лівого боку записує більше даних швидше, ніж інша сторона труби може сподіватися її обробити?"

Цього не відбувається. У трубі є досить невеликий буфер обмеженого розміру; подивіться, наскільки великий буфер для труб?

Після заповнення буфера труби програма передачі блокується . Коли він робить дзвінок запису, ядро ​​не поверне керування програмі, поки дані не будуть записані в буфер. Це дає час процесора програми читання час, в який спорожняє буфер.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.