Dlaczego dd z / dev / random daje różne rozmiary plików?

26

W systemie Ubuntu uruchamiam następujące polecenie:

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

Jednak za każdym razem, gdy go uruchamiam, kończę na pliku o innym rozmiarze. Dlaczego to? Jak mogę wygenerować plik o danym rozmiarze wypełniony losowymi danymi?

Daniel
źródło
1
/dev/randomzablokuje się, jeśli nie będzie wystarczającej ilości entropii do wygenerowania żądanej liczby cyfr. po prostu potrzeba czasu, aby zebrać tyle wysokiej jakości losowej „losowości” psuedo ... Albo użyj /dev/urandommniej losowej „losowej” wartości, albo sprawdź pulę entropii (w pętli i poczekaj w razie potrzeby) ...
Peter.O
Zobacz także to pytanie .
Keith Thompson
3
wystarczy dodaćiflag=fullblock
frostschutz

Odpowiedzi:

31

Obserwujesz połączenie osobliwego zachowania ddz osobliwym zachowaniem Linuksa /dev/random. Nawiasem mówiąc, oba są rzadko odpowiednim narzędziem do pracy.

Linux /dev/randomzwraca dane oszczędnie. Opiera się na założeniu, że entropia w generatorze liczb pseudolosowych gaśnie bardzo szybko. Ponieważ gromadzenie nowej entropii jest powolne, /dev/randomzwykle rezygnuje tylko z kilku bajtów naraz.

ddto stary, zepsuty program początkowo przeznaczony do działania na urządzeniach taśmowych. Kiedy powiesz mu, żeby przeczytał jeden blok 1kB, spróbuje odczytać jeden blok. Jeśli odczyt zwraca mniej niż 1024 bajty, trudne, to wszystko, co dostajesz. Tak dd if=/dev/random bs=1K count=2czyni dwa read(2)połączenia. Ponieważ odczytuje /dev/random, dwa readwywołania zwykle zwracają tylko kilka bajtów, w różnej liczbie w zależności od dostępnej entropii. Zobacz także Kiedy dd nadaje się do kopiowania danych? (lub, gdy są czytane () i write () częściowe)

O ile nie projektujesz instalatora lub klonera systemu operacyjnego, nigdy nie powinieneś używać go /dev/randompod Linuksem, zawsze /dev/urandom. urandomStrona man jest nieco mylące; /dev/urandomw rzeczywistości nadaje się do kryptografii, nawet do generowania kluczy długowiecznych. Jedynym ograniczeniem /dev/urandomjest to, że musi być ono zaopatrzone w wystarczającą entropię; Dystrybucje Linuksa zwykle zapisują entropię między ponownymi uruchomieniami, więc jedyną chwilą, gdy nie masz wystarczającej ilości entropii, jest nowa instalacja. Entropia nie zużywa się w praktyce. Aby uzyskać więcej informacji, przeczytaj Czy rand z / dev / urandom jest bezpieczny dla klucza logowania? oraz Feeding / dev / random entropy pool? .

Większość zastosowań ddlepiej wyrażają narzędzia takie jak headlub tail. Jeśli chcesz 2kB losowych bajtów, uruchom

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

Dzięki starszym jądrom Linuksa możesz uciec

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

ponieważ /dev/urandomszczęśliwie zwrócił tyle bajtów, ile zażądano. Ale nie jest to już prawdą, ponieważ jądro 3.16 jest teraz ograniczone do 32 MB .

Na ogół, gdy trzeba użyć dd, aby wyodrębnić określoną liczbę bajtów, a jego wejście nie nadchodzi od zwykłego pliku lub urządzenia blokowego, trzeba czytać bajt po bajcie: dd bs=1 count=2048.

Gilles „SO- przestań być zły”
źródło
Dzięki za wskazówkę dotyczącą używania głowy zamiast dd. To pozwala mi nadal używać / dev / random, jeśli chcę. Chociaż / dev / urandom byłby prawdopodobnie wystarczający, jak wspomniałeś, dobrze jest wiedzieć, jak używać / dev / random, jeśli zajdzie taka potrzeba.
Daniel
na jądrach, ponieważ 3.16 /dev/urandom zwraca 32m naread() .
mikeserv
Ewentualnie, jeśli potrzebujesz polecenia zgodnego z POSIX, możesz skorzystać z tej sztuczki tutaj: unix.stackexchange.com/a/192114 dd if=/dev/urandom ibs=1k obs=1k | dd bs=1k count=2
Rufflewind
11

Z man 4 randompudełka RHEL 5:

Podczas odczytu urządzenie / dev / random zwróci losowe bajty tylko w ramach szacunkowej liczby bitów szumu w puli entropii.

Na tym komputerze dostaję pliki o rozmiarze 213 bajtów. Powrót do losowego mężczyzny 4:

Podczas odczytu urządzenie / dev / urandom zwróci tyle bajtów, ile jest wymaganych.

Otrzymuję 2048 bajtów przy każdym wywołaniu dd if=/dev/urandom of=rand bs=1K count=2

Stwierdzam, że różnica wynika z tego, ile entropii twoja maszyna generuje między wywołaniami dd if=/dev/random ...

