Dlaczego / dev / null jest plikiem? Dlaczego jego funkcja nie jest zaimplementowana jako prosty program?


114

Próbuję zrozumieć pojęcie specjalnych plików w systemie Linux. Jednak posiadanie specjalnego pliku /devwydaje 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 nullzamiast 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?


20
Nawiasem mówiąc, duża część tego narzutu jest również powodem, dla którego cat foo | barjest znacznie gorzej (w skali) niż bar <foo. catjest trywialnym programem, ale nawet trywialny program generuje koszty (niektóre z nich są specyficzne dla semantyki FIFO - ponieważ programy nie seek()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/nullmoże sfałszować te operacje lub za pomocą prawdziwego pliku może je zaimplementować, ale FIFO nie zezwala na jakąkolwiek obsługę kontekstową).
Charles Duffy

13
grep blablubb file.txt 2>/dev/null && dosomethingnie mógł działać z wartością null będącą programem lub funkcją.
rexkogitans

16
Może to być pouczające (a przynajmniej poszerzające umysł), aby przeczytać o systemie operacyjnym Plan 9, aby zobaczyć, dokąd zmierza wizja „wszystko jest plikiem” - staje się trochę łatwiej dostrzec moc udostępniania zasobów w postaci pliku ścieżki, gdy zobaczysz, że system w pełni obejmuje tę koncepcję (a nie głównie / częściowo, jak robią to nowoczesne Linux / Unix).
mtraceur

25
Oprócz tego, że nikt nie wskazywał, że sterownik urządzenia działający w przestrzeni jądra jest programem z „garstką linii C” , żadna z dotychczasowych odpowiedzi nie dotyczyła przypuszczenia, że ​​„zbyt wiele programów uzyskuje dostęp do tego samego pliku” w pytaniu.
JdeBP

12
Re „jego funkcja może być zaimplementowana przez kilka linii w C”: Nie uwierzyłbyś, ale jest zaimplementowany przez kilka linii w C! Na przykład treść readfunkcji for /dev/nullskł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 :-).
Peter A. Schneider,

Odpowiedzi:


153

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.

# We don't care about log output.
$ frobify --log-file=/dev/null

# We are not interested in the compiled binary, just seeing if there are errors.
$ gcc foo.c -o /dev/null  || echo "foo.c does not compile!".

# Easy way to force an empty list of exceptions.
$ start_firewall --exception_list=/dev/null

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:

# Suppress errors, but print output.
$ grep foo * 2>/dev/null

14
Nie używasz też /dev/nulltylko 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/nullPlik nie musi odróżniać zwykłego pliku.
pabouk

Nie jestem pewien, czy rozumiem, jak trudna jest część oddzielnych przekierowań do zatapiania plików wykonywalnych. W C, wystarczy zrobić pipe, forki execvejak każdy inny rurociągów technologicznych, tylko ze zmianami do dup2połą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 /devi /procbyły traktowane jako pliki wykonywalne, pociski zostałyby zaprojektowane sposoby zrobić to tak łatwo, jak teraz przekierowujemy.
aschepler

6
@aschepler To, że nie przekierowywanie do plików wykonywalnych sink jest trudne. Chodzi o to, że pisanie aplikacji, które mogą zapisywać / odczytywać z obu plików i zerowego zlewu, byłoby bardziej skomplikowane, gdyby zerowy zlew nie był plikiem. Chyba że mówisz o świecie, w którym zamiast wszystkiego, co jest plikiem, wszystko jest plikiem wykonywalnym? Byłby to zupełnie inny model niż ten, który masz w * nix OS.
Cubic

1
@aschepler Zapomniałeś 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.
ioctl

2
@ioctl w odniesieniu do muszli; zarówno Zsh, jak i Bash przynajmniej pozwolą Ci robić takie rzeczy grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile), w wyniku czego są osobno przetwarzane errfileioutfile
Matija Nalis

62

Szczerze mówiąc, nie jest to zwykły plik jako taki ; to specjalne urządzenie postaci :

$ file /dev/null
/dev/null: character special (3/2)

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.


24
cat file | nullmiałby dużo narzutu, najpierw przy ustawianiu potoku, spawnowaniu procesu, wykonywaniu „null” w nowym procesie itp. Również nullsam zużywałby sporo CPU w pętli, odczytując bajty do bufora, który jest później właśnie odrzucone ... Implementacja /dev/nullw jądrze jest po prostu bardziej wydajna w ten sposób. A co jeśli chcesz przekazać /dev/nulljako argument zamiast przekierowania? (Możesz użyć <(...)w bashu, ale jest to nawet znacznie cięższe!)
filbranden 16.04.18

