Perché / dev / null è un file? Perché la sua funzione non è implementata come un semplice programma?


114

Sto cercando di capire il concetto di file speciali su Linux. Tuttavia, avere un file speciale in /devsembra sciocco quando la sua funzione potrebbe essere implementata da una manciata di righe in C a mia conoscenza.

Inoltre potresti usarlo più o meno allo stesso modo, ad esempio eseguendo il piping nullinvece di reindirizzare /dev/null. C'è un motivo specifico per averlo come file? Fare un file non causa molti altri problemi come troppi programmi che accedono allo stesso file?


20
Per inciso, gran parte di questo sovraccarico è anche il motivo per cui cat foo | barè molto peggio (su larga scala) di bar <foo. catè un programma banale, ma anche un programma banale crea costi (alcuni specifici per la semantica FIFO - perché i programmi non possono essere utilizzati seek()all'interno di FIFO, ad esempio un programma che potrebbe essere implementato in modo efficiente con la ricerca può finire per fare operazioni molto più costose quando viene data una pipeline; con un dispositivo a caratteri come /dev/nullquesto può falsare quelle operazioni o con un file reale può implementarle, ma un FIFO non consente alcun tipo di gestione contestuale).
Charles Duffy,

13
grep blablubb file.txt 2>/dev/null && dosomethingnon può funzionare con null essendo un programma o una funzione.
rexkogitans,

16
Potresti trovare illuminante (o almeno in espansione mentale) leggere sul sistema operativo Plan 9 per vedere dove stava andando la visione "tutto è un file" - diventa un po 'più facile vedere il potere di avere risorse disponibili come file percorsi una volta che vedi un sistema che abbraccia completamente il concetto (piuttosto che principalmente / parzialmente, come fanno i moderni Linux / Unix).
mtraceur,

25
Oltre a nessuno che sottolinea che un driver di dispositivo in esecuzione nello spazio del kernel è un programma con "una manciata di linee di C" , nessuna delle risposte finora ha effettivamente affrontato la supposizione di "troppi programmi che accedono allo stesso file" nella domanda.
JdeBP,

12
Ri "la sua funzione potrebbe essere implementata da una manciata di righe in C": non ci crederesti, ma è implementata da una manciata di righe in C! Ad esempio, il corpo della readfunzione per /dev/nullconsiste in uno "return 0" (nel senso che non fa nulla e, suppongo, si traduce in un EOF): (Da 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; }(Oh, ho appena visto che @JdeBP ha già sottolineato questo punto. Comunque, ecco l'illustrazione :-).
Peter A. Schneider,

Risposte:


153

Oltre ai vantaggi in termini di prestazioni derivanti dall'uso di un dispositivo speciale per i personaggi, il vantaggio principale è la modularità . / dev / null può essere utilizzato in quasi tutti i contesti in cui è previsto un file, non solo nelle pipeline di shell. Considera i programmi che accettano i file come parametri della riga di comando.

# 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

Questi sono tutti casi in cui l'utilizzo di un programma come sorgente o sink sarebbe estremamente complicato. Anche nel caso della pipeline della shell, stdout e stderr possono essere reindirizzati ai file in modo indipendente, cosa difficile da fare con gli eseguibili come sink:

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

14
Inoltre non si usa /dev/nullsolo nei comandi della shell. È possibile utilizzarlo in altri parametri forniti a un software, ad esempio nei file di configurazione. --- Fort questo software è molto conveniente. Non è necessario fare la differenza tra /dev/nulle un file normale.
pabouk,

Non sono sicuro di capire che la parte sui reindirizzamenti separati per affondare gli eseguibili è difficile. In C, fai semplicemente un pipe, forke execvecome qualsiasi altro processo di piping, solo con le modifiche alle dup2chiamate che hanno impostato le connessioni, giusto? È vero che la maggior parte delle shell non offre i modi più belli per farlo, ma presumibilmente se non avessimo così tanti pattern dispositivo-come-file e la maggior parte delle cose dentro /deve /procfossero trattati come eseguibili, le shell sarebbero state progettate con modi per farlo con la stessa facilità con cui reindirizziamo ora.
aschepler

6
@aschepler È difficile non reindirizzare verso gli eseguibili. È che scrivere applicazioni in grado di scrivere / leggere da entrambi i file e il sink null sarebbe più complicato se il sink null non fosse un file. A meno che tu non stia parlando di un mondo in cui invece che tutto sia un file, tutto è un eseguibile? Sarebbe un modello molto diverso da quello che hai in * nix OS.
Cubico,

1
@aschepler Hai dimenticato wait4! Hai ragione, è certamente possibile reindirizzare stdout e stderr a diversi programmi usando le API POSIX e potrebbe essere possibile inventare una sintassi di shell intelligente per reindirizzare stdout e stderr a diversi comandi. Tuttavia, non sono a conoscenza di nessuna di queste shell in questo momento, e il punto più grande è che / dev / null si adatta perfettamente agli strumenti esistenti (che funzionano in gran parte con i file), e / bin / null non lo farebbe. Potremmo anche immaginare alcune API IO che rendono facile per gcc l'output (sicuro!) Su un programma come su un file, ma non è questa la situazione in cui ci troviamo.
ioctl

2
@ioctl riguardo alle shell; sia zsh che bash almeno ti permetteranno di fare cose del genere grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile), il risultato verrà elaborato separatamente errfileeoutfile
Matija Nalis,

62

In tutta onestà, non è un file normale di per sé; è un dispositivo speciale per i personaggi :

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

Funzionando come un dispositivo piuttosto che come un file o programma significa che è un'operazione più semplice reindirizzare l'input o l'output da esso, in quanto può essere collegato a qualsiasi descrittore di file, incluso input / output / errore standard.


24
cat file | nullavrebbe un sacco di sovraccarico, prima di impostare una pipe, generare un processo, eseguire "null" nel nuovo processo, ecc. Inoltre, nulluserebbe un bel po 'di CPU in un ciclo per leggere byte in un buffer che è più tardi appena scartato ... L'implementazione /dev/nullnel kernel è solo più efficiente in questo modo. Inoltre, cosa succede se si desidera passare /dev/nullcome argomento, anziché come reindirizzamento? (Potresti usare <(...)in bash, ma è ancora più pesante!)
filbranden,

4
Se dovessi reindirizzare a un programma chiamato nullInvece di utilizzare il reindirizzamento a /dev/null, ci sarebbe un modo semplice e chiaro per dire alla shell di eseguire un programma mentre invia solo il suo standard a null?
Mark Plotnick,

5
Questa è una configurazione davvero costosa per una dimostrazione ambientale. Suggerirei /dev/zeroinvece di utilizzare .
Chrylis

20
Questi esempi sono sbagliati. dd of=-scrive su un file chiamato -, basta omettere of=di scrivere su stdout poiché è lì che ddscrive di default. Il piping falsenon funzionerebbe perché falsenon legge il suo stdin, quindi ddverrebbe ucciso con un SIGPIPE. Per un comando che scarta il suo input è possibile utilizzare ... cat > /dev/null. Anche il confronto che probabilmente è irrilevante come il collo della bottiglia sarebbe probabilmente la generazione casuale di numeri qui.
Stéphane Chazelas,

8
Le versioni AST di ddecc. Non si disturbano nemmeno a scrivere un syscall quando rilevano che la destinazione è / dev / null.
Mark Plotnick,

54

Sospetto che il perché abbia molto a che fare con la visione / il design che ha plasmato Unix (e di conseguenza Linux) e i vantaggi che ne derivano.

Senza dubbio c'è un vantaggio non trascurabile in termini di prestazioni nel non avviare un processo aggiuntivo, ma penso che ci sia molto altro: Early Unix aveva una metafora "tutto è un file", che ha un vantaggio non ovvio ma elegante se si guarda dal punto di vista del sistema, piuttosto che dal punto di vista dello script di shell.

Supponi di avere il tuo nullprogramma da riga di comando e /dev/nullil nodo del dispositivo. Dal punto di vista della shell-scripting, il foo | nullprogramma è davvero utile e conveniente , e foo >/dev/nullrichiede un po 'più di tempo per scrivere e può sembrare strano.

Ma ecco due esercizi:

  1. Facciamo l'attuazione del programma nullutilizzando gli strumenti Unix esistenti e /dev/null- facile: cat >/dev/null. Fatto.

  2. Puoi implementare /dev/nullin termini di null?

Hai perfettamente ragione sul fatto che il codice C per scartare l'input sia banale, quindi potrebbe non essere ancora chiaro il motivo per cui è utile avere un file virtuale disponibile per l'attività.

Considera: quasi tutti i linguaggi di programmazione hanno già bisogno di lavorare con file, descrittori di file e percorsi di file, perché facevano parte del paradigma "tutto è un file" di Unix sin dall'inizio.

Se tutto ciò che hai sono programmi che scrivono su stdout, beh, al programma non importa se li reindirizzi in un file virtuale che ingoia tutte le scritture o una pipe in un programma che ingoia tutte le scritture.

Ora, se hai programmi che prendono percorsi di file per leggere o scrivere dati (cosa che la maggior parte dei programmi fa) - e vuoi aggiungere la funzionalità "input vuoto" o "scartare questo output" a quei programmi - beh, con /dev/nullquello viene gratis.

Si noti che l'eleganza di ciò è che riduce la complessità del codice di tutti i programmi coinvolti - per ogni caso d'uso comune ma speciale che il sistema può fornire come "file" con un "nome file" effettivo, il codice può evitare l'aggiunta di comandi personalizzati -line opzioni e percorsi di codice personalizzati da gestire.

Una buona ingegneria del software dipende spesso dalla ricerca di metafore buone o "naturali" per astrarre qualche elemento di un problema in un modo che diventa più facile da pensare ma rimane flessibile , in modo da poter risolvere sostanzialmente la stessa gamma di problemi di livello superiore senza dover dedicare costantemente tempo ed energie mentali alla reimplementazione di soluzioni agli stessi problemi di livello inferiore.

"Tutto è un file" sembra essere una tale metafora per accedere alle risorse: si chiama openun determinato percorso in uno spazio dei nomi gerarchico, si ottiene un riferimento (descrittore di file) all'oggetto e si può reade write, ecc., Sui descrittori di file. Il tuo stdin / stdout / stderr sono anche descrittori di file che sono appena stati pre-aperti per te. Le tue pipe sono solo file e descrittori di file e il reindirizzamento dei file ti consente di incollare tutti questi pezzi insieme.

Unix ha avuto successo tanto quanto in parte grazie al modo in cui queste astrazioni hanno funzionato insieme, ed /dev/nullè meglio compreso come parte di tutto.


PS Vale la pena guardare la versione Unix di "tutto è un file" e cose come /dev/nulli primi passi verso una generalizzazione più flessibile e potente della metafora che è stata implementata in molti sistemi che seguirono.

Ad esempio, in Unix oggetti simili /dev/nulla file simili dovevano essere implementati nel kernel stesso, ma si scopre che è abbastanza utile esporre funzionalità in forma di file / cartella che da allora sono stati creati più sistemi che forniscono un modo per i programmi fare quello.

Uno dei primi è stato il sistema operativo Plan 9, realizzato da alcune delle stesse persone che hanno creato Unix. Più tardi, GNU Hurd ha fatto qualcosa di simile con i suoi "traduttori". Nel frattempo, Linux ha finito per ottenere FUSE (che ormai si è diffuso anche agli altri sistemi tradizionali).


8
@PeterCordes il punto della risposta parte da una posizione di non comprensione del design. Se tutti avessero già capito il design, questa domanda non esisterebbe.
OrangeDog,

1
@mtraceur: montare un file immagine senza il permesso di root? mostra alcune prove che FUSE potrebbe non richiedere root, ma non ne sono sicuro.
Peter Cordes,

1
@PeterCordes RE: "sembra strano": non è una scusa per la progettazione, solo un riconoscimento di come può sembrare se non stai pensando all'implementazione del sistema sotto di esso e non hai ancora avuto il momento eureka sul sistema vantaggi di progettazione in tutto il mondo. Ho cercato di chiarirlo aprendo quella frase con "da una prospettiva di scripting di shell", e alludendo al contrasto tra quella rispetto a una prospettiva di sistema di un paio di frasi prima. A pensarci ancora, "può sembrare strano" è meglio, quindi lo modificherò. Accolgo con favore ulteriori suggerimenti di formulazione per renderlo più chiaro senza renderlo troppo dettagliato.
mtraceur,

2
La prima cosa che mi è stata raccontata da giovane ingegnere in relazione a Unix è stata "Tutto è un file" e giuro che potresti sentire le capitali. E prendere presto in considerazione questa idea rende Unix / Linux molto più facile da capire. Linux ha ereditato gran parte di quella filosofia di design. Sono contento che qualcuno l'abbia menzionato.
StephenG

2
@PeterCordes, DOS "risolto" il problema di battitura facendo NULapparire il nome del file magico in ogni directory, cioè tutto ciò che devi scrivere è > NUL.
Cristian Ciupitu,

15

Penso che /dev/nullsia un dispositivo a caratteri (che si comporta come un normale file) invece di un programma per motivi di prestazioni .

Se fosse un programma richiederebbe il caricamento, l'avvio, la pianificazione, l'esecuzione e successivamente l'arresto e lo scaricamento del programma. Il semplice programma C che stai descrivendo ovviamente non consumerebbe molte risorse, ma penso che faccia una differenza significativa quando si considera un gran numero (diciamo milioni) di azioni di reindirizzamento / piping poiché le operazioni di gestione dei processi sono costose su larga scala come coinvolgono cambi di contesto.

Un altro presupposto: il piping in un programma richiede che la memoria sia allocata dal programma ricevente (anche se viene scartata subito dopo). Quindi se installi nello strumento hai il doppio consumo di memoria, una volta sul programma di invio e di nuovo sul programma di ricezione.


10
Non è solo il costo di installazione, è che ogni scrittura in una pipe richiede la copia della memoria e un cambio di contesto al programma di lettura. (O almeno un cambio di contesto quando il buffer delle pipe è pieno. E il lettore deve fare un'altra copia quando sono readi dati). Questo non è trascurabile su un PDP-11 single-core in cui Unix è stato progettato! La larghezza di banda / copia della memoria oggi è molto più economica di quanto non fosse allora. Una writechiamata di sistema a un FD aperto /dev/nullpuò tornare immediatamente senza nemmeno leggere alcun dato dal buffer.
Peter Cordes,

@PeterCordes, la mia nota è tangenziale, ma è possibile che, paradossalmente, le scritture di memoria oggi siano più costose che mai. Una CPU a 8 core esegue potenzialmente 16 operazioni intere in un tempo di clock, mentre una scrittura di memoria end-to-end si completerebbe ad esempio in 16 clock (CPU da 4 GHz, RAM da 250 MHz). Questo è il fattore 256. La RAM per la CPU moderna è come un RL02 per la CPU PDP-11, quasi come un'unità di archiviazione periferica! :) Non così semplice, naturalmente, ma tutto ciò che colpisce la cache verrà scritto e le scritture inutili priverebbero altri calcoli del sempre importante spazio della cache.
kkm

@kkm: Sì, sprecare circa 2x 128 kB di footprint della cache L3 su un buffer di pipe nel kernel e un buffer di lettura nel nullprogramma farebbe schifo, ma la maggior parte delle CPU multi-core non funzionano con tutti i core sempre occupati, quindi il Il tempo della CPU per eseguire il nullprogramma è per lo più gratuito. Su un sistema con tutti i core collegati, le tubazioni inutili sono un grosso problema. Ma no, un buffer "caldo" può essere riscritto molte volte senza essere scaricato nella RAM, quindi siamo per lo più in competizione per la larghezza di banda L3, non per la cache. Non eccezionale, specialmente su un sistema SMT (hyperthreading) in cui altri core logici sullo stesso fisico sono in competizione ...
Peter Cordes,

.... Ma il tuo calcolo della memoria è molto imperfetto. Le moderne CPU hanno un sacco di parallelismo della memoria, quindi anche se la latenza alla DRAM è qualcosa come 200-400 cicli di clock core e L3> 40, la larghezza di banda è ~ 8 byte / clock. (Sorprendentemente, la larghezza di banda a thread singolo a L3 o DRAM è peggiore su un Xeon a molti core con memoria quad-channel rispetto a un desktop quad-core, perché è limitato dalla concorrenza massima di richieste che un core può mantenere in volo. Bandwidth = max_concurrency / latency: Perché Skylake è molto meglio di Broadwell-E per il throughput di memoria a thread singolo? )
Peter Cordes,

... Vedi anche 7-cpu.com/cpu/Haswell.html per i numeri Haswell che confrontano quad-core e 18-core. Ad ogni modo, sì, le moderne CPU possono svolgere una quantità ridicola di lavoro per clock, se non sono bloccati in attesa di memoria. I tuoi numeri sembrano essere solo 2 operazioni ALU per orologio, come forse un Pentium del 1993 o un moderno ARM di fascia bassa a doppia emissione di fascia bassa. Un Ryzen o Haswell esegue potenzialmente 4 operazioni ALU intere scalari + 2 operazioni di memoria per core per clock, o molto di più con SIMD. ad es. Skylake-AVX512 ha (per core) un throughput di 2 per clock attivo vpaddd zmm: 16 elementi a 32 bit per istruzione.
Peter Cordes,

7

Oltre a "tutto è un file" e quindi facilità d'uso ovunque su cui si basano la maggior parte delle altre risposte, c'è anche un problema di prestazioni come menzionato da @ user5626466.

Per mostrare in pratica, creeremo un semplice programma chiamato nullread.c:

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

e compilarlo con gcc -O2 -Wall -W nullread.c -o nullread

(Nota: non possiamo usare lseek (2) sui tubi, quindi l'unico modo per drenare il tubo è leggere da esso fino a quando non è vuoto).

% 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

considerando che con /dev/nullil reindirizzamento dei file standard otteniamo velocità molto migliori (a causa dei fatti menzionati: meno cambio di contesto, kernel semplicemente ignorando i dati invece di copiarli ecc.):

% 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

(questo dovrebbe essere un commento lì, ma è troppo grande per questo e sarebbe completamente illeggibile)


Su quale hardware hai provato? 4.8GB / s è piuttosto basso rispetto ai 23GB / s che ottengo su uno Skylake i7-6700k (DDR4-2666, ma il buffer dovrebbe rimanere caldo nella cache L3. Quindi una buona parte del costo è che le chiamate di sistema sono costose con Spectre + Attenuazione della fusione attivata. Ciò fa doppiamente male alle tubazioni, perché i buffer delle tubazioni sono più piccoli di 1 milione, quindi è più chiamate di sistema in scrittura / lettura. Quasi 10 volte la differenza di perf è peggio di quanto mi aspettassi, sul mio sistema Skylake è di 23 GB / s vs. 3.3GB / s , con x86-64 Linux 4.15.8-1-ARCH, quindi questo è un fattore di 6.8. Caspita, le chiamate di sistema ora sono costose )
Peter Cordes

1
@PeterCordes 3GB / s con buffer di tubo 64k suggerisce 2x 103124 syscalls al secondo ... e quel numero di switch di contesto, eh. Su un server CPU, con 200000 syscalls al secondo, si potrebbe prevedere un sovraccarico del 8% circa dal PTI, poiché il set di lavoro è molto limitato. (Il grafico a cui mi riferisco non include l'ottimizzazione PCID, ma forse non è così significativo per piccoli gruppi di lavoro). Quindi non sono sicuro che la PTI abbia un grande impatto lì? brendangregg.com/blog/2018-02-09/…
sourcejedi

1
Oh interessante, quindi è un Silvermont con 2 MB di cache L2 , quindi il tuo ddbuffer + buffer di ricezione non si adatta; probabilmente hai a che fare con la larghezza di banda della memoria anziché con la larghezza di banda della cache dell'ultimo livello. È possibile ottenere una larghezza di banda migliore con buffer da 512k o addirittura con buffer da 64k. (Secondo straceil mio desktop, writee sto readrestituendo 1048576, quindi penso che ciò significhi che stiamo pagando all'utente <-> costo del kernel di invalidazione TLB + flush della previsione del ramo una volta per MiB, non per 64k, @sourcejedi. È Spectre mitigazione che ha il maggior costo, credo)
Peter Cordes,

1
@sourcejedi: Con l'attenuazione dello Spettro abilitata, il costo di una syscallche ritorna subito con ENOSYS~ 1800 cicli su Skylake con l'attenuazione dello Spettro abilitata, la maggior parte di essa è quella wrmsrche invalida la BPU, secondo i test di @ BeeOnRope . Con la mitigazione disabilitata, il tempo di andata e ritorno utente-> kernel-> utente è di ~ 160 cicli. Ma se siete un sacco toccanti della memoria, la mitigazione Meltdown è significativo, anche. Hugepages dovrebbe aiutare (è necessario ricaricare meno voci TLB).
Peter Cordes,

1
@PeterCordes Su un sistema unix single-core, vedremmo sicuramente 1 switch di contesto per 64 KB, o qualunque sia il tuo buffer di pipe, e ciò danneggerebbe ... in realtà vedo anche lo stesso [numero di switch di contesto con 2 core CPU] ( unix.stackexchange.com/questions/439260/… ); deve anche contare un ciclo sleep / wake per ogni 64k come interruttore di contesto (per un "processo inattivo" nominale). Mantenere i processi della pipeline sulla stessa cpu ha funzionato in realtà più del doppio della velocità.
sourcejedi

6

La tua domanda è posta come se qualcosa potesse essere guadagnato forse nella semplicità usando un programma null al posto di un file. Forse possiamo liberarci del concetto di "file magici" e invece avere solo "pipe ordinarie".

Ma considera, anche una pipe è un file . Normalmente non hanno un nome e quindi possono essere manipolati solo attraverso i loro descrittori di file.

Considera questo esempio in qualche modo inventato:

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

Usando la sostituzione del processo di Bash possiamo ottenere la stessa cosa in un modo più indiretto:

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

Sostituisci il grepper echoe possiamo vedere sotto le copertine:

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

Il <(...)costrutto è appena sostituito con un nome file, e grep pensa che stia aprendo qualsiasi vecchio file, sembra solo che sia chiamato /dev/fd/63. Qui, /dev/fdc'è una directory magica che crea le named pipe per ogni descrittore di file posseduto dal file che vi accede.

Potremmo renderlo meno magico con la mkfifocreazione di una pipe denominata che si presenta in lse tutto, proprio come un normale file:

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

Altrove:

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

ed ecco, grep produrrà foo.

Penso che una volta che ti rendi conto che pipe e file regolari e file speciali come / dev / null sono tutti solo file, è evidente che implementare un programma null è più complesso. Il kernel deve gestire le scritture in un file in entrambi i modi, ma nel caso di / dev / null può semplicemente rilasciare le scritture sul pavimento, mentre con una pipe deve effettivamente trasferire i byte su un altro programma, che quindi deve li leggo davvero.


@Lyle Sì? Allora perché l'eco stampa /dev/fd/63?
Phil Frost,

Hm. Buon punto. Bene, questo è implementato dalle shell, quindi forse la tua shell è diversa dalla vecchia shell Bourne con cui sono cresciuto.
Lyle,

Una differenza è che l'eco non legge dallo stdin, mentre grep lo fa, ma non riesco a pensare a come la shell lo saprebbe prima di eseguirli.
Lyle,

1
E Strace rende questo più chiaro: per me. hai esattamente ragione, con bash. Il costrutto '<(...)' sta facendo qualcosa di molto diverso da <nomefile. Hm. Ho imparato qualcosa
Lyle,

0

Direi che esiste un problema di sicurezza al di là dei paradigmi storici e delle prestazioni. Limitare il numero di programmi con credenziali di esecuzione privilegiate, non importa quanto semplice, è un principio fondamentale della sicurezza del sistema. Una sostituzione /dev/nullsarebbe certamente richiesta per avere tali privilegi dovuti all'uso da parte dei servizi di sistema. I moderni quadri di sicurezza svolgono un ottimo lavoro nella prevenzione degli exploit, ma non sono infallibili. Un dispositivo guidato dal kernel a cui si accede come file è molto più difficile da sfruttare.


Sembra una sciocchezza. Scrivere un driver del kernel privo di bug non è più facile che scrivere un programma privo di bug che legge + scarta il suo stdin. Non ha bisogno di essere setuid o niente, quindi per entrambi /dev/nullo un programma di input-scartando proposto, il vettore di attacco sarebbe lo stesso: ottenere uno script o un programma che viene eseguito come root per fare qualcosa di strano (come cercare di lseekin /dev/nullo aperto più volte dallo stesso processo, o IDK cosa. O invocare /bin/nullcon un ambiente strano, o altro).
Peter Cordes,

0

Come altri hanno già sottolineato, /dev/null è un programma composto da una manciata di righe di codice. È solo che queste righe di codice fanno parte del kernel.

Per renderlo più chiaro, ecco l'implementazione di Linux: un dispositivo a caratteri chiama funzioni quando viene letto o scritto. Scrivere sulle /dev/nullchiamate write_null , durante la lettura delle chiamate read_null , registrato qui .

Letteralmente una manciata di righe di codice: queste funzioni non fanno nulla. Avresti bisogno di più righe di codice rispetto alle dita delle mani solo se conti funzioni diverse da leggere e scrivere.


Forse avrei dovuto dirlo in modo più preciso. Intendevo perché implementarlo come dispositivo char invece che come programma. Sarebbe qualche riga in entrambi i modi, ma l'attuazione del programma sarebbe decisamente più semplice. Come hanno sottolineato le altre risposte, ci sono alcuni vantaggi in questo; il capo di efficienza e portabilità fra loro.
Ankur S,

Sicuro. Ho appena aggiunto questa risposta perché vedere l'implementazione effettiva è stato divertente (l'ho scoperto di recente da solo), ma la vera ragione è ciò che gli altri hanno effettivamente sottolineato.
Matthieu Moy,

Anche a me! Di recente ho iniziato ad apprendere dispositivi su Linux e le risposte sono state piuttosto istruttive
Ankur S

-2

Spero che tu sia anche a conoscenza di / dev / chargen / dev / zero e di altri come loro, incluso / dev / null.

LINUX / UNIX ne ha messi a disposizione alcuni in modo che le persone possano fare buon uso di FRAGMEnTS CODICE SCRITTO BEN.

Chargen è progettato per generare un modello specifico e ripetuto di caratteri: è abbastanza veloce e spingerebbe i limiti dei dispositivi seriali e aiuterebbe a eseguire il debug dei protocolli seriali che erano stati scritti e fallivano alcuni test o altro.

Zero è progettato per popolare un file esistente o generare molti zero

/ dev / null è solo un altro strumento con la stessa idea in mente.

Tutti questi strumenti nel tuo toolkit significa che hai la metà delle possibilità di far fare a un programma esistente qualcosa di unico, indipendentemente dal loro (tuo bisogno specifico) come dispositivo o sostituzione di file

Consente di impostare un concorso per vedere chi può produrre il risultato più emozionante dati solo i pochi dispositivi personaggio nella tua versione di LINUX.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.