Tại sao dd từ / dev / ngẫu nhiên cho các kích thước tệp khác nhau?


26

Tôi đang chạy lệnh sau trên hệ thống Ubuntu:

dd if=/dev/random of=rand bs=1K count=2

Tuy nhiên, mỗi lần tôi chạy nó, tôi kết thúc với một tệp có kích thước khác nhau. Tại sao lại thế này? Làm cách nào tôi có thể tạo một tệp có kích thước nhất định chứa đầy dữ liệu ngẫu nhiên?


1
/dev/randomsẽ chặn nếu không có đủ entropy có sẵn để tạo số chữ số bạn muốn. đơn giản chỉ cần có thời gian để thu thập số lượng "ngẫu nhiên" ngẫu nhiên chất lượng cao đó ... Hoặc sử dụng /dev/urandomcho giá trị "ngẫu nhiên" ít ngẫu nhiên hơn hoặc kiểm tra nhóm entropy của bạn (trong một vòng lặp và chờ khi cần) ...
Peter.O


3
chỉ cần thêmiflag=fullblock
frostschutz

Câu trả lời:


31

Bạn đang quan sát sự kết hợp giữa hành vi kỳ dị ddvới hành vi kỳ dị của Linux /dev/random. Cả hai, hiếm khi, là công cụ phù hợp cho công việc.

Linux /dev/randomtrả về dữ liệu một cách tiết kiệm. Nó dựa trên giả định rằng entropy trong trình tạo số giả ngẫu nhiên bị dập tắt với tốc độ rất nhanh. Vì việc thu thập entropy mới là chậm, /dev/randomthường chỉ từ bỏ một vài byte mỗi lần.

ddlà một chương trình cũ, cáu kỉnh ban đầu dự định hoạt động trên các thiết bị băng. Khi bạn bảo nó đọc một khối 1kB, nó sẽ cố đọc một khối. Nếu việc đọc trả về ít hơn 1024 byte, khó khăn, đó là tất cả những gì bạn nhận được. Thế là dd if=/dev/random bs=1K count=2thực hiện hai read(2)cuộc gọi. Vì nó đọc từ đó /dev/random, hai readcuộc gọi thường chỉ trả về một vài byte, với số lượng khác nhau tùy thuộc vào entropy có sẵn. Xem thêm Khi nào dd phù hợp để sao chép dữ liệu? (hoặc, khi được đọc () và viết () một phần)

Trừ khi bạn đang thiết kế trình cài đặt hoặc trình sao chép hệ điều hành, bạn sẽ không bao giờ nên sử dụng /dev/randomLinux /dev/urandom. Các urandomtrang người đàn ông có phần sai lệch; /dev/urandomtrong thực tế là phù hợp cho mật mã, thậm chí để tạo ra các khóa tồn tại lâu dài. Hạn chế duy nhất /dev/urandomlà nó phải được cung cấp đủ entropy; Các bản phân phối Linux thường lưu entropy giữa các lần khởi động lại, vì vậy lần duy nhất bạn có thể không có đủ entropy là trên bản cài đặt mới. Entropy không mất đi trong điều kiện thực tế. Để biết thêm thông tin, hãy đọc Có phải rand từ / dev / urandom an toàn cho khóa đăng nhập không? cho ăn / dev / nhóm entropy ngẫu nhiên? .

Hầu hết việc sử dụng ddđược thể hiện tốt hơn với các công cụ như headhoặc tail. Nếu bạn muốn 2kB byte ngẫu nhiên, hãy chạy

head -c 2k </dev/urandom >rand

Với các nhân Linux cũ hơn, bạn có thể thoát khỏi

dd if=/dev/urandom of=rand bs=1k count=2

bởi vì /dev/urandomvui vẻ trả lại nhiều byte theo yêu cầu. Nhưng điều này không còn đúng kể từ kernel 3.16, giờ đây nó bị giới hạn ở mức 32 MB .

Nói chung, khi bạn cần sử dụng ddđể trích xuất một số byte cố định và đầu vào của nó không đến từ một tệp thông thường hoặc thiết bị chặn, bạn cần đọc từng byte theo byte : dd bs=1 count=2048.