4
Gdybyś musiał połączyć się z programem o nazwie nullZamiast 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?
Mark Plotnick

5
To naprawdę kosztowna konfiguracja do prezentacji z góry. Sugerowałbym użycie /dev/zerozamiast tego.
Chrylis

20
Te przykłady są błędne. dd of=-zapisuje do pliku o nazwie -, po prostu pomiń of=zapisywanie na standardowe wyjście, ponieważ tam ddzapisuje się domyślnie. Pipowanie do falsenie działałoby, ponieważ falsenie odczytuje standardowego wejścia, więc ddzostał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.
Stéphane Chazelas,

8
Wersje AST dditp. Nawet nie zawracają sobie głowy wykonaniem syscall zapisu, gdy wykryją, że miejsce docelowe to / dev / null.
Mark Plotnick

54

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 nullprogram wiersza polecenia i /dev/nullwęzeł urządzenia. Z punktu widzenia skryptowania powłoki foo | nullprogram jest naprawdę przydatny i wygodny , a foo >/dev/nullpisanie zajmuje trochę więcej czasu i może wydawać się dziwny.

Ale oto dwa ćwiczenia:

  1. Spójrzmy prawdzie w realizacji programu nullprzy użyciu istniejących narzędzi Unix i /dev/null- łatwa: cat >/dev/null. Gotowy.

  2. Czy możesz wdrożyć /dev/nullpod względem null?

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/nulljest 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 openz danej ścieżki w obszarze nazw heirarchical, coraz odniesienia (deskryptor) do obiektu, a można readi writeitp 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/nullnajlepiej jest to rozumieć jako część tej całości.


PS Warto spojrzeć na uniksową wersję „wszystko jest plikiem” i rzeczy takie /dev/nulljak 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/nulldo 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).


8
@PeterCordes punkt odpowiedzi zaczyna się od niezrozumienia projektu. Gdyby wszyscy zrozumieli już projekt, to pytanie nie istniałoby.
OrangeDog

1
@mtraceur: Zamontować plik obrazu bez uprawnień roota? pokazuje pewne dowody, że FUSE może nie wymagać rootowania, ale nie jestem pewien.
Peter Cordes,

1
@PeterCordes RE: „wydaje się dziwny”: To nie jest przeprosiny za projekt, tylko potwierdzenie tego, jak może się wydawać, jeśli nie myślisz o wdrożeniu systemu pod nim i nie miałeś jeszcze eureki momentu na temat systemu -szerokie zalety projektowe. Próbowałem to wyjaśnić, otwierając to zdanie „z perspektywy skryptów powłoki” i nawiązując do kontrastu między tym a perspektywą systemową kilka zdań wcześniej. Po dalszej myśli, „może wydawać się dziwny”, lepiej, więc dostosuję to do tego. Z zadowoleniem przyjmuję dalsze sugestie dotyczące sformułowań, aby uczynić je bardziej zrozumiałymi, bez nadmiernego gadania.
mtraceur

2
Pierwszą rzeczą, jaką powiedziano mi jako młody inżynier w stosunku do Uniksa, było „Everything Is A File” i przysięgam, że słyszałeś stolice. Zdobycie tego pomysłu wcześnie sprawia, że ​​Unix / Linux wydaje się o wiele łatwiejszy do zrozumienia. Linux odziedziczył większość tej filozofii projektowania. Cieszę się, że ktoś o tym wspomniał.
StephenG

2
@PeterCordes, DOS „rozwiązał” problem z pisaniem, wyświetlając magiczną nazwę pliku NULw każdym katalogu, tzn. Wystarczy wpisać > NUL.
Cristian Ciupitu

15

Myślę, że /dev/nullto 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.


10
To nie tylko koszt instalacji, ale to, że każdy zapis do potoku wymaga kopiowania pamięci i przełączania kontekstu na program do czytania. (Lub przynajmniej przełącznik kontekstu, gdy bufor potoku jest pełny. A czytnik musi wykonać kolejną kopię, gdy są to readdane). 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. writeWywołanie systemowe do FD otworzyć na /dev/nullmoże wrócić od razu, nawet bez czytania żadnych danych z bufora.
Peter Cordes,

@PeterCordes, moja uwaga jest styczna, ale możliwe jest, że paradoksalnie zapisy pamięci są dziś droższe niż kiedykolwiek. 8-rdzeniowy procesor potencjalnie wykonuje 16 operacji na liczbach całkowitych w czasie zegara, podczas gdy zapis pamięci od końca do końca byłby zakończony np. 16 zegarami (procesor 4GHz, 250 MHz RAM). To współczynnik 256. Pamięć RAM dla współczesnego procesora jest jak RL02 dla procesora PDP-11, prawie jak peryferyjna jednostka pamięci! :) Oczywiście nie tak proste, ale wszystko, co trafi do pamięci podręcznej, zostanie zapisane, a bezużyteczne zapisy pozbawiłyby inne obliczenia zawsze ważnej przestrzeni pamięci podręcznej.
kkm

