Kompresja strumienia w locie, która nie rozlewa się na zasoby sprzętowe?


23

Mam 200 GB wolnego miejsca na dysku, 16 GB pamięci RAM (z czego ~ 1 GB zajmuje komputer i jądro) oraz 6 GB wymiany.

Mam zewnętrzny dysk SSD o pojemności 240 GB, z 70 GB wykorzystałem 1, a resztę bezpłatnie, i muszę wykonać kopię zapasową na dysku.

Zwykle dd if=/dev/sdb of=Desktop/disk.imgnajpierw najpierw dysk, a następnie kompresję, ale najpierw nie jest możliwe zrobienie obrazu, ponieważ wymagałoby to znacznie więcej miejsca na dysku niż ja, nawet jeśli krok kompresji spowoduje, że wolne miejsce zostanie zmiażdżone, więc końcowe archiwum można łatwo zmieścić na moim dysku.

dddomyślnie zapisuje do STDOUT i gzipmoże czytać ze STDIN, więc teoretycznie mogę pisać dd if=/dev/sdb | gzip -9 -, ale gzipczytanie bajtów zajmuje znacznie więcej czasu niż ddich wytworzenie.

Od man pipe:

Dane zapisywane na końcu zapisu potoku są buforowane przez jądro, dopóki nie zostaną odczytane z końca odczytu potoku.

Wizualizuję, |że jest jak prawdziwa potok - jedna aplikacja wpycha dane, a druga pobiera dane z kolejki potoku tak szybko, jak to możliwe.

Co się stanie, gdy program po lewej stronie będzie zapisywał więcej danych szybciej niż druga strona potoku może mieć nadzieję na ich przetworzenie? Czy spowoduje to ekstremalne użycie pamięci lub zamiany, czy też jądro spróbuje utworzyć FIFO na dysku, wypełniając w ten sposób dysk? A może po prostu zawiedzie, SIGPIPE Broken pipejeśli bufor jest zbyt duży?

Zasadniczo sprowadza się to do dwóch pytań:

  1. Jakie są implikacje i wyniki wrzucania większej ilości danych do potoku niż odczytywane jednocześnie?
  2. Jaki jest niezawodny sposób kompresji strumienia danych na dysk bez umieszczania całego nieskompresowanego strumienia danych na dysku?

Uwaga 1: Nie mogę po prostu skopiować dokładnie pierwszych 70 używanych GB i oczekiwać, że dostanę działający system lub system plików, z powodu fragmentacji i innych rzeczy, które będą wymagały nienaruszonej pełnej zawartości.


Dlaczego miałbyś tworzyć kopię zapasową takiego systemu plików zamiast tylko katalogów użytkowników i być może listy zainstalowanych niestandardowych programów?
jamesqf

5
@jamesqf Np. ponieważ o wiele łatwiej jest przywrócić ...
deviantfan

4
@jamesqf Ponieważ wtedy otrzymuję również sektor rozruchowy i partycję wymiany, dzięki czemu mogę dokładnie odtworzyć dysk zamiast mieć miliard irytujących plików.
kot

3
Losowa wskazówka: spójrz lzopzamiast gzip; kompresuje się znacznie szybciej przy jedynie nieco niższym stopniu kompresji. Uważam, że idealnie nadaje się do obrazów dysków, na których szybkość kompresji może być prawdziwym wąskim gardłem.
marcelm

1
„Co się stanie, gdy program po lewej stronie będzie zapisywał więcej danych szybciej niż druga strona potoku może liczyć na ich przetworzenie?” Jądro spowoduje, że proces pisania będzie spał, dopóki w rurze nie będzie więcej miejsca.
Tavian Barnes,

Odpowiedzi:


16

Technicznie nie potrzebujesz nawet dd:

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

Jeśli używasz dd, powinieneś zawsze korzystać z większego niż domyślny rozmiaru bloków, takiego jak dd bs=1Mlub cierpieć z powodu piekła systemowego ( dddomyślny rozmiar bloku to 512 bajtów, ponieważ jest to read()s i write()s 4096wywołań na MiB, zbyt duży narzut).

gzip -9używa dużo DUŻO procesora, a do tego niewiele. Jeśli gzipspowalnia Cię, obniż poziom kompresji lub użyj innej (szybszej) metody kompresji.

