Khi nào dd thích hợp để sao chép dữ liệu? (hoặc, khi được đọc () và viết () một phần)


60

Phiên bản ngắn: Trong trường hợp nào là ddan toàn để sử dụng để sao chép dữ liệu, có nghĩa là an toàn không có rủi ro tham nhũng do đọc hoặc ghi một phần?

Phiên bản dài - lời mở đầu: dd thường được sử dụng để sao chép dữ liệu, đặc biệt là từ hoặc đến một thiết bị ( ví dụ ). Đôi khi, các thuộc tính bí ẩn của việc có thể truy cập các thiết bị ở mức độ thấp hơn các công cụ khác (trong thực tế, đó là tệp thiết bị thực hiện phép thuật) - nhưng vẫn dd if=/dev/sdagiống như vậy cat /dev/sda. ddđôi khi được cho là nhanh hơn, nhưng catcó thể đánh bại nó trong thực tế . Tuy nhiên, ddđôi khi có các thuộc tính độc đáo làm cho nó thực sự hữu ích .

Vấn đề: dd if=foo of=bar trên thực tế, không giống như cat <foo >bar. Trên hầu hết các thông báo¹, ddthực hiện một cuộc gọi đến read(). (Tôi thấy POSIX mờ trên những gì cấu thành nên đọc một khối đầu vào, trong đó dd.) Nếu read()trả về một kết quả một phần (mà theo POSIX và các tài liệu tham chiếu khác, nó được phép trừ khi tài liệu triển khai nói khác), một phần khối được sao chép. Chính xác vấn đề tương tự tồn tại cho write().

Quan sát : Trong thực tế, tôi thấy rằng ddcó thể đối phó với các thiết bị khối và các tệp thông thường, nhưng điều đó có thể là tôi đã không thực hiện nó nhiều. Khi nói đến đường ống, không khó để đặt ddlỗi; ví dụ thử mã này :

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

và kiểm tra kích thước của outtệp (có thể dưới 10 MB).

Câu hỏi : Trong trường hợp nào là ddan toàn để sử dụng để sao chép dữ liệu? Nói cách khác, những điều kiện nào về kích thước khối, khi triển khai, trên các loại tệp, v.v., có thể đảm bảo rằng ddsẽ sao chép tất cả dữ liệu?

( GNU dd có một fullblockcờ để yêu cầu nó gọi read()hoặc write()trong một vòng lặp để chuyển một khối đầy đủ. Vì vậy, dd iflag=fullblockluôn an toàn. Câu hỏi của tôi là về trường hợp khi các cờ này (không tồn tại trên các triển khai khác) không được sử dụng .)

¹ Tôi đã kiểm tra trên OpenBSD, GNU coreutils và BusyBox.


Tôi chưa bao giờ thấy bất kỳ hệ thống Unixy nào thực sự có thể đọc một vài MiB trong một lần đọc (2) ...
vonbrand

3
Khi sử dụng count, iflag=fullblocklà bắt buộc (hoặc, thay thế, iflag=count_bytes). Không có oflag=fullblock.
frostschutz

Câu trả lời:


10

Từ thông số kỹ thuật :

  • Nếu bs=exprtoán hạng được chỉ định và không có chuyển đổi nào ngoài sync, noerrorhoặc notruncđược yêu cầu, dữ liệu được trả về từ mỗi khối đầu vào sẽ được ghi dưới dạng một khối đầu ra riêng biệt; nếu read()trả về ít hơn một khối đầy đủ và syncchuyển đổi không được chỉ định, khối đầu ra kết quả sẽ có cùng kích thước với khối đầu vào.

Vì vậy, đây có lẽ là những gì gây ra sự nhầm lẫn của bạn. Có, vì ddđược thiết kế để chặn, theo mặc định, một phần read()s sẽ được ánh xạ 1: 1 thành một phần write(), hoặc nếu không thì syncxuất hiện trên phần đệm đuôi NUL hoặc ký tự khoảng trắng theo bs=kích thước khi conv=syncđược chỉ định.