@kkm: Tak, marnowanie około 2x 128kB śladu pamięci podręcznej L3 na bufor potoku w jądrze i bufor odczytu w nullprogramie by zasysało, ale większość procesorów wielordzeniowych nie działa cały czas z zajętymi rdzeniami, więc Czas procesora do uruchomienia nullprogramu 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 ...
Peter Cordes

.... Ale twoje obliczenia pamięci są bardzo błędne. Współczesne procesory mają wiele równoległości pamięci, więc nawet jeśli opóźnienie do pamięci DRAM jest czymś w rodzaju 200-400 rdzeni zegarowych i L3> 40, przepustowość wynosi ~ 8 bajtów / zegar. (Zaskakujące jest to, że jedno-wątkowa przepustowość do L3 lub DRAM jest gorsza na wielordzeniowym Xeon z pamięcią czterokanałową w porównaniu z czterordzeniowym komputerem stacjonarnym, ponieważ jest ograniczona przez maksymalną współbieżność żądań, które jeden rdzeń może utrzymywać w ruchu. Bandwidth = max_concurrency / latency: Dlaczego Skylake jest o wiele lepszy niż Broadwell-E w przypadku przepustowości pamięci jednowątkowej? )
Peter Cordes

... Zobacz także 7-cpu.com/cpu/Haswell.html dla liczb Haswella porównujących czterordzeniowy z 18-rdzeniowym. W każdym razie tak, współczesne procesory mogą wykonywać absurdalnie dużo pracy na zegar, jeśli nie utkną w oczekiwaniu na pamięć. Twoje liczby wydają się być tylko 2 operacjami ALU na zegar, jak może Pentium z 1993 r. Lub nowoczesny niskiej klasy ARM z podwójnym wydaniem. Ryzen lub Haswell potencjalnie wykonuje 4 operacje ALU na liczbach całkowitych + 2 operacje na pamięć na rdzeń na zegar lub znacznie więcej z SIMD. np. Skylake-AVX512 ma (na rdzeń) przepustowość 2 na zegar na vpaddd zmm: 16 32-bitowych elementach na instrukcję.
Peter Cordes,

7

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:

#include <unistd.h>
char buf[1024*1024];
int main() {
        while (read(0, buf, sizeof(buf)) > 0);
}

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

% time dd if=/dev/zero bs=1M count=5000 |  ./nullread
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 9,33127 s, 562 MB/s
dd if=/dev/zero bs=1M count=5000  0,06s user 5,66s system 61% cpu 9,340 total
./nullread  0,02s user 3,90s system 41% cpu 9,337 total

podczas gdy ze standardowym /dev/nullprzekierowaniem 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.):

% time dd if=/dev/zero bs=1M count=5000 > /dev/null
5242880000 bytes (5,2 GB, 4,9 GiB) copied, 1,08947 s, 4,8 GB/s
dd if=/dev/zero bs=1M count=5000 > /dev/null  0,01s user 1,08s system 99% cpu 1,094 total

(powinien to być komentarz, ale jest na to za duży i byłby całkowicie nieczytelny)


Na jakim sprzęcie testowałeś? 4,8 GB / s jest dość niskie w porównaniu do 23 GB / s, które dostaję na Skylake i7-6700k (DDR4-2666, ale bufor powinien pozostać gorący w pamięci podręcznej L3. Tak więc dużą część kosztów stanowią kosztowne wywołania systemowe, które są drogie w przypadku Spectre + Włączone łagodzenie załamania. Boli podwójnie dla pipingu, ponieważ bufory rur są mniejsze niż 1M, więc to więcej wywołań systemowych zapisu / odczytu. Prawie 10-krotna różnica perf jest gorsza niż się spodziewałem. W moim systemie Skylake jest to 23 GB / s vs. 3.3GB / s , bieganie x86-64 Linux 4.15.8-1-Arch, więc to jest współczynnik 6,8 Wow, wywołania systemowe są. drogiego nOW)
Peter Cordes

