Próbuję zrozumieć pojęcie specjalnych plików w systemie Linux. Jednak posiadanie specjalnego pliku /dev
wydaje się głupie, gdy jego funkcja może być zaimplementowana przez kilka wierszy w C, o ile mi wiadomo.
Co więcej, możesz go używać w prawie ten sam sposób, tj. Wpakowywanie do null
zamiast przekierowywania do /dev/null
. Czy jest jakiś konkretny powód, aby mieć go jako plik? Czy utworzenie pliku nie powoduje wielu innych problemów, takich jak zbyt wiele programów uzyskujących dostęp do tego samego pliku?
files
devices
executable
null
Ankur S.
źródło
źródło
cat foo | bar
jest znacznie gorzej (w skali) niżbar <foo
.cat
jest trywialnym programem, ale nawet trywialny program generuje koszty (niektóre z nich są specyficzne dla semantyki FIFO - ponieważ programy nieseek()
mogą znajdować się w FIFO, na przykład program, który można efektywnie zaimplementować za pomocą wyszukiwania, może w końcu wykonać znacznie droższe operacje po otrzymaniu potoku; przy użyciu takiego urządzenia znakowego/dev/null
może sfałszować te operacje lub za pomocą prawdziwego pliku może je zaimplementować, ale FIFO nie zezwala na jakąkolwiek obsługę kontekstową).grep blablubb file.txt 2>/dev/null && dosomething
nie mógł działać z wartością null będącą programem lub funkcją.read
funkcji for/dev/null
składa się z „return 0” (co oznacza, że nic nie robi i, jak sądzę, powoduje EOF): (From static github.com/torvalds/linux/blob/master/ drivers / char / mem.c )ssize_t read_null(struct file *file, char __user *buf, size_t count, loff_t *ppos) { return 0; }
(Och, po prostu widzę, że @JdeBP już o tym wspomniało. W każdym razie, oto ilustracja :-).Odpowiedzi:
Oprócz korzyści związanych z wydajnością korzystania ze specjalnego urządzenia, podstawową korzyścią jest modułowość . / dev / null może być używany w prawie każdym kontekście, w którym oczekiwany jest plik, nie tylko w potokach powłoki. Rozważ programy, które akceptują pliki jako parametry wiersza polecenia.
Są to wszystkie przypadki, w których użycie programu jako źródła lub ujścia byłoby wyjątkowo kłopotliwe. Nawet w przypadku potoku powłoki stdout i stderr mogą być przekierowywane do plików niezależnie, co jest trudne do zrobienia w plikach wykonywalnych takich jak sinking:
źródło
/dev/null
tylko poleceń powłoki. Możesz go użyć w innych parametrach dostarczonych do oprogramowania --- na przykład w plikach konfiguracyjnych. --- W przypadku oprogramowania jest to bardzo wygodne./dev/null
Plik nie musi odróżniać zwykłego pliku.pipe
,fork
iexecve
jak każdy inny rurociągów technologicznych, tylko ze zmianami dodup2
połączeń, które skonfigurować połączenia, prawda? To prawda, większość powłok nie oferują najpiękniejsze sposoby, aby to zrobić, ale przypuszczalnie jeśli nie mamy tyle urządzenie-as-file wzór i większość rzeczy w/dev
i/proc
były traktowane jako pliki wykonywalne, pociski zostałyby zaprojektowane sposoby zrobić to tak łatwo, jak teraz przekierowujemy.wait4
! Masz rację, z pewnością możliwe jest przesyłanie stdout i stderr do różnych programów za pomocą POSIX apis i może być możliwe wynalezienie sprytnej składni powłoki do przekierowywania stdout i stderr do różnych poleceń. Jednak nie jestem teraz świadomy żadnej takiej powłoki, a większą kwestią jest to, że / dev / null pasuje do istniejącego narzędzia (które w dużej mierze działa z plikami), a / bin / null nie. Możemy również wyobrazić sobie interfejs API we / wy, który ułatwia gcc (bezpieczne!) Wyjście do programu jak do pliku, ale nie w takiej sytuacji jesteśmy.grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile)
, w wyniku czego są osobno przetwarzaneerrfile
ioutfile
Szczerze mówiąc, nie jest to zwykły plik jako taki ; to specjalne urządzenie postaci :
Działa jako urządzenie, a nie jako plik lub program, oznacza, że łatwiej jest przekierować dane wejściowe lub wyjściowe, ponieważ można je podłączyć do dowolnego deskryptora pliku, w tym standardowego wejścia / wyjścia / błędu.
źródło
cat file | null
miałby dużo narzutu, najpierw przy ustawianiu potoku, spawnowaniu procesu, wykonywaniu „null” w nowym procesie itp. Równieżnull
sam zużywałby sporo CPU w pętli, odczytując bajty do bufora, który jest później właśnie odrzucone ... Implementacja/dev/null
w jądrze jest po prostu bardziej wydajna w ten sposób. A co jeśli chcesz przekazać/dev/null
jako argument zamiast przekierowania? (Możesz użyć<(...)
w bashu, ale jest to nawet znacznie cięższe!)null
Zamiast korzystać z przekierowania do/dev/null
, czy byłby prosty, jasny sposób, aby powiedzieć powłoce, aby uruchomiła program, wysyłając tylko jego stderr do zera?/dev/zero
zamiast tego.dd of=-
zapisuje do pliku o nazwie-
, po prostu pomińof=
zapisywanie na standardowe wyjście, ponieważ tamdd
zapisuje się domyślnie. Pipowanie dofalse
nie działałoby, ponieważfalse
nie odczytuje standardowego wejścia, więcdd
zostałby zabity SIGPIPE. Na polecenie, które porzuca swoje wejście można użyć ...cat > /dev/null
. Również porównanie, które prawdopodobnie nie ma znaczenia, ponieważ szyjka butelki byłoby prawdopodobnie generacją liczb losowych tutaj.dd
itp. Nawet nie zawracają sobie głowy wykonaniem syscall zapisu, gdy wykryją, że miejsce docelowe to / dev / null.Podejrzewam, dlaczego ma to wiele wspólnego z wizją / projektowaniem, które ukształtowało Uniksa (a co za tym idzie Linuksa) oraz wynikające z niego korzyści.
Nie ma wątpliwości, że nie ma dodatkowej korzyści w zakresie wydajności, jeśli nie rozpędza się dodatkowego procesu, ale myślę, że jest coś więcej: wczesny Unix miał metaforę „wszystko jest plikiem”, co ma nieoczywistą, ale elegancką zaletę, jeśli spojrzysz na z perspektywy systemowej, a nie z perspektywy skryptów powłoki.
Załóżmy, że masz
null
program wiersza polecenia i/dev/null
węzeł urządzenia. Z punktu widzenia skryptowania powłokifoo | null
program jest naprawdę przydatny i wygodny , afoo >/dev/null
pisanie zajmuje trochę więcej czasu i może wydawać się dziwny.Ale oto dwa ćwiczenia:
Spójrzmy prawdzie w realizacji programu
null
przy użyciu istniejących narzędzi Unix i/dev/null
- łatwa:cat >/dev/null
. Gotowy.Czy możesz wdrożyć
/dev/null
pod względemnull
?Masz całkowitą rację, że kod C, aby po prostu odrzucić dane wejściowe, jest trywialny, więc może nie być jeszcze oczywiste, dlaczego warto mieć plik wirtualny dostępny dla zadania.
Zastanów się: prawie każdy język programowania musi już pracować z plikami, deskryptorami plików i ścieżkami plików, ponieważ były one od początku częścią uniksowego paradygmatu „wszystko jest plikiem”.
Jeśli wszystko, co masz, to programy, które piszą na standardowe wyjście, cóż, program nie dba o to, czy przekierujesz je do wirtualnego pliku, który połyka wszystkie zapisy, lub potoku do programu, który połyka wszystkie zapisy.
Teraz, jeśli masz programy, które pobierają ścieżki plików zarówno do odczytu, jak i zapisu danych (co robi większość programów) - i chcesz dodać do tych programów funkcję „pustego wejścia” lub „odrzuć to wyjście” - cóż,
/dev/null
jest to bezpłatne.Zauważ, że jego elegancją jest to, że zmniejsza złożoność kodu wszystkich zaangażowanych programów - dla każdego wspólnego, ale specjalnego przypadku użycia, który twój system może zapewnić jako „plik” z rzeczywistą „nazwą pliku”, twój kod może uniknąć dodania niestandardowego polecenia -line opcje i niestandardowe ścieżki kodu do obsługi.
Dobra inżynieria oprogramowania często zależy od znalezienia dobrych lub „naturalnych” metafor dla wyodrębnienia jakiegoś elementu problemu w sposób, który staje się łatwiejszy do myślenia, ale pozostaje elastyczny , dzięki czemu można rozwiązać zasadniczo ten sam zakres problemów wyższego poziomu bez konieczności spędzaj czas i energię psychiczną na ciągłym wdrażaniu rozwiązań tych samych problemów niższego poziomu.
„Wszystko jest plikiem” wydaje się być jednym z takich metaforą dostępu do zasobów: Dzwonisz
open
z danej ścieżki w obszarze nazw heirarchical, coraz odniesienia (deskryptor) do obiektu, a możnaread
iwrite
itp na deskryptorów plików. Twoje stdin / stdout / stderr to także deskryptory plików, które właśnie zostały dla Ciebie wstępnie otwarte. Twoje potoki to tylko pliki i deskryptory plików, a przekierowanie plików pozwala skleić wszystkie te elementy razem.Uniksowi udało się tak samo, jak częściowo, ze względu na to, jak dobrze te abstrakcje działały razem, i
/dev/null
najlepiej jest to rozumieć jako część tej całości.PS Warto spojrzeć na uniksową wersję „wszystko jest plikiem” i rzeczy takie
/dev/null
jak pierwsze kroki w kierunku bardziej elastycznego i potężnego uogólnienia metafory zaimplementowanej w wielu późniejszych systemach.Na przykład w Uniksie specjalne obiekty podobne
/dev/null
do plików, takie jak musiały zostać zaimplementowane w samym jądrze, ale okazuje się, że wystarczające jest udostępnienie funkcjonalności w postaci pliku / folderu, która od tego czasu stworzyła wiele systemów, które zapewniają sposób dla programów aby to zrobić.Jednym z pierwszych był system operacyjny Plan 9, stworzony przez tych samych ludzi, którzy stworzyli Uniksa. Później GNU Hurd zrobił coś podobnego ze swoimi „tłumaczami”. Tymczasem Linux skończył się FUSE (który również rozprzestrzenił się na inne systemy głównego nurtu).
źródło
NUL
w każdym katalogu, tzn. Wystarczy wpisać> NUL
.Myślę, że
/dev/null
to urządzenie znakowe (które zachowuje się jak zwykły plik) zamiast programu ze względu na wydajność .Jeśli byłby to program, wymagałby ładowania, uruchamiania, planowania, uruchamiania, a następnie zatrzymywania i rozładowywania programu. Prosty program C, który opisujesz, nie zużyłby wiele zasobów, ale myślę, że robi to znaczącą różnicę, biorąc pod uwagę dużą liczbę (powiedzmy miliony) działań przekierowujących / potokowych, ponieważ operacje zarządzania procesami są kosztowne na dużą skalę, ponieważ obejmują przełączniki kontekstu.
Kolejne założenie: Pipingowanie do programu wymaga przydzielenia pamięci przez program odbierający (nawet jeśli jest on następnie usuwany bezpośrednio). Jeśli więc podłączasz się do narzędzia, masz podwójne zużycie pamięci, raz w programie wysyłającym i ponownie w programie odbierającym.
źródło
read
dane). Nie jest to bez znaczenia na jedno-rdzeniowym PDP-11, w którym zaprojektowano Uniksa! Przepustowość / kopiowanie pamięci jest dziś znacznie tańsze niż wtedy.write
Wywołanie systemowe do FD otworzyć na/dev/null
może wrócić od razu, nawet bez czytania żadnych danych z bufora.null
programie by zasysało, ale większość procesorów wielordzeniowych nie działa cały czas z zajętymi rdzeniami, więc Czas procesora do uruchomienianull
programu jest w większości darmowy. W systemie, w którym wszystkie rdzenie są ze sobą powiązane, bezużyteczne orurowanie to ważna sprawa. Ale nie, „gorący” bufor może zostać wielokrotnie przepisany bez opróżniania pamięci RAM, więc głównie walczymy o przepustowość L3, a nie pamięć podręczną. Niezbyt dobrze, szczególnie w systemie SMT (hyperthreading), w którym konkurują inne logiczne rdzenie na tej samej fizyce ...vpaddd zmm
: 16 32-bitowych elementach na instrukcję.Oprócz „wszystko jest plikiem”, a zatem łatwości użycia wszędzie tam, gdzie opiera się większość innych odpowiedzi, istnieje również problem z wydajnością, jak wspomniano w @ user5626466.
Aby pokazać w praktyce, stworzymy prosty program o nazwie
nullread.c
:i skompiluj to z
gcc -O2 -Wall -W nullread.c -o nullread
(Uwaga: nie możemy używać lseek (2) na rurach, więc jedynym sposobem na opróżnienie rury jest czytanie z niej, dopóki nie będzie pusta).
podczas gdy ze standardowym
/dev/null
przekierowaniem plików uzyskujemy znacznie lepszą prędkość (ze względu na wspomniane fakty: mniej przełączania kontekstu, jądro po prostu ignoruje dane zamiast kopiować itp.):(powinien to być komentarz, ale jest na to za duży i byłby całkowicie nieczytelny)
źródło
dd
bufor + bufor odbioru nie pasują; prawdopodobnie zajmujesz się przepustowością pamięci zamiast przepustowości pamięci podręcznej ostatniego poziomu. Możesz uzyskać lepszą przepustowość dzięki buforom 512k lub nawet 64k. (Zgodnie zstrace
moim pulpitemwrite
iread
zwracamy 1048576, więc myślę, że to oznacza, że płacimy tylko <-> koszt jądra użytkownika za unieważnienie TLB + opróżnienie przewidywania gałęzi raz na MiB, a nie na 64k @sourcejedi. To Spectre jak sądzę, łagodzenie, które ma najwięcej kosztów)syscall
natychmiastowego powrotuENOSYS
wynosi ~ 1800 cykli na Skylake z włączoną funkcją ograniczania widma, przy czym większość z nichwrmsr
unieważnia BPU, zgodnie z testami @ BeeOnRope . Przy wyłączonym łagodzeniu czas podróży użytkownika> jądro> użytkownika w obie strony wynosi ~ 160 cykli. Ale jeśli są dotykania dużo pamięci, łagodzenie Odwilż jest znacząca, too. Hugepages powinien pomóc (należy ponownie załadować mniej wpisów TLB).Twoje pytanie jest postawione tak, jakby coś można było uzyskać, być może w prostocie, za pomocą programu zerowego zamiast pliku. Być może możemy pozbyć się pojęcia „magicznych plików” i zamiast tego mieć po prostu „zwykłe fajki”.
Ale zastanów się, potok jest również plikiem . Zwykle nie są nazwane, więc można nimi manipulować tylko za pomocą deskryptorów plików.
Rozważmy ten nieco wymyślony przykład:
Korzystając z zastępowania procesów przez Basha, możemy osiągnąć to samo w bardziej okrągły sposób:
Wymień
grep
naecho
i możemy zobaczyć pod okładkami:<(...)
Konstrukt jest po prostu wymienić z nazwy pliku, grep i uważa, że to otwarcie byle plik, to właśnie dzieje się w nazwie/dev/fd/63
. Oto/dev/fd
magiczny katalog, który tworzy nazwane potoki dla każdego deskryptora pliku posiadanego przez plik uzyskujący do niego dostęp.Możemy sprawić, by mniej magii
mkfifo
było stworzenie nazwanego potoku, który pojawiłby sięls
i wszystko, tak jak zwykły plik:Gdzie indziej:
a oto grep wyświetli
foo
.Myślę, że kiedy zdasz sobie sprawę, że potoki i zwykłe pliki oraz specjalne pliki, takie jak / dev / null, są tylko plikami, to oczywiste jest, że implementacja programu zerowego jest bardziej złożona. Jądro musi obsługiwać zapisy do pliku w dowolny sposób, ale w przypadku / dev / null może po prostu upuścić zapisy na podłogę, podczas gdy za pomocą potoku musi faktycznie przenieść bajty do innego programu, który następnie musi faktycznie je czytam.
źródło
/dev/fd/63
?Twierdziłbym, że istnieje problem bezpieczeństwa wykraczający poza historyczne paradygmaty i wydajność. Ograniczenie liczby programów posiadających uprzywilejowane poświadczenia wykonania, bez względu na to, jak proste, jest podstawową zasadą bezpieczeństwa systemu. Z
/dev/null
pewnością konieczna byłaby wymiana , aby mieć takie uprawnienia ze względu na korzystanie z usług systemowych. Nowoczesne ramy bezpieczeństwa wykonują doskonałą pracę, zapobiegając exploitom, ale nie są niezawodne. Urządzenie napędzane przez jądro, do którego dostęp uzyskuje się jako plik, jest znacznie trudniejsze do wykorzystania.źródło
/dev/null
lub proponowanego programu Input-wyrzuceniem, atak wektor byłby taki sam: uzyskać skrypt lub program, który działa jako root, aby zrobić coś dziwnego (jak próbująlseek
się/dev/null
lub otwarte wiele razy z tego samego procesu lub co IDK. Lub wywołać/bin/null
w dziwnym środowisku, czy cokolwiek innego.Jak już zauważyli inni,
/dev/null
jest to program złożony z kilku wierszy kodu. Po prostu te wiersze kodu są częścią jądra.Aby to wyjaśnić, oto implementacja systemu Linux: urządzenie znakowe wywołuje funkcje podczas odczytu lub zapisu. Zapisywanie do
/dev/null
połączeń write_null , podczas odczytywania połączeń read_null , zarejestrowanych tutaj .Dosłownie garstka wierszy kodu: te funkcje nic nie robią. Potrzebujesz więcej linii kodu niż palców na dłoniach, tylko jeśli policzysz funkcje inne niż czytanie i pisanie.
źródło
Mam nadzieję, że znasz również / dev / chargeen / dev / zero i inni je lubią, w tym / dev / null.
LINUX / UNIX ma kilka z nich - udostępnionych, aby ludzie mogli dobrze wykorzystać WELL PISEMNE KOD Fragme.
Chargen ma na celu generowanie określonego i powtarzającego się wzoru znaków - jest dość szybki i przesuwa granice urządzeń szeregowych i pomaga debugować protokoły szeregowe, które zostały napisane i nieudane w jakimś teście lub innym.
Zero ma za zadanie wypełnić istniejący plik lub wyprowadzić wiele zer
/ dev / null to kolejne narzędzie z myślą o tym samym pomyśle.
Wszystkie te narzędzia w zestawie narzędzi oznaczają, że masz pół szansy, aby istniejący program zrobił coś wyjątkowego bez względu na ich (Twoją konkretną potrzebę) rolę urządzeń lub zamienników plików
Pozwala zorganizować konkurs, aby zobaczyć, kto może uzyskać najbardziej ekscytujący wynik, biorąc pod uwagę tylko kilka urządzeń postaci w twojej wersji LINUX.
źródło