Điều này có nghĩa ddan toàn để sử dụng để sao chép dữ liệu (không có rủi ro tham nhũng do đọc hoặc ghi một phần) trong mọi trường hợp, nhưng một trường hợp bị giới hạn tùy ý bởi một count=đối số, vì nếu không thì ddsẽ vui lòng write()đầu ra của nó trong các khối có kích thước giống hệt nhau cho những người mà đầu vào của nó là read()cho đến khi nó read()hoàn toàn thông qua nó. Và thậm chí báo trước đây là chỉ đúng khi bs=được quy định hoặc obs=được không chỉ định, như câu hôm sau tại các tiểu bang spec:

  • Nếu bs=exprtoán hạng không được xác định, hoặc chuyển đổi khác hơn sync, noerrorhoặc notruncđược yêu cầu, đầu vào sẽ được xử lý và thu thập thành các khối lượng cỡ lớn cho đến khi kết thúc đầu vào là đạt.

Không có ibs=và / hoặc obs=đối số điều này không thể quan trọng - bởi vì ibsobscả hai đều có cùng kích thước theo mặc định. Tuy nhiên, bạn có thể nhận được rõ ràng về bộ đệm đầu vào bằng cách chỉ định các kích thước khác nhau cho một trong hai và không chỉ định bs= (vì nó được ưu tiên) .

Ví dụ: nếu bạn làm:

IN| dd ibs=1| OUT

... Sau đó, một POSIX ddsẽ có write()khối lượng 512 byte bằng cách thu thập từng read()byte đơn lẻ vào một khối đầu ra duy nhất.

Mặt khác, nếu bạn làm ...

IN| dd obs=1kx1k| OUT

... một POSIX ddsẽ có read() tối đa 512 byte mỗi lần, nhưng write()mỗi khối đầu ra có kích thước megabyte (hạt nhân cho phép và ngoại trừ có thể là cuối cùng - bởi vì đó là EOF) đầy đủ bằng cách thu thập đầu vào thành các khối đầu ra có kích thước đầy đủ .

Cũng từ thông số kỹ thuật, mặc dù:

  • count=n
    • Chỉ sao chép n khối đầu vào.

count=ánh xạ tới i?bs=các khối và do đó, để xử lý giới hạn tùy ý trên một cách hợp lý, count=bạn sẽ cần hai dds. Cách thực tế nhất để làm điều đó với hai dds là bằng cách đưa đầu ra của một vào đầu vào của một cái khác, điều này chắc chắn đưa chúng ta vào thế giới đọc / ghi một tệp đặc biệt bất kể loại đầu vào ban đầu.

Một ống IPC có nghĩa là khi chỉ định lập [io]bs=luận rằng, để làm như vậy một cách an toàn, bạn phải giữ các giá trị đó trong PIPE_BUFgiới hạn xác định của hệ thống . POSIX nói rằng hạt nhân hệ thống chỉ phải đảm bảo read()s và s nguyên tử write()trong giới hạn PIPE_BUFnhư được định nghĩa trong limits.h. POSIX đảm bảo rằng PIPE_BUFít nhất ...

  • {_POSIX_PIPE_BUF}
    • Số byte tối đa được đảm bảo là nguyên tử khi ghi vào đường ống.
    • Giá trị: 512

... (cũng có thể là ddkích thước khối i / o mặc định ) , nhưng giá trị thực tế thường ít nhất là 4k. Trên một hệ thống linux cập nhật, theo mặc định, nó là 64k.

Vì vậy, khi bạn thiết lập các ddquy trình của mình, bạn nên thực hiện theo yếu tố khối dựa trên ba giá trị:

  1. bs = (obs = PIPE_BUFhoặc ít hơn)
  2. n = tổng số byte mong muốn được đọc
  3. đếm = n / bs

Như:

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

