Nén luồng nhanh mà không tràn vào tài nguyên phần cứng?


23

Tôi có dung lượng ổ đĩa trống 200 GB, RAM 16 GB (trong đó ~ 1 GB bị chiếm bởi máy tính để bàn và kernel) và 6 GB trao đổi.

Tôi có ổ SSD ngoài 240 GB, với 70 GB được sử dụng 1 và phần còn lại miễn phí, tôi cần sao lưu vào đĩa của mình.

Thông thường, tôi sẽ dd if=/dev/sdb of=Desktop/disk.imgđĩa trước, sau đó nén nó, nhưng làm cho hình ảnh trước không phải là một tùy chọn vì làm như vậy sẽ cần nhiều không gian đĩa hơn tôi có, mặc dù bước nén sẽ dẫn đến không gian trống bị nén nên lưu trữ cuối cùng có thể dễ dàng phù hợp trên đĩa của tôi.

ddghi vào STDOUT theo mặc định và gzipcó thể đọc từ STDIN, vì vậy theo lý thuyết tôi có thể viết dd if=/dev/sdb | gzip -9 -, nhưng gzipmất nhiều thời gian hơn để đọc byte hơn là ddcó thể tạo ra chúng.

Từ man pipe:

Dữ liệu được ghi vào đầu ghi của ống được đệm bởi nhân cho đến khi nó được đọc từ đầu đọc của ống.

Tôi hình dung |giống như một đường ống thực sự - một ứng dụng đẩy dữ liệu vào và ứng dụng khác lấy dữ liệu ra khỏi hàng đợi của đường ống càng nhanh càng tốt.

Điều gì khi chương trình ở phía bên trái ghi nhiều dữ liệu nhanh hơn phía bên kia của đường ống có thể hy vọng xử lý nó? Nó sẽ gây ra bộ nhớ cực lớn hoặc sử dụng trao đổi, hoặc hạt nhân sẽ cố gắng tạo ra một bộ xếp hình trên đĩa, do đó làm đầy đĩa? Hoặc nó sẽ thất bại SIGPIPE Broken pipenếu bộ đệm quá lớn?

Về cơ bản, điều này rút ra hai câu hỏi:

  1. Ý nghĩa và kết quả của việc đẩy nhiều dữ liệu vào một đường ống hơn là được đọc tại một thời điểm?
  2. Cách đáng tin cậy để nén một luồng dữ liệu vào đĩa mà không đưa toàn bộ luồng dữ liệu không nén vào đĩa?

Lưu ý 1: Tôi không thể chỉ sao chép chính xác 70 GB đầu tiên được sử dụng và mong muốn có được một hệ thống làm việc hoặc hệ thống tập tin, vì sự phân mảnh và những thứ khác sẽ yêu cầu toàn bộ nội dung.


Tại sao bạn lại sao lưu toàn bộ hệ thống tệp như vậy, thay vì chỉ thư mục người dùng và có lẽ là danh sách các phần mềm không chuẩn được cài đặt?
jamesqf

5
@jamesqf Ví dụ. bởi vì nó dễ dàng hơn nhiều để khôi phục ...
deviantfan

4
@jamesqf Bởi vì sau đó tôi cũng nhận được khu vực khởi động và phân vùng trao đổi, để tôi có thể tạo lại đĩa chính xác thay vì có một tỷ tệp khó chịu.
con mèo

3
Mẹo ngẫu nhiên: nhìn vào lzopthay vì gzip; nó nén nhanh hơn nhiều chỉ với tỷ lệ nén thấp hơn một chút. Tôi thấy nó lý tưởng cho các hình ảnh đĩa trong đó tốc độ nén có thể là một nút cổ chai thực sự.
marcelm

1
"Điều gì khi chương trình ở phía bên trái ghi nhiều dữ liệu nhanh hơn phía bên kia của đường ống có thể hy vọng xử lý nó?" Hạt nhân sẽ làm cho quá trình viết đi ngủ cho đến khi có nhiều chỗ hơn trong đường ống.
Tavian Barnes

Câu trả lời:


16

Về mặt kỹ thuật, bạn thậm chí không cần dd:

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