Bruce Ediger
źródło
Tak, praktycznie, chyba że jest w prawdziwej aplikacji kryptograficznej, @Daniel powinien używać / dev / urandom. Ale zastanawiam się, dlaczego dd if=/dev/random bs=1K count=2kończy się, gdy pula entropii jest najwyraźniej wyczerpana. Z dokumentów powinien się blokować, dopóki nie będzie więcej entropii, więc ddzapisuje plik powoli, zamiast po prostu zrzucić bieżącą pulę i wyjść.
cjc
Też się nad tym zastanawiałem, ale jest spójny z RHEL, Slackware 13.1 i całkiem aktualnym Arch. RHEL był x86_64, pozostałe były 32-bitowe. Niestety dokumenty dd są w formacie informacji GNU, więc nie przeczytałem ich wszystkich.
Bruce Ediger
Jest również zgodny z Gentoo.
Matthew Scharley
4
@cjc: Dzieje się tak, ponieważ gdy wywołujesz read(fd, mybuf, 1024)blokujący FD, wraca, gdy tylko urządzenie bazowe zwróci niektóre dane. Jeśli jest 1024 bajtów do odczytania, zwraca to. Jeśli jest tylko 201 bajtów, zwróci 201. Jeśli dostępnych jest 0 bajtów, zostanie zablokowany, aż przynajmniej jeden bajt stanie się dostępny, a następnie zwróci go / je.
Warren Young,
@WarrenYoung czy czytanie z / dev / random drenuje jego zawartość? Zakładam, że tak.
Michael Martinez
5

Dlaczego ddupuszcza dane? ... Gilles postawił to interesujące pytanie dd:
Kiedy dd nadaje się do kopiowania danych? (lub, gdy są czytane () i write () częściowe)
Oto fragment tego pytania:

    * ... nietrudno jest winić dd; na przykład wypróbuj ten kod: **
        yes | dd of=out bs=1024k count=10
    i sprawdź rozmiar pliku wyjściowego (prawdopodobnie będzie to mniej niż 10 MB).


Oprócz mojego komentarza (na końcu twojego pytania), coś takiego jest warte obejrzenia ... Łapie twoje bajty w pliku $trnd. Pół arbitralnie wybrałem bs = 8

Poruszaj myszką i patrz, jak przyspiesza.
Gdy mój komputer był bezczynny (AFK i brak aktywności sieciowej), a po wyczerpaniu puli entropii zebranie tylko 1192 bajtów zajęło 2 godziny i 12 minut , w którym to momencie anulowałem.

Następnie, gdy ja ciągle poruszałem myszą, zajęło to stosunkowo dużo mniej 1 minutę i 15 sekund na zebranie tej samej liczby bajtów.

Pokazuje to dość wyraźnie, że gromadzenie entropii nie jest oparte na szybkości procesora, ale raczej na losowych zdarzeniach i że mój system Ubuntu używa myszy jako jednego z istotnych czynników losowych.

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"
Peter.O
źródło
1

ddjest przeznaczony do blokowania - jest to zwykle najlepsze narzędzie do Twojej dyspozycji do odczytu ze zmiennych danych wejściowych, jeśli potrzebujesz tego natychmiast, ponieważ ddnie buforuje bieżących odczytów w przyszłości write() (chyba że bardzo wyraźnie skonfigurujesz to w ten sposób z większym obsłem niż ibs) , ale zamiast write()wszystko to brzmi tak szybko, jak read()to wszystko (i ewentualnie przetwarza je) .

Oto kilka ważnych definicji :

  • ibs=expr
    • Określ rozmiar bloku wejściowego, w bajtach, do (domyślnie 512) .expr
  • obs=expr
    • Określ rozmiar bloku wyjściowego, w bajtach, do (domyślnie 512) .expr
  • bs=expr
    • Ustaw rozmiar bloku wejściowego i wyjściowego na exprbajty, zastępując ibs=i obs=. Jeśli konwersja nie inna niż sync, noerrori notruncjest określona, każdy blok wejściowy zostanie skopiowany na wyjście jako pojedynczy blok bez agregacji krótkie bloki.

Widzisz więc, kiedy ibsi obssą zdefiniowane razem, ponieważ bswtedy ibsma pierwszeństwo - ale w przeciwnym razie, jeśli jesteś konkretny, to albo obsalbo cbs.

Oto przykład, w którym ibsjest najważniejszy. Możesz zrobić coś takiego, jeśli chcesz śledzić, jak szybko /dev/randombasen się zapełni ...

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

Dopóki if=„s celem jest czytelny w ogóle, że będzie zawsze prowadzić w tej samej wielkości pliku wynikowego, ponieważ ddbędzie synchronize bloki odczytu w na null. Innymi słowy, jeśli dd read()s dla wejściowego bloku $((size=10)) $((count=5))czasów, a read()plik zwraca 2 bajty, to 8 bajtów, następnie 12 bajtów, następnie 2 bajty, a następnie 4 bajty, ddzapisze w swoim pliku wyjściowym coś w rodzaju

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

... ponieważ dddomyślnie nie opóźnia. Jeśli więc chcesz śledzić in-stream i ograniczyć zapisy innego procesu, ddto jest to narzędzie dla Ciebie.

Jeśli po prostu zapisujesz pewną ilość danych do zwykłego pliku, w przeciwieństwie do innych oświadczeń tutaj zawartych, możesz również użyć dddo tego - i dość łatwo - ale potrzebujesz więcej niż jednego i niezawodnego czynnika blokującego .

Na przykład, jeśli zrobiłeś:

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

... pierwszy ddbuforowałby tyle ibs="$size"bloków wejściowych, ile było niezbędnych do wypełnienia co najmniej jednego obs="${size}x$block_factor"bloku wyjściowego dla każdego write()potoku między nim a drugim dd. Oznacza to, że drugi ddmoże niezawodnie ograniczyć moc wyjściową, count="$lmt"ponieważ wszystkie write()pierwsze marki będą pasować do swojego rozmiaru bloku we / wy - niezależnie od tego, ile read()s ddmusi zrobić pierwszy .

W ten sposób możesz ddniezawodnie czytać potoki lub inne typy plików specjalnych - z odrobiną matematyki.

mikeserv
źródło