Коли DD підходить для копіювання даних? (або, коли читаються () і записуються () частково)


60

Коротка версія: За яких обставин ddбезпечно використовувати для копіювання даних, що означає, що немає ризику корупції через часткове читання чи запис?

Довга версія - преамбула: dd часто використовується для копіювання даних, особливо з або на пристрій ( приклад ). Іноді приписують містичні властивості можливості доступу до пристроїв нижчого рівня, ніж інші інструменти (адже насправді саме цей файл пристрою робить магію) - все ж dd if=/dev/sdaце те саме, що cat /dev/sda. ddіноді вважається швидшим, але catна практиці це може обіграти . Тим не менш, він ddмає унікальні властивості, які роблять його часом справді корисним .

Проблема: dd if=foo of=bar насправді не те саме, що cat <foo >bar. У більшості Unice¹ ddздійснює один дзвінок на read(). (Я знаходжу POSIX нечітким у тому, що являє собою "зчитування блоку введення" dd.) Якщо read()повертається частковий результат (який, згідно з POSIX та іншими довідковими документами, дозволено, якщо в документації на реалізацію не сказано інше), частковий блок копіюється. Точно така ж проблема існує і для write().

Зауваження : На практиці я виявив, що ddможе впоратися з блоковими пристроями та звичайними файлами, але це може бути просто те, що я цього мало використовував. Якщо мова заходить про труби, це не важко поставити ddна розлогу; наприклад, спробуйте цей код :

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

і перевірте розмір outфайлу (ймовірно, це буде значно менше 10 Мб).

Питання : За яких обставин ddбезпечно використовувати для копіювання даних? Іншими словами, які умови щодо розмірів блоків, реалізації, типів файлів тощо можуть забезпечити ddкопіювання всіх даних?

( GNU dd має fullblockпрапор, щоб вказати йому дзвонити read()або write()в циклі, щоб перенести повний блок. Тому dd iflag=fullblockзавжди безпечно. Питання про те, коли ці прапори (яких немає в інших реалізаціях) не використовуються .)

¹ Я перевірив OpenBSD, GNU coreutils та BusyBox.


Я ніколи не бачив жодної системи Unixy, яка справді могла б прочитати кілька MiB в одному читанні (2) ...
vonbrand

3
Під час використання count, iflag=fullblockобов'язкове (або, альтернативно, iflag=count_bytes). Немає oflag=fullblock.
frostschutz

Відповіді:


10

З специфікації :

  • Якщо bs=exprоперанд вказаний і немає перетворень, крім sync, noerrorабо notruncзапитуються, дані, повернені з кожного вхідного блоку, записуються як окремий вихідний блок; якщо read()повернення менше повного блоку і syncперетворення не вказано, отриманий вихідний блок повинен бути того ж розміру, що і блок введення.

Отож, це, мабуть, викликає вашу плутанину. Та тому, що ddце призначене для блокування, за замовчуванням часткових read()сек буде відображатися 1: 1 частковим write()с, або syncd на хвіст відступ NUL або космічних символи в bs=розмір , коли conv=syncвказано.

Це означає , що ddце безпечно використовувати для копіювання даних (ж / без ризику корупції з - за часткове читання або запис) , в кожному разі , крім одного , в якому вона довільно обмежена по count=аргументу, оскільки в іншому випадку ddбуде щасливо write()його вихід в однаковому розмірі блоків до тих, у яких його вклад був, read()поки він read()повністю не пройшов через нього. І навіть це застереження тільки справедливо , якщо bs=вказано або obs=в НЕ вказано, як таку пропозицію в специфікації говориться:

  • Якщо bs=exprоперанд не вказаний, або перетворення, крім sync, noerrorабо notruncзапитується, вхід обробляється і збирається в повнорозмірні вихідні блоки до досягнення кінця введення.

Без ibs=і / або obs=аргументи , це не може мати значення - тому що ibsі obsобидва той же розмір за замовчуванням. Однак ви можете отримати явну інформацію про буферизацію введення, вказавши різні розміри для або не вказавши bs= (тому що це має перевагу) .

Наприклад, якщо ви робите:

IN| dd ibs=1| OUT

... тоді POSIX ddбуде write()в шматки 512 байтів, збираючи кожен read()байт окремо в один вихідний блок.

Інакше, якщо ви зробите ...

IN| dd obs=1kx1k| OUT

... POSIX одночасноdd матиме read() максимум 512 байт, але write()кожен вихідний блок розміром мегабайт (ядро, що дозволяє і виключає, можливо, останній - тому що це EOF) повністю, збираючи вхід у повнорозмірні вихідні блоки .

Також із специфікації, однак:

  • count=n
    • Скопіюйте лише n вхідних блоків.

count=карти на i?bs=блоки, і так, щоб обробляти довільне обмеження на count=портативному порталі, вам знадобиться два ddс. Найпрактичніший спосіб зробити це за допомогою двох dds - це передати висновок одного на вхід іншого, що, безумовно, ставить нас у сферу читання / запису спеціального файлу незалежно від вихідного типу введення.

IPC-труба означає, що, задаючи [io]bs=аргументи, які, щоб зробити це безпечно, повинні зберігати такі значення в межах визначеного системою PIPE_BUFмежі. POSIX стверджує, що ядро ​​системи повинно гарантувати лише атомні read()s і write()s в межах PIPE_BUF, визначених у limits.h. POSIX гарантує , що PIPE_BUFбуде принаймні ...

  • {_POSIX_PIPE_BUF}
    • Максимальна кількість байтів, які гарантовано є атомними при записі на трубу.
    • Значення: 512

... (що також буває за замовчуванням ddрозмір вводу-виводу за замовчуванням ) , але фактичне значення зазвичай становить принаймні 4k. У сучасній системі Linux це за замовчуванням 64k.

Отже, коли ви налаштовуєте свої ddпроцеси, ви повинні робити це на факторі блоку на основі трьох значень:

  1. bs = (obs = PIPE_BUFабо менший)
  2. n = загальна бажана кількість прочитаних байтів
  3. count = n / bs

Подібно до:

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

Вам потрібно синхронізувати ddвведення / обмін / обробку вхідних даних, які не можна шукати. Іншими словами, зробіть буферні труби явними, і вони перестануть бути проблемою. Ось для чого dd. Невідома кількість тут - yesрозмір буфера, але якщо ви заблокуєте його до відомої кількості іншим, ddто незначне помноження може зробити dd безпечним для копіювання даних (без ризику пошкодження через часткове читання або запис) навіть коли довільно обмежує вхідний w / count=w / будь-який довільний тип введення в будь-якій системі POSIX і не пропускає жодного байта.

Ось фрагмент із специфікації POSIX :

  • ibs=expr
    • Вкажіть розмір блоку введення в байтах за (за замовчуванням 512) .expr
  • obs=expr
    • Вкажіть розмір вихідного блоку в байтах за (за замовчуванням 512) .expr
  • bs=expr
    • Встановіть як розмір блоку вводу, так і виводу на exprбайти, заміщення ibs=та obs=. Якщо перетворення, окрім sync, noerrorта notruncне вказане, кожен вхідний блок повинен бути скопійований на вихід у вигляді одного блоку без агрегації коротких блоків.

Ви також знайдете тут щось краще пояснене тут .


5

З розетками, трубами або ttys читання () і запис () може передавати менше потрібного розміру, тому при використанні DD на них вам потрібен прапор fullblock. Однак у звичайних файлах та блокових пристроях є лише два рази, коли вони можуть робити коротке читання / запис: коли ви дістаєтесь до EOF або якщо є помилка. Ось чому старіші реалізації DD без прапора fullblock були безпечними для дублювання дисків.


Це правда для всіх сучасних уніцій? (Я знаю, що це не було правдою для Linux в якийсь момент, можливо, до 2.0.x або 2.2.x. Я пам’ятаю, що вийшов з mke2fsладу мовчки, тому що дзвонив write()з розміром не 2 живлення (3kB IIRC) і ядро ​​округлене до потужності 2.)
Жиль,

@Gilles, це звучить як зовсім інше питання. Завжди потрібно використовувати кратний розмір належного розміру блоку з блоковими пристроями. Я впевнений, що це стосується всіх уніцій, а також для Windows.
psusi

Окрім стрічок, розмір блоку пристрою суто для того, щоб ядро ​​дбало чи ні. cat </dev/sda >/dev/sdbдобре працює для клонування диска.
Жиль

@Gilles, тому що кішка використовує відповідний розмір блоку, як зазначив OrbWeaver у своїй відповіді.
psusi

Ні, немає "відповідного розміру блоку". catпідбирає розмір буфера для продуктивності; він не отримує ніякої інформації, пов’язаної з пристроєм, з ядра. Крім стрічок, ви можете read()і write()на блоковий пристрій будь-якого розміру. Щонайменше, для Linux st_blksizeзалежить лише файлова система, де знаходиться inode блочного пристрою, а не від базового пристрою.
Жиль
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.