Jeśli tworzysz kopie zapasowe oparte na plikach zamiast ddobrazów, możesz mieć logikę, która decyduje, czy w ogóle kompresować, czy nie (nie ma sensu robić tego dla różnych typów plików). dar( taralternatywny`) to jeden przykład, który ma takie opcje.

Jeśli ilość wolnego miejsca jest równa zero (bo to SSD, które niezawodnie zwraca zero po TRIM i pobiegł fstrimi spadł bufory) można również korzystać ddz conv=sparseflagą na tworzenie nieskompresowanego, Pętla scianie, rzadki obraz zastosowania zerowej przestrzeni dyskowej dla obszarów zerowych . Wymaga, aby plik obrazu był wspierany przez system plików, który obsługuje rzadkie pliki.

Alternatywnie dla niektórych systemów plików istnieją programy zdolne do obrazowania tylko używanych obszarów.


1
„Jeśli używasz dd, zawsze powinieneś mieć większy niż domyślny rozmiar bloku jak dd bs=1M - Możesz, ale nie oczekuj zbyt wiele. Na moim komputerze ddwystarczy około 2 GB / s przy 512-bajtowych blokach. To nie będzie wąskie gardło; gzipbędzie.
marcelm

@marcelm Nigdy nie wiemy, jakiej maszyny używają ludzie. Jeśli masz dd2 GB / s z 512-bajtowymi blokami, byłbym zaskoczony, gdyby nie zmaksymalizował jednego rdzenia procesora w 100%. Teraz, jeśli twoje pudełko jest quadcore, które i tak pozostaje bezczynne, możesz nie zauważyć różnicy. Jednak wszyscy inni to robią.
frostschutz

9
Westchnienie. Za każdym razem, gdy ddpojawia się wzmianka o wielkości bloków, ludzie przychodzą do gry. gzipintensywność procesorów również była częścią mojej odpowiedzi, dobrze? I przepraszam, nie zgadzam się z „nieistotnym”. Może dodać tylko 1-2 sekundy na koncert gzip -9(ale to wciąż trwa kilka minut podczas przetwarzania setek koncertów), ale biorąc pod uwagę twoją radę, lzop -1to 1s na koncert vs. 4s na koncert. Testowany na ziemniaku (vserver z jednym rdzeniem). Dodanie rozsądnego rozmiaru bloku ddnic nie kosztuje i ma zero wad. Nie chwytaj się. Po prostu to zrób. ymmv
frostschutz

19

ddodczytuje i zapisuje dane jeden blok naraz, i zawsze ma tylko jeden blok zaległy. Więc

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

pokazuje, że ddzużywa około 1 MB pamięci. Możesz bawić się wielkością bloku i upuszczać valgrind, aby zobaczyć wpływ na ddszybkość.

Kiedy wpadasz gzip, ddpo prostu zwalnia, aby dopasować gzipprędkość. Jej użycie pamięci nie zwiększa, ani nie powodują jądro do przechowywania buforów na dysk (jądro nie wie, jak to zrobić, z wyjątkiem poprzez zamiany). Zepsuta rura zdarza się tylko wtedy, gdy umiera jeden z końców rury; zobacz signal(7)i write(2)po szczegóły.

A zatem

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

to bezpieczny sposób na robienie tego, czego szukasz.

Podczas pipowania proces zapisu zostaje zablokowany przez jądro, jeśli proces odczytu nie nadąża. Możesz to zobaczyć, uruchamiając

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

Zobaczysz, że ddodczytuje 1 MB, a następnie wydaje, write()który siedzi tam i czeka przez minutę podczas sleepbiegu. W ten sposób równoważą się obie strony potoku: bloki jądra zapisują, jeśli proces zapisu jest zbyt szybki, a blokują odczyt, jeśli proces odczytu jest zbyt szybki.


1
To fajnie. Który mechanizm ddwie, aby spowolnić, aby dopasować gzipprędkość? Jest to automatyczne, podobnie jak jądro, czy oblicza na podstawie metadanych dotyczących deskryptora pliku wyjściowego?
kot

9
@cat To automatyczne; ddwywołania w write()celu umieszczenia danych w potoku. write()faktycznie przekazuje kontrolę do jądra, aby mógł manipulować pamięcią potoku. Jeśli jądro zobaczy, że potok jest pełny, zaczeka („blok”), aż potok będzie miał wystarczająco dużo miejsca. Tylko wtedy write()połączenie zakończy się i przekaże kontrolę z powrotem do dd, który następnie ponownie zapisze dane do potoku.
marcelm

9

Nie ma żadnych negatywnych konsekwencji poza wydajnością: potok ma bufor, który zwykle wynosi 64 KB, a następnie zapis do potoku będzie po prostu blokowany, dopóki nie gzipodczyta więcej danych.


8

Odpowiadając na rzeczywiste pytanie, jak to działa: „co, jeśli program po lewej stronie zapisuje więcej danych szybciej niż druga strona potoku może liczyć na ich przetworzenie?”

To się nie zdarza. W rurze znajduje się dość mały bufor o ograniczonym rozmiarze; zobacz Jak duży jest bufor rurowy?

Po zapełnieniu bufora potoku program wysyłający blokuje się . Kiedy wykonuje polecenie zapisu, jądro nie zwróci kontroli do programu, dopóki dane nie zostaną zapisane w buforze. Daje to czas procesora programu czytającego na opróżnienie bufora.


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.