Cảm ơn các mẹo về việc sử dụng đầu thay vì dd. Điều đó cho phép tôi vẫn sử dụng / dev / ngẫu nhiên nếu tôi muốn. Mặc dù / dev / urandom có ​​thể là đủ như bạn đề cập, thật tuyệt khi biết cách sử dụng / dev / ngẫu nhiên khi có nhu cầu.
Daniel

trên hạt nhân kể từ 3.16 /dev/urandom trả về 32m mỗiread() .
mikeerv

Ngoài ra, nếu bạn cần một lệnh tuân thủ POSIX, bạn có thể sử dụng mẹo ở đây: unix.stackexchange.com/a/192114 dd if=/dev/urandom ibs=1k obs=1k | dd bs=1k count=2
Rufflewind

11

Từ man 4 randomtrên hộp RHEL 5:

Khi đọc, thiết bị / dev / ngẫu nhiên sẽ chỉ trả về các byte ngẫu nhiên trong số lượng bit nhiễu ước tính trong nhóm entropy.

Tôi nhận được các tệp có kích thước 213 byte trên máy đó. Trở lại với người đàn ông 4 ngẫu nhiên:

Khi đọc, thiết bị / dev / urandom sẽ trả về bao nhiêu byte theo yêu cầu.

Tôi nhận được 2048 byte từ mỗi lần gọi dd if=/dev/urandom of=rand bs=1K count=2

Tôi kết luận rằng sự khác biệt là do máy của bạn tạo ra bao nhiêu entropy giữa các lần gọi dd if=/dev/random ...


Vâng, thực tế, trừ khi anh ta ở một ứng dụng tiền điện tử thực sự, @Daniel nên sử dụng / dev / urandom. Nhưng tôi bối rối không hiểu tại sao dd if=/dev/random bs=1K count=2lại dừng lại khi bể entropy rõ ràng bị rút cạn. Từ các tài liệu, nó sẽ chặn cho đến khi có nhiều entropy hơn, vì vậy ddsẽ viết ra tệp từ từ, thay vì chỉ vứt bỏ nhóm hiện tại và thoát.
cjc

Tôi cũng băn khoăn về điều đó, nhưng nó phù hợp trên cả RHEL, Slackware 13.1 và Arch hiện tại. RHEL là x86_64, các số khác là 32 bit. Thật không may, các tài liệu dd có định dạng thông tin GNU, vì vậy tôi chưa đọc hết.
Bruce Ediger

Nó cũng phù hợp với Gentoo.
Matthew Scharley

4
@cjc: Đó là bởi vì khi bạn gọi read(fd, mybuf, 1024)FD chặn, nó sẽ trả về ngay khi thiết bị bên dưới trả về một số dữ liệu. Nếu có 1024 byte để đọc, nó sẽ trả về điều đó. Nếu chỉ có 201 byte, nó sẽ trả về 201. Nếu có 0 byte có sẵn, nó sẽ chặn cho đến khi có ít nhất một byte có sẵn, sau đó trả lại nó / chúng.
Warren Young

@WarrenYoung có đọc từ / dev / ngẫu nhiên thoát nội dung của nó không? Tôi giả sử như vậy.
Michael Martinez

5

Tại sao ddgiảm dữ liệu? ... Gilles đã đặt ra câu hỏi hấp dẫn này về dd:
Khi nào dd phù hợp để sao chép dữ liệu? (hoặc, khi được đọc () và viết () một phần)
Đây là đoạn trích từ câu hỏi đó:

    * ... không khó để đặt dd bị lỗ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 tệp ra (nó có thể dưới 10MB).


Ngoài nhận xét của tôi (ở cuối câu hỏi của bạn), một cái gì đó như thế này đang lặp đi lặp lại để xem ... Nó bắt các byte của bạn trong tệp $trnd. Tôi đã chọn tùy ý bs = 8

Di chuyển chuột của bạn và xem nó tăng tốc.
Với máy tính của tôi không hoạt động (AFK và không có hoạt động Mạng) và sau khi cạn kiệt nhóm entropy, phải mất 2 giờ 12 phút để thu thập chỉ 1192 byte, tại thời điểm đó tôi đã hủy nó.