1
@PeterCordes 3GB / s z buforami potoków 64k sugeruje 2x 103124 wywołań systemowych na sekundę ... i ta liczba przełączników kontekstu, heh. Na jednostce centralnej serwera, z 200000 wywołań systemowych na sekundę, można oczekiwać ~ 8% narzutu z PTI, ponieważ zestaw roboczy jest bardzo mały. (Wykres, do którego się odnoszę, nie obejmuje optymalizacji PCID, ale może nie jest to tak znaczące w przypadku małych zestawów roboczych). Więc nie jestem pewien, czy PTI ma tam duży wpływ? brendangregg.com/blog/2018-02-09/…
sourcejedi

1
Och, ciekawe, więc jest to Silvermont z 2 MB pamięci podręcznej L2 , więc twój ddbufor + 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 z stracemoim pulpitem writei readzwracamy 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)
Peter Cordes,

1
@sourcejedi: Przy włączonej funkcji ograniczania widma, koszt syscallnatychmiastowego powrotu ENOSYSwynosi ~ 1800 cykli na Skylake z włączoną funkcją ograniczania widma, przy czym większość z nich wrmsrunieważ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 dotykania dużo pamięci, łagodzenie Odwilż jest znacząca, too. Hugepages powinien pomóc (należy ponownie załadować mniej wpisów TLB).
Peter Cordes

1
@PeterCordes W jednordzeniowym systemie unix z pewnością zobaczylibyśmy 1 przełącznik kontekstu na 64 KB lub cokolwiek to byłby Twój bufor potoku, i to by bolało ... faktycznie widzę też [taką samą liczbę przełączników kontekstu z 2 rdzeniami procesora] ( unix.stackexchange.com/questions/439260/… ); musi także liczyć cykl uśpienia / budzenia dla każdego 64k jako przełącznik kontekstu (do nominalnego „bezczynności”). Utrzymywanie procesów potokowych na tym samym procesorze działało faktycznie ponad dwa razy szybciej.
sourcejedi

6

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:

$ echo -e 'foo\nbar\nbaz' | grep foo
foo

Korzystając z zastępowania procesów przez Basha, możemy osiągnąć to samo w bardziej okrągły sposób:

$ grep foo <(echo -e 'foo\nbar\nbaz')
foo

Wymień grepna echoi możemy zobaczyć pod okładkami:

$ echo foo <(echo -e 'foo\nbar\nbaz')
foo /dev/fd/63

<(...)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/fdmagiczny 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 mkfifobyło stworzenie nazwanego potoku, który pojawiłby się lsi wszystko, tak jak zwykły plik:

$ mkfifo foofifo
$ ls -l foofifo 
prw-rw-r-- 1 indigo indigo 0 Apr 19 22:01 foofifo
$ grep foo foofifo

Gdzie indziej:

$ echo -e 'foo\nbar\nbaz' > foofifo

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.


@Lyle Tak? Dlaczego więc drukowane jest echo /dev/fd/63?
Phil Frost

Hm Słuszna uwaga. Cóż, jest to realizowane przez powłoki, więc być może twoja powłoka różni się od starej powłoki Bourne'a, z którą dorastałem.
Lyle

Jedną różnicą jest to, że echo nie odczytuje ze standardowego wejścia, podczas gdy grep tak, ale nie mogę myśleć, skąd powłoka wiedziałaby, że zanim je wykona.
Lyle

1
A strace to wyjaśnia: dla mnie. masz dokładnie rację, dzięki bash. Konstrukcja „<(...)” robi coś zupełnie innego niż <nazwa pliku. Hm Nauczyłem się czegoś.
Lyle

0

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/nullpewnoś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.


To brzmi jak nonsens. Pisanie bezbłędnego sterownika jądra nie jest łatwiejsze niż pisanie bezbłędnego programu, który czyta + odrzuca standardowe wejście. To nie musi być setuid lub cokolwiek, więc dla obu /dev/nulllub 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ą lseeksię /dev/nulllub otwarte wiele razy z tego samego procesu lub co IDK. Lub wywołać /bin/nullw dziwnym środowisku, czy cokolwiek innego.
Peter Cordes

0

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/nullpołą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.


Może powinienem to precyzyjniej sformułować. Miałem na myśli, dlaczego zaimplementowałem to jako urządzenie char zamiast programu. Tak czy inaczej byłoby kilka linii, ale wdrożenie programu byłoby zdecydowanie prostsze. Jak wskazały inne odpowiedzi, jest z tego całkiem sporo korzyści; szef wydajności i przenośności.
Ankur S

Pewnie. Właśnie dodałem tę odpowiedź, ponieważ oglądanie faktycznej implementacji było zabawne (sam odkryłem ją niedawno), ale prawdziwy powód jest tym, co inni zauważyli.
Matthieu Moy,

Ja też! Niedawno zacząłem uczyć się urządzeń w systemie Linux i odpowiedzi były dość pouczające
Ankur S

-2

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.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.