Bạn phải đồng bộ hóa i / ow / ddđể xử lý các đầu vào không thể tìm kiếm. Nói cách khác, làm cho bộ đệm ống rõ ràng và chúng không còn là vấn đề nữa. Đó là những gì dddành cho. Số lượng không xác định ở đây là yeskích thước bộ đệm - nhưng nếu bạn chặn số lượng đó với một số lượng đã biếtdd thì phép nhân được thông báo một chút có thể dd an toàn để sử dụng để sao chép dữ liệu (không có rủi ro tham nhũng do đọc hoặc ghi một phần) ngay cả khi tùy ý giới hạn đầu vào w / count=w / bất kỳ loại đầu vào tùy ý nào trên bất kỳ hệ thống POSIX nào và không thiếu một byte nào.

Đây là một đoạn trích từ thông số POSIX :

  • ibs=expr
    • Chỉ định kích thước khối đầu vào, tính bằng byte, theo (mặc định là 512) .expr
  • obs=expr
    • Chỉ định kích thước khối đầu ra, tính bằng byte, theo (mặc định là 512) .expr
  • bs=expr
    • Đặt cả kích thước khối đầu vào và đầu ra thành exprbyte, thay thế ibs=obs=. Nếu không chuyển đổi khác hơn sync, noerrornotruncđược chỉ định, mỗi khối đầu vào sẽ được sao chép vào đầu ra như một khối duy nhất mà không tập hợp khối ngắn.

Bạn cũng sẽ tìm thấy một số điều này giải thích tốt hơn ở đây .


5

Với ổ cắm, đường ống hoặc ttys, read () và write () có thể chuyển ít hơn kích thước yêu cầu, vì vậy khi sử dụng dd trên những cái này, bạn cần cờ fullblock. Tuy nhiên, với các tệp thông thường và các thiết bị chặn, chỉ có hai lần chúng có thể đọc / ghi ngắn: khi bạn đạt EOF hoặc nếu có lỗi. Đây là lý do tại sao các triển khai dd cũ hơn mà không có cờ fullblock được sử dụng để sao chép đĩa.


Điều đó có đúng với tất cả các đoàn thể hiện đại không? (Tôi biết rằng điều đó không đúng với Linux vào một lúc nào đó, có thể lên tới 2.0.x hoặc 2.2.x. Tôi nhớ rằng đã mke2fsthất bại trong âm thầm vì nó được gọi write()với một số kích cỡ không phải là 2 (3kB IIRC) và hạt nhân được làm tròn xuống sức mạnh 2.)
Gilles 'SO- ngừng trở thành ác quỷ'

@Gilles nghe có vẻ như là một vấn đề hoàn toàn khác. Bạn luôn phải sử dụng nhiều kích thước khối thích hợp với các thiết bị khối. Tôi khá chắc chắn rằng nó đúng với tất cả các thông tin, và nó cũng đúng với Windows.
psusi

Ngoài băng, kích thước khối của thiết bị hoàn toàn là để nhân quan tâm, hoặc không. cat </dev/sda >/dev/sdbchỉ hoạt động tốt để sao chép một đĩa.
Gilles 'SO- ngừng trở nên xấu xa'

@Gilles đó là bởi vì con mèo sử dụng kích thước khối thích hợp, như OrbWeaver đã lưu ý trong câu trả lời của mình.
psusi

Không, không có kích thước khối thích hợp của người Viking. catchọn một kích thước bộ đệm cho hiệu suất; nó không nhận được bất kỳ thông tin nào liên quan đến thiết bị từ kernel. Ngoài băng, bạn có thể read()write()đến một thiết bị khối với bất kỳ kích thước nào. Ít nhất là trên Linux, st_blksizechỉ phụ thuộc vào hệ thống tệp nơi đặt nút inode của thiết bị chứ không phụ thuộc vào thiết bị bên dưới.
Gilles 'SO- ngừng trở nên xấu xa'
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.