Sau đó, với việc tôi di chuyển chuột liên tục, phải mất 1 phút 15 giây tương đối ngắn hơn để thu thập cùng số byte.

Điều này cho thấy khá rõ rằng việc thu thập entropy không dựa trên tốc độ CPU, mà là dựa trên các sự kiện ngẫu nhiên và hệ thống Ubuntu của tôi sử dụng chuột như một trong những yếu tố ngẫu nhiên quan trọng của nó .

get=2048
trnd=/tmp/$USER.rnd; >"$trnd"
while (( $(wc -c <"$trnd") < $get )) ;do
    dd if=/dev/random bs=8 count=1 2>/dev/null >>"$trnd"
    echo -n "itt: $((i+=1))  ct: "; wc -c <"$trnd"
done
truncate -s $get "$trnd"
echo -e "\nfinal count: "; wc -c <"$trnd"

1

ddđược thiết kế để chặn - nó thường là công cụ tốt nhất để bạn đọc từ các đầu vào có kích thước thay đổi nếu bạn cần thực hiện ngay lập tứcddsẽ không đệm hiện tại đọc vào một số tương lai write() (trừ khi bạn cấu hình rõ ràng theo cách đó với mức độ quan sát lớn hơn ibs) , nhưng sẽ thay vào đó write()mọi thứ nó đọc ngay khi read()(và tùy ý xử lý nó) .

Dưới đây là một số định nghĩa quan trọng :

  • 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.

Vì vậy, bạn thấy, khi ibsobsđược định nghĩa cùng nhau bssau đó ibsđược ưu tiên - nhưng nếu không, nếu bạn là cụ thể, thì hoặc là obshoặc cbs.

Đây là một ví dụ trong đó ibslà quan trọng nhất. Bạn có thể làm một cái gì đó như thế này nếu bạn muốn theo dõi bao lâu /dev/randomhồ bơi đầy ...

dd "ibs=$size" conv=sync "count=$lmt" \ 
    if=/dev/random of="$somefile"

Miễn là if=mục tiêu của bạn hoàn toàn có thể đọc được, điều đó sẽ luôn dẫn đến cùng một tệp đầu ra có kích thước tương tự, bởi vì ddsẽ syncsắp xếp lại các khối đọc trong null. Nói cách khác, nếu dd read()s cho một khối $((size=10)) $((count=5))thời gian đầu vào và read()tệp trả về 2 byte, sau đó 8 byte, sau đó là 12 byte, sau đó là 2 byte, sau đó là 4 byte, ddsẽ ghi vào tệp của nó một cái gì đó như

 2 read bytes 8NULs \
 8 read bytes 2NULs \
10 read bytes 0NULs \
 4 read bytes 6NULs \
 4 read bytes 6NULs

... bởi vì dd, theo mặc định, không trì hoãn. Vì vậy, nếu bạn cần theo dõi trong luồng và phân định việc ghi của một số quy trình khác, ddlà công cụ dành cho bạn.

Nếu bạn chỉ viết một số lượng dữ liệu vào một tệp thông thường thì ngược lại với các tuyên bố khác được đưa ra ở đây, bạn cũng có thể sử dụng ddcho việc này - và khá dễ dàng - nhưng bạn sẽ cần nhiều hơn một và một yếu tố chặn đáng tin cậy .

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

{   dd ibs="$size" obs="${size}x$block_factor" |
    dd bs="${size}x$blockfactor" "count=$lmt"
}  <infile >outfile

... khối thứ nhất ddsẽ đệm càng nhiều ibs="$size"khối đầu vào cần thiết để lấp đầy ít nhất một obs="${size}x$block_factor"khối đầu ra cho mỗi write()ống giữa nó và khối thứ hai dd. Điều này có nghĩa là cái thứ hai ddcó thể giới hạn đầu ra một cách đáng tin cậy count="$lmt"bởi vì tất cả các sản write()phẩm đầu tiên sẽ khớp với kích thước khối i / o của nó - bất kể bao nhiêu read()cái đầu tiên ddphải làm để làm như vậy.

đó là cách bạn có thể sử dụng ddđể đọc các đường ống hoặc các loại tệp đặc biệt khác một cách đáng tin cậy - chỉ với một chút toán học.

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.