Nếu bạn làm sử dụng dd, bạn nên luôn luôn đi với lớn hơn kích cỡ khối mặc định như dd bs=1Mhoặc bị quái syscall ( dd's kích cỡ khối mặc định là 512 byte, vì nó read()s và write()s đó là 4096syscalls mỗi MiB, quá nhiều chi phí).

gzip -9sử dụng rất nhiều CPU với rất ít để hiển thị cho nó. Nếu gziplàm bạn chậm lại, hãy giảm mức độ nén hoặc sử dụng phương pháp nén khác (nhanh hơn).

Nếu bạn đang thực hiện sao lưu dựa trên tệp thay vì ddhình ảnh, bạn có thể có một số logic quyết định có nén hay không (không có cách nào để làm như vậy đối với các loại tệp khác nhau). dar( tarthay thế`) là một ví dụ có các tùy chọn để làm như vậy.

Nếu không gian trống của bạn là ZERO (vì đó là ổ SSD trả về 0 một cách đáng tin cậy sau TRIM và bạn đã chạy fstrimvà bỏ bộ nhớ cache), bạn cũng có thể sử dụng ddvới conv=sparsecờ để tạo một hình ảnh thưa thớt, không thể nén, có thể lặp, sử dụng không gian đĩa cho vùng 0 . Yêu cầu tệp hình ảnh phải được hỗ trợ bởi một hệ thống tệp hỗ trợ các tệp thưa thớt.

Ngoài ra, đối với một số hệ thống tập tin, có các chương trình chỉ có thể hình ảnh các khu vực được sử dụng.


1
"Nếu bạn sử dụng dd, bạn phải luôn đi với kích thước khối lớn hơn mặc định như dd bs=1M" - Bạn có thể, nhưng đừng kỳ vọng quá nhiều. Trên PC của tôi, ddsẽ thực hiện khoảng 2GB / giây với các khối 512 byte. Đó sẽ không phải là nút cổ chai; gzipsẽ là.
marcelm

@marcelm Chúng tôi không bao giờ biết loại máy nào mọi người đang sử dụng. Nếu bạn đã sử dụng dd2GB / giây với các khối 512 byte, tôi sẽ ngạc nhiên nếu nó không đạt tối đa một lõi CPU 100% trong quy trình. Bây giờ nếu hộp của bạn là một hình tứ giác chỉ đứng yên, bạn có thể không nhận thấy sự khác biệt. Mọi người khác vẫn làm, mặc dù.
frostschutz

9
Thở dài. Mỗi khi ddblockize được đề cập, mọi người đến nitpicking. gzipĐược cpu chuyên sâu cũng là một phần câu trả lời của tôi, được chứ? Và xin lỗi, tôi không đồng ý với "không đáng kể". Nó chỉ có thể thêm 1-2 giây mỗi gig với gzip -9(nhưng vẫn chỉ mất vài phút khi xử lý hàng trăm hợp đồng biểu diễn) nhưng hãy đưa ra lời khuyên của bạn với tốc lzop -1độ 1 giây trên gig so với 4s mỗi gig. Đã thử nghiệm trên một củ khoai tây (vserver lõi đơn). Thêm một khối lành mạnh để ddchi phí không có gì và không có nhược điểm. Đừng nittc. Cứ làm đi. ymmv
frostschutz

19

ddđọc và ghi dữ liệu một khối tại một thời điểm và nó chỉ có một khối xuất sắc. Vì thế

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

cho thấy ddsử dụng khoảng 1 MB bộ nhớ. Bạn có thể chơi xung quanh với kích thước khối và thả valgrind, để xem hiệu ứng về ddtốc độ.

Khi bạn đi vào gzip, ddchỉ cần chậm lại để phù hợp với gziptốc độ của. Việc sử dụng bộ nhớ của nó không tăng, cũng không khiến kernel lưu trữ bộ đệm trên đĩa (kernel không biết cách thực hiện điều đó, ngoại trừ thông qua trao đổi). Một đường ống bị vỡ chỉ xảy ra khi một trong hai đầu của ống chết; xem signal(7)write(2)để biết chi tiết.

Như vậy

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

là một cách an toàn để làm những gì bạn đang theo đuổi.

Khi đường ống, quá trình viết kết thúc bị chặn bởi kernel nếu quá trình đọc không theo kịp. Bạn có thể thấy điều này bằng cách chạy

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

Bạn sẽ thấy rằng ddđọc 1MB, sau đó phát hành một write()cái ngồi đó chờ trong một phút trong khi sleepchạy. Đó là cách cả hai mặt của một đường ống cân bằng: các khối nhân ghi nếu quá trình viết quá nhanh và nó chặn đọc nếu quá trình đọc quá nhanh.


1
Điều đó thật tuyệt. Cơ chế nào ddbiết làm chậm để phù hợp với gziptốc độ của? Nó là tự động, giống như kernel, hoặc nó tính toán từ siêu dữ liệu về bộ mô tả tệp đầu ra của nó?
con mèo

9
@cat Nó tự động; ddcác cuộc gọi write()để đưa dữ liệu vào đường ống. write()thực sự chuyển điều khiển sang kernel để nó có thể thao tác bộ nhớ ống. Nếu hạt nhân thấy đường ống đầy, nó sẽ đợi ("chặn") cho đến khi đường ống có đủ chỗ. Chỉ sau đó, write()cuộc gọi kết thúc và chuyển điều khiển trở lại dd, sau đó sẽ ghi lại dữ liệu vào đường ống.
marcelm

9

Không có ý nghĩa tiêu cực nào khác ngoài hiệu suất: đường ống có bộ đệm, thường là 64K và sau đó, ghi vào đường ống sẽ đơn giản chặn cho đến khi gzipđọc thêm một số dữ liệu.


8

Trả lời câu hỏi thực tế về cách thức hoạt động của nó: "điều gì xảy ra nếu chương trình ở phía bên trái ghi nhiều dữ liệu nhanh hơn phía bên kia của đường ống có thể hy vọng xử lý nó?"

Điều này không xảy ra. Có một bộ đệm khá nhỏ, kích thước hạn chế trong đường ống; xem bộ đệm ống lớn như thế nào?

Khi bộ đệm ống đầy, chương trình gửi sẽ chặn . Khi nó thực hiện một cuộc gọi ghi, kernel sẽ không trả lại quyền điều khiển cho chương trình cho đến khi dữ liệu được ghi vào bộ đệm. Điều này cho thời gian CPU chương trình đọc để làm trống bộ đệm.


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.