Kapan dd cocok untuk menyalin data? (atau, ketika dibaca () dan tulis () parsial)


60

Versi singkat: Dalam keadaan apa ddaman digunakan untuk menyalin data, aman berarti bahwa tidak ada risiko korupsi karena membaca atau menulis sebagian?

Versi panjang - mukadimah: dd sering digunakan untuk menyalin data, terutama dari atau ke perangkat ( contoh ). Kadang-kadang dikaitkan sifat mistis karena dapat mengakses perangkat pada tingkat yang lebih rendah daripada alat lain (padahal sebenarnya itu adalah file perangkat yang melakukan keajaiban) - namun dd if=/dev/sdahal yang sama cat /dev/sda. ddkadang-kadang dianggap lebih cepat, tetapi catdapat mengalahkannya dalam praktik . Meskipun demikian, ddmemiliki sifat unik yang membuatnya kadang-kadang benar-benar bermanfaat .

Masalah: dd if=foo of=bar sebenarnya tidak sama dengan cat <foo >bar. Pada sebagian besar uni¹, ddlakukan satu panggilan ke read(). (Saya menemukan POSIX kabur pada apa yang disebut "membaca blok input" dd.) Jika read()mengembalikan hasil parsial (yang, menurut POSIX dan dokumen referensi lainnya, diizinkan kecuali kecuali dokumentasi implementasi mengatakan sebaliknya), sebagian blok disalin. Persis masalah yang sama ada untuk write().

Pengamatan : Dalam praktiknya, saya telah menemukan bahwa dddapat mengatasi perangkat blok dan file biasa, tetapi itu mungkin karena saya belum banyak menggunakannya. Ketika berbicara tentang pipa, tidak sulit untuk ddmenyalahkan; misalnya coba kode ini :

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

dan periksa ukuran outfile (kemungkinan berada di bawah 10MB).

Pertanyaan : Dalam keadaan apa ddaman digunakan untuk menyalin data? Dengan kata lain, kondisi apa pada ukuran blok, pada implementasi, pada jenis file, dll, dapat memastikan bahwa ddakan menyalin semua data?

( GNU dd memiliki fullblockflag untuk memberitahukannya untuk memanggil read()atau write()dalam satu loop sehingga untuk mentransfer blok penuh. Jadi dd iflag=fullblockselalu aman. Pertanyaan saya adalah tentang kasus ketika flag-flag ini (yang tidak ada pada implementasi lain) tidak digunakan .)

¹ Saya sudah memeriksa OpenBSD, GNU coreutils dan BusyBox.


Saya belum pernah melihat sistem Unixy yang benar-benar bisa membaca beberapa MiB dalam sekali baca (2) ...
vonbrand

3
Saat menggunakan count, iflag=fullblockini wajib (atau, sebagai alternatif, iflag=count_bytes). Tidak ada oflag=fullblock.
frostschutz

Jawaban:


10

Dari spec :

  • Jika bs=exproperan ditentukan dan tidak ada konversi selain sync,, noerroratau notruncdiminta, data yang dikembalikan dari setiap blok input harus ditulis sebagai blok output terpisah; jika read()pengembalian kurang dari blok penuh dan synckonversi tidak ditentukan, blok output yang dihasilkan harus sama dengan blok input.

Jadi ini mungkin yang menyebabkan kebingungan Anda. Ya, karena ddini dirancang untuk memblokir, secara default parsial read()s akan dipetakan 1: 1 sampai parsial write()s, atau syncd pada ekor bantalan NUL atau karakter ruang untuk bs=ukuran ketika conv=syncditentukan.

Ini berarti bahwa ddadalah aman digunakan untuk menyalin data (w / tidak ada risiko korupsi karena membaca sebagian atau write) di setiap kasus tapi satu di mana ia sewenang-wenang dibatasi oleh count=argumen, karena jika tidak ddakan dengan senang hati write()output dalam blok berukuran identik untuk mereka yang inputnya read()sampai read()benar-benar melewatinya. Dan bahkan peringatan ini hanya berlaku bila bs=ditentukan atau obs=yang tidak ditentukan, sebagai kalimat berikutnya di negara-negara spesifikasi:

  • Jika bs=exproperan tidak ditentukan, atau konversi selain sync,, noerroratau notruncdiminta, input harus diproses dan dikumpulkan ke dalam blok output berukuran penuh sampai akhir input tercapai.

Tanpa ibs=dan / atau obs=argumen ini tidak masalah - karena ibsdan obskeduanya berukuran sama secara default. Namun, Anda bisa mendapatkan secara eksplisit tentang buffer input dengan menentukan ukuran yang berbeda untuk salah satu dan tidak menentukan bs= (karena itu diutamakan) .

Misalnya, jika Anda melakukannya:

IN| dd ibs=1| OUT

... maka POSIX ddakan write()dalam potongan 512 byte dengan mengumpulkan setiap read()byte tunggal ke dalam satu blok output.

Jika tidak, jika Anda ...

IN| dd obs=1kx1k| OUT

... POSIX ddakan read() maksimal 512 byte pada satu waktu, tetapi write()setiap blok output berukuran megabyte (kernel memungkinkan dan mengecualikan yang terakhir - karena EOF) secara penuh dengan mengumpulkan input ke dalam blok output berukuran penuh .

Juga dari spec, meskipun:

  • count=n
    • Salin hanya n blok input.

count=peta ke i?bs=blok, dan untuk menangani batas sewenang-wenang count=portable Anda akan membutuhkan dua dds. Cara paling praktis untuk melakukannya dengan dua dds adalah dengan menyalurkan output dari satu ke input yang lain, yang tentunya menempatkan kita dalam bidang membaca / menulis file khusus terlepas dari jenis input asli.

Pipa IPC berarti bahwa ketika menetapkan [io]bs=argumen bahwa, untuk melakukannya dengan aman, Anda harus menyimpan nilai-nilai tersebut dalam PIPE_BUFbatas yang ditentukan sistem . POSIX menyatakan bahwa sistem kernel hanya harus menjamin atom read()dan write()s dalam batas-batas PIPE_BUFsebagaimana didefinisikan dalam limits.h. POSIX menjamin bahwa PIPE_BUFmenjadi setidaknya ...

  • {_POSIX_PIPE_BUF}
    • Jumlah maksimum byte yang dijamin atom saat menulis ke pipa.
    • Nilai: 512

... (yang juga merupakan default ddi / o blocksize) , tetapi nilai sebenarnya biasanya setidaknya 4k. Pada sistem linux yang terbaru, secara default, 64k.

Jadi ketika Anda mengatur ddproses Anda, Anda harus melakukannya pada faktor blok berdasarkan pada tiga nilai:

  1. bs = (obs = PIPE_BUFatau lebih kecil)
  2. n = jumlah total byte yang diinginkan yang dibaca
  3. hitung = n / bs

Suka:

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

Anda harus menyinkronkan i / ow / dduntuk menangani input yang tidak dapat dicari. Dengan kata lain, buat penyangga pipa eksplisit dan tidak lagi menjadi masalah. Itu untuk apa dd. Kuantitas yang tidak diketahui di sini adalah yesukuran buffer - tetapi jika Anda memblokirnya ke kuantitas yang diketahui dengan yang lain, ddmaka penggandaan informasi yang sedikit dapat membuat dd aman untuk digunakan untuk menyalin data (tanpa risiko korupsi karena membaca atau menulis sebagian) bahkan ketika secara sewenang-wenang membatasi input w / count=w / sembarang tipe input sembarang pada sistem POSIX dan tanpa melewatkan satu byte pun.

Berikut cuplikan dari spec POSIX :

  • ibs=expr
    • Tentukan ukuran blok input, dalam bytes, oleh (standarnya adalah 512) .expr
  • obs=expr
    • Tentukan ukuran blok output, dalam bytes, oleh (standarnya adalah 512) .expr
  • bs=expr
    • Atur ukuran blok input dan output ke exprbyte, menggantikan ibs=dan obs=. Jika tidak ada konversi selain sync,, noerrordan notruncditentukan, setiap blok input harus disalin ke output sebagai satu blok tanpa mengagregasi blok pendek.

Anda juga akan menemukan beberapa penjelasan yang lebih baik di sini .


5

Dengan soket, pipa, atau ttys, read () dan write () dapat mentransfer kurang dari ukuran yang diminta, jadi ketika menggunakan dd pada ini, Anda memerlukan bendera blok penuh. Namun, dengan file biasa dan blokir perangkat, hanya ada dua kali ketika mereka dapat melakukan baca / tulis singkat: ketika Anda mencapai EOF, atau jika ada kesalahan. Inilah sebabnya mengapa implementasi lama dari dd tanpa tanda blok penuh aman digunakan untuk duplikasi disk.


Apakah itu berlaku untuk semua persatuan modern? (Saya tahu itu tidak benar untuk Linux di beberapa titik, mungkin hingga 2.0.x atau 2.2.x. Saya ingat mke2fsgagal secara diam-diam karena disebut write()dengan beberapa ukuran non-power-of-2 (3kB IIRC) dan kernel bulat turun ke kekuatan 2.)
Gilles 'SO- stop being evil'

@Gilles yang terdengar seperti masalah yang sama sekali berbeda. Anda selalu harus menggunakan kelipatan ukuran blok yang tepat dengan perangkat blok. Saya cukup yakin itu berlaku untuk semua unicies, dan itu juga berlaku untuk Windows.
psusi

Terlepas dari kaset, ukuran blok suatu perangkat murni untuk diperhatikan oleh kernel, atau tidak. cat </dev/sda >/dev/sdbberfungsi dengan baik untuk mengkloning disk.
Gilles 'SO- berhenti bersikap jahat'

@Gilles itu karena kucing menggunakan ukuran blok yang sesuai, seperti yang dicatat OrbWeaver dalam jawabannya.
psusi

Tidak, tidak ada "ukuran blok yang sesuai". catmengambil ukuran buffer untuk kinerja; itu tidak mendapatkan informasi terkait perangkat dari kernel. Terlepas dari kaset, Anda dapat read()dan write()ke perangkat blok dengan ukuran apa pun. Setidaknya di Linux, st_blksizehanya bergantung pada sistem file tempat inode perangkat blok berada, bukan pada perangkat yang mendasarinya.
Gilles 'SO- berhenti bersikap jahat'
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.