GNU/Linux >> Znalost Linux >  >> Linux

Kdy je Dd vhodné pro kopírování dat? (neboli, kdy jsou čtení() a zápis() částečné)?

Krátká verze: Za jakých okolností je dd bezpečné použití pro kopírování dat, bezpečné znamená, že neexistuje žádné riziko poškození v důsledku částečného čtení nebo zápisu?

Dlouhá verze – preambule: dd se často používá ke kopírování dat, zejména ze zařízení nebo do zařízení (příklad). Někdy se mu připisují mystické vlastnosti možnosti přístupu k zařízením na nižší úrovni než jiným nástrojům (i když ve skutečnosti je to soubor zařízení, kdo dělá kouzlo) – přesto dd if=/dev/sda je to samé jako cat /dev/sda . dd je někdy považováno za rychlejší, ale cat může to překonat v praxi. Nicméně dd má jedinečné vlastnosti, díky kterým je někdy skutečně užitečný.

Problém: dd if=foo of=bar není ve skutečnosti totéž jako cat <foo >bar . Na většině unices¹, dd provede jediné volání read() . (Považuji POSIX za nejasný v tom, co představuje „čtení vstupního bloku“ v dd .) Pokud read() vrátí částečný výsledek (což je podle POSIX a dalších referenčních dokumentů povoleno, pokud implementační dokumentace neříká jinak), částečný blok se zkopíruje. Přesně stejný problém existuje pro write() .

Postřehy :V praxi jsem zjistil, že dd umí si poradit s blokovými zařízeními a běžnými soubory, ale to může být jen tím, že jsem to moc necvičil. Pokud jde o potrubí, není těžké zadat dd na vině; zkuste například tento kód:

yes | dd of=out bs=1024k count=10

a zkontrolujte velikost out (pravděpodobně bude mít mnohem méně než 10 MB).

Otázka :Za jakých okolností je dd bezpečné použití pro kopírování dat? Jinými slovy, jaké podmínky ohledně velikostí bloků, implementace, typů souborů atd. mohou zajistit, že dd zkopíruje všechna data?

(GNU dd má fullblock flag, aby mu řekl, aby zavolal read() nebo write() ve smyčce tak, aby se přenesl celý blok. Takže dd iflag=fullblock je vždy v bezpečí. Moje otázka se týká případu, kdy tyto příznaky (které v jiných implementacích neexistují) nejsou použity.)

¹
Zkontroloval jsem OpenBSD, GNU coreutils a BusyBox.

Přijatá odpověď:

Ze specifikace:

  • Pokud je bs= expr je zadán operand a žádné jiné konverze než sync , noerror nebo notrunc jsou požadovány, data vrácená z každého vstupního bloku se zapíší jako samostatný výstupní blok; pokud read() vrátí méně než celý blok a sync převod není specifikován, výsledný výstupní blok bude mít stejnou velikost jako vstupní blok.

Takže to je pravděpodobně to, co způsobuje váš zmatek. Ano, protože dd je navrženo pro blokování ve výchozím nastavení částečné read() s bude mapováno 1:1 na částečné write() s nebo jinak sync d ven na ocasní vycpávce NUL nebo mezerník na bs= velikost při conv=sync je zadáno.

To znamená, že dd je bezpečné použití pro kopírování dat (bez rizika poškození v důsledku částečného čtení nebo zápisu) v každém případě kromě toho, ve kterém je libovolně omezena count= argument, protože jinak dd bude šťastně write() jeho výstup v blocích stejné velikosti jako bloky, ve kterých byl jeho vstup read() dokud read() je úplně přes to. A i toto upozornění je pouze pravdivé když bs= je zadáno nebo obs= není specifikováno, protože hned další věta ve specifikaci uvádí:

  • Pokud je bs= expr není zadán operand nebo se jedná o jinou konverzi než sync , noerror nebo notrunc je požadováno, vstup bude zpracován a shromážděn do výstupních bloků plné velikosti dokud není dosaženo konce vstupu.
Související:Debian – opakovaná ztráta bezdrátového připojení?

Bez ibs= a/nebo obs= argumenty na tom nezáleží – protože ibs a obs oba mají ve výchozím nastavení stejnou velikost. Nicméně můžete být explicitní o ukládání do vyrovnávací paměti zadáním různých velikostí pro buď a ne zadáním bs= (protože má přednost) .

Pokud například uděláte:

IN| dd ibs=1| OUT

…potom POSIX dd bude write() v blocích po 512 bytech shromažďováním každý jednotlivě read() byte do jednoho výstupního bloku.

Jinak, pokud tak učiníte…

IN| dd obs=1kx1k| OUT

…a POSIX dd bude read() maximálně 512 bajtů najednou, ale write() každý megabajtový výstupní blok (jádro umožňuje a možná s výjimkou posledního – protože to je EOF) v plném rozsahu shromažďováním vstupu do výstupních bloků plné velikosti .

Také však ze specifikace:

  • count=n
    • Kopírovat pouze n vstupní bloky.

count= mapuje na i?bs= bloky, a tak zvládnout libovolný limit na count= přenosně budete potřebovat dva dd s. Nejpraktičtější způsob, jak to udělat se dvěma dd s je propojením výstupu jednoho do vstupu druhého, což nás jistě staví do oblasti čtení/zápisu speciálního souboru bez ohledu na původní typ vstupu.

IPC roura znamená, že při zadání [io]bs= tvrdí, že k tomu, abyste tak učinili bezpečně, musíte tyto hodnoty udržovat v rámci systému definovaného PIPE_BUF omezit. POSIX uvádí, že jádro systému musí zaručit pouze atomické read() s a write() s v mezích PIPE_BUF jak je definováno v limits.h . POSIX zaručuje, že PIPE_BUF být nejméně

  • {_POSIX_PIPE_BUF}
    • Maximální počet bajtů, u kterých je zaručeno, že jsou atomické při zápisu do kanálu.
    • Hodnota:512

(což je také výchozí dd velikost bloku i/o) , ale skutečná hodnota je obvykle minimálně 4k. Na aktuálním linuxovém systému je to standardně 64k.

Když tedy nastavíte dd procesy, měli byste to udělat na blokovém faktoru na základě tří hodnot:

  1. bs =( obs =PIPE_BUF nebo menší )
  2. n =celkový požadovaný počet přečtených bajtů
  3. počet =n / bs

Jako:

yes | dd obs=1k | dd bs=1k count=10k of=/dev/null
10240+0 records in
10240+0 records out
10485760 bytes (10 MB) copied, 0.1143 s, 91.7 MB/s

Musíte synchronizovat i/o s dd pro zpracování nehledatelných vstupů. Jinými slovy, udělejte pipe-buffery explicitní a přestanou být problémem. To je to, co dd je pro. Neznámá veličina je zde yes „velikost vyrovnávací paměti – ale pokud ji zablokujete pro známé množství s jiným dd pak trochu informované násobení může způsobit dd bezpečné použití pro kopírování dat (bez rizika poškození v důsledku částečného čtení nebo zápisu) i při libovolném omezení vstupu pomocí count= w/ libovolný libovolný typ vstupu na jakémkoli systému POSIX a bez vynechání jediného bajtu.

Zde je úryvek ze specifikace POSIX:

  • ibs= expr
    • Uveďte velikost vstupního bloku v bajtech pomocí expr (výchozí hodnota je 512) .
  • obs= expr
    • Uveďte velikost výstupního bloku v bajtech pomocí expr (výchozí hodnota je 512) .
  • bs= expr
    • Nastavte velikosti vstupních i výstupních bloků na expr bajtů, nahrazujících ibs= a obs= . Pokud neproběhne jiná konverze než sync , noerror a notrunc je zadáno, každý vstupní blok se zkopíruje na výstup jako jeden blok bez agregace krátkých bloků.
Související:Jak zařídit, aby se na terminálu zobrazovalo [chráněno e-mailem] tučným písmem?

Některé z toho také najdete lépe vysvětlené zde.


Linux
  1. Čtěte a zapisujte data odkudkoli s přesměrováním v terminálu Linux

  2. Použijte příkaz Netcat ke čtení a zápisu dat v síti na Ubuntu 20.04

  3. Změnit oprávnění na čtení, zápis a spouštění?

  1. Příkaz bsdtar – Čtení a zápis páskových archivních souborů

  2. Kdy mám použít TCP_NODELAY a kdy TCP_CORK?

  3. Jak otevřít, číst a zapisovat ze sériového portu v C?

  1. V Bash, kdy alias, kdy skript a kdy napsat funkci?

  2. Kdy zkontrolovat EINTR a opakovat volání funkce?

  3. copy_to_user() a copy_from_user() pro základní datový typ