Ich versuche das Konzept spezieller Dateien unter Linux zu verstehen. Ein spezielles File in zu haben, /dev
erscheint mir jedoch albern, wenn die Funktion meines Wissens durch ein paar Zeilen in C implementiert werden könnte.
Darüber hinaus können Sie es auf die gleiche Art und Weise verwenden, z. B. null
umleiten, anstatt umzuleiten /dev/null
. Gibt es einen bestimmten Grund dafür, es als Datei zu haben? Verursacht das Erstellen einer Datei nicht viele andere Probleme, z. B. zu viele Programme, die auf dieselbe Datei zugreifen?
files
devices
executable
null
Ankur S
quelle
quelle
cat foo | bar
viel schlimmer (im Maßstab) alsbar <foo
.cat
ist ein triviales Programm, aber selbst ein triviales Programm verursacht Kosten (von denen einige spezifisch für die FIFO-Semantik sind), da Programme nichtseek()
in FIFOs implementiert werden können Wenn eine Pipeline angegeben wird, kann sie mit einem Zeichengerät wie/dev/null
diesem diese Operationen vortäuschen oder mit einer echten Datei implementieren, aber ein FIFO lässt keine kontextbezogene Behandlung zu.grep blablubb file.txt 2>/dev/null && dosomething
konnte nicht funktionieren, wenn null ein Programm oder eine Funktion war.read
Funktion für/dev/null
besteht zum Beispiel aus einer "return 0" (was bedeutet, dass sie nichts tut und vermutlich zu einem EOF führt): (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; }
(Oh, ich sehe nur, dass @JdeBP diesen Punkt bereits gemacht hat. Wie auch immer, hier ist die Abbildung :-).Antworten:
Neben den Leistungsvorteilen der Verwendung eines charakterspezifischen Geräts liegt der Hauptvorteil in der Modularität . / dev / null kann in fast jedem Kontext verwendet werden, in dem eine Datei erwartet wird, nicht nur in Shell-Pipelines. Betrachten Sie Programme, die Dateien als Befehlszeilenparameter akzeptieren.
Dies sind alle Fälle, in denen die Verwendung eines Programms als Quelle oder Senke äußerst umständlich wäre. Sogar im Fall einer Shell-Pipeline können stdout und stderr unabhängig voneinander in Dateien umgeleitet werden, was mit ausführbaren Dateien als Senken schwierig zu tun ist:
quelle
/dev/null
nur in der Shell Befehle. Sie können es in anderen Parametern verwenden, die für eine Software bereitgestellt werden, z. B. in Konfigurationsdateien. --- Für die Software ist dies sehr praktisch. Es muss keinen Unterschied zwischen/dev/null
einer normalen Datei und einer normalen Datei machen.pipe
,fork
undexecve
wie bei jedem anderen Prozess-Piping, nur mit Änderungen an dendup2
Aufrufen aus, die die Verbindungen herstellen, richtig? Es ist wahr, dass die meisten Shells nicht die hübschesten Möglichkeiten bieten, dies zu tun, aber wahrscheinlich wären Shells mit Möglichkeiten entworfen worden , wenn wir nicht so viele Device-as-File-Muster und die meisten Dinge darin gehabt hätten/dev
und/proc
als ausführbare Dateien behandelt worden wären Um es so einfach zu machen, wie wir es jetzt umleiten.wait4
! Sie haben Recht, es ist sicherlich möglich, stdout und stderr mit POSIX apis an verschiedene Programme weiterzuleiten, und es ist möglicherweise möglich, eine clevere Shell-Syntax für die Umleitung von stdout und stderr an verschiedene Befehle zu erfinden. Allerdings ist mir derzeit keine solche Shell bekannt, und der größere Punkt ist, dass / dev / null gut in vorhandene Tools (die größtenteils mit Dateien funktionieren) passt und / bin / null nicht. Wir könnten uns auch eine IO-API vorstellen, die es für gcc so einfach macht, (sicher!) In ein Programm wie in eine Datei auszugeben, aber das ist nicht die Situation, in der wir uns befinden.grep localhost /dev/ /etc/hosts 2> >(sed 's/^/STDERR:/' > errfile ) > >(sed 's/^/STDOUT:/' > outfile)
, was separat verarbeitet wirderrfile
undoutfile
Fairerweise handelt es sich nicht um eine reguläre Datei an sich. Es ist ein spezielles Zeichengerät :
Da es als Gerät und nicht als Datei oder Programm fungiert, ist es einfacher, Eingaben in das Gerät oder Ausgaben daraus umzuleiten, da es an jeden Dateideskriptor angehängt werden kann, einschließlich Standardeingabe / -ausgabe / -fehler.
quelle
cat file | null
würde viel Overhead haben, zuerst beim Einrichten einer Pipe, beim Erzeugen eines Prozesses, beim Ausführen von "null" im neuen Prozess usw. Außerdemnull
würde selbst eine ganze Menge CPU in einer Schleife zum Lesen von Bytes in einen späteren Puffer verwendet nur verworfen ... Die Implementierung/dev/null
im Kernel ist auf diese Weise nur effizienter. Was ist auch, wenn Sie/dev/null
anstelle einer Umleitung als Argument übergeben möchten ? (Sie könnten<(...)
in Bash verwenden, aber das ist noch viel schwerer!)null
Anstatt die Umleitung zu" verwenden/dev/null
müssten, gibt es eine einfache, eindeutige Möglichkeit, der Shell mitzuteilen, dass sie ein Programm ausführen soll, während nur das stderr an null gesendet wird?/dev/zero
stattdessen zu verwenden.dd of=-
Schreibvorgänge in eine Datei mit dem Namen-
lassen Sie das Feldof=
zum Schreiben in stdout einfach weg, da dortdd
standardmäßig geschrieben wird. Piping tofalse
würde nicht funktionieren, dafalse
es nicht dendd
Standardwert liest, und würde daher mit einem SIGPIPE getötet. Für einen Befehl, der seine Eingabe verwirft, können Sie ... verwendencat > /dev/null
. Auch der Vergleich, der wahrscheinlich als Flaschenhals irrelevant ist, wäre hier wahrscheinlich die Zufallszahlengenerierung.dd
etc. machen sich nicht einmal die Mühe, einen Schreib-Syscall durchzuführen, wenn sie feststellen, dass das Ziel / dev / null ist.Ich vermute, dass das Warum viel mit der Vision / dem Design zu tun hat, das Unix (und folglich Linux) geprägt hat, und den Vorteilen, die sich daraus ergeben.
Kein Zweifel, es ist ein nicht zu vernachlässigender Leistungsvorteil, keinen zusätzlichen Prozess zu starten, aber ich denke, es steckt noch mehr dahinter: Early Unix hatte eine Metapher "Alles ist eine Datei", die einen nicht offensichtlichen, aber eleganten Vorteil hat, wenn man sich das ansieht es aus einer Systemperspektive und nicht aus einer Shell-Skriptperspektive.
Angenommen, Sie haben Ihr
null
Befehlszeilenprogramm und/dev/null
den Geräteknoten. Von einer Shell-Scripting - Perspektive, dasfoo | null
ist Programm eigentlich wirklich nützlich und bequem , undfoo >/dev/null
nimmt ein kleines bisschen länger kann zu geben und seltsam erscheinen.Aber hier sind zwei Übungen:
Lassen Sie uns die Umsetzung des Programms in
null
einem der vorhandenen Unix - Tools und/dev/null
- einfach:cat >/dev/null
. Getan.Können Sie
/dev/null
in Bezug auf implementierennull
?Sie haben absolut Recht, dass der C-Code zum Verwerfen von Eingaben trivial ist. Daher ist es möglicherweise noch nicht klar, warum es nützlich ist, eine virtuelle Datei für die Aufgabe zur Verfügung zu haben.
Bedenken Sie: Fast jede Programmiersprache muss bereits mit Dateien, Dateideskriptoren und Dateipfaden arbeiten, da diese von Anfang an Teil des Unix-Paradigmas "Alles ist eine Datei" waren.
Wenn Sie nur Programme haben, die nach stdout schreiben, ist es dem Programm egal, ob Sie sie in eine virtuelle Datei umleiten, die alle Schreibvorgänge schluckt, oder eine Pipe in ein Programm, das alle Schreibvorgänge schluckt.
Wenn Sie nun Programme haben, die Dateipfade zum Lesen oder Schreiben von Daten verwenden (was die meisten Programme tun) - und diesen Programmen die Funktion "Leereingabe" oder "Ausgabe verwerfen" hinzufügen möchten -, ist dies
/dev/null
kostenlos.Beachten Sie, dass die Eleganz darin besteht, dass die Codekomplexität aller beteiligten Programme verringert wird. Für jeden allgemeinen, aber besonderen Verwendungszweck, den Ihr System als "Datei" mit einem tatsächlichen "Dateinamen" bereitstellen kann, kann Ihr Code das Hinzufügen eines benutzerdefinierten Befehls vermeiden -Line-Optionen und benutzerdefinierte Codepfade zu behandeln.
Gutes Software-Engineering hängt oft davon ab, gute oder "natürliche" Metaphern zu finden, um ein Element eines Problems auf eine Weise zu abstrahieren, die leichter zu überlegen ist, aber dennoch flexibel bleibt , sodass Sie im Grunde die gleichen Probleme auf höherer Ebene lösen können, ohne dass dies erforderlich ist verbringen Sie die Zeit und die geistige Energie damit, Lösungen für dieselben Probleme auf niedrigerer Ebene ständig neu zu implementieren.
"Alles ist eine Datei" scheint eine solche Metapher für den Zugriff auf Ressourcen zu sein: Sie rufen
open
einen bestimmten Pfad in einem hierarchischen Namespace auf, erhalten einen Verweis (Dateideskriptor) auf das Objekt, und Sie könnenread
undwrite
usw. auf die Dateideskriptoren. Ihre stdin / stdout / stderr sind auch Dateideskriptoren, die gerade für Sie vorab geöffnet wurden. Ihre Pipes sind nur Dateien und Dateideskriptoren, und mit der Dateiumleitung können Sie all diese Teile zusammenfügen.Unix war ebenso erfolgreich wie Unix, da diese Abstraktionen gut zusammengearbeitet haben und
/dev/null
am besten als Teil dieses Ganzen verstanden werden können.PS: Es lohnt sich, sich die Unix-Version von "Alles ist eine Datei" anzuschauen, und zwar
/dev/null
als erste Schritte zu einer flexibleren und leistungsfähigeren Verallgemeinerung der Metapher, die in vielen folgenden Systemen implementiert wurde.Zum Beispiel mussten in Unix spezielle dateiähnliche Objekte wie
/dev/null
im Kernel selbst implementiert werden, aber es hat sich herausgestellt, dass es nützlich genug ist, Funktionen in Datei- / Ordnerform bereitzustellen, die seitdem mehrere Systeme haben, die eine Möglichkeit für Programme bieten das zu tun.Eines der ersten war das Betriebssystem Plan 9, das von einigen der gleichen Leute wie Unix entwickelt wurde. Später machte GNU Hurd mit seinen "Übersetzern" etwas Ähnliches. In der Zwischenzeit bekam Linux FUSE (das sich mittlerweile auch auf die anderen Mainstream-Systeme ausgebreitet hat).
quelle
NUL
in jedem Verzeichnis angezeigt wurde , dh alles, was Sie eingeben müssen, ist> NUL
.Ich denke, es
/dev/null
ist ein Zeichengerät (das sich wie eine gewöhnliche Datei verhält) anstelle eines Programms aus Leistungsgründen .Wenn es sich um ein Programm handeln würde, müsste das Programm geladen, gestartet, geplant, ausgeführt und anschließend gestoppt und entladen werden. Das einfache C-Programm, das Sie beschreiben, würde natürlich nicht viel Ressourcen verbrauchen, aber ich denke, es macht einen signifikanten Unterschied, wenn man eine große Anzahl (sagen wir Millionen) von Redirect / Piping-Aktionen in Betracht zieht, da Prozessmanagementvorgänge in großem Umfang kostspielig sind Sie beinhalten Kontextwechsel.
Eine andere Annahme: Das Weiterleiten in ein Programm erfordert die Zuweisung von Speicher durch das empfangende Programm (auch wenn es direkt danach verworfen wird). Wenn Sie also in das Tool weiterleiten, haben Sie den doppelten Speicherverbrauch, einmal im Sendeprogramm und einmal im Empfangsprogramm.
quelle
read
die Daten handelt.) Dies ist auf einem Single-Core-PDP-11, für den Unix entwickelt wurde, nicht zu vernachlässigen! Speicherbandbreite / Kopieren ist heute viel billiger als damals. Einwrite
Systemaufruf zu einem FD, der am geöffnet ist,/dev/null
kann sofort zurückkehren, ohne dass Daten aus dem Puffer gelesen werden.null
Programm wäre zum Kotzen, aber die meisten Multi-Core-CPUs werden nicht immer mit allen Kernen ausgelastet Die CPU-Zeit zum Ausführen desnull
Programms ist größtenteils frei. Bei einem System, bei dem alle Kerne angeschlossen sind, ist die Verwendung nicht verwendbarer Leitungen ein größeres Problem. Nein, ein "heißer" Puffer kann viele Male umgeschrieben werden, ohne dass er in den Arbeitsspeicher geschrieben wird. Wir konkurrieren also meist nur um die L3-Bandbreite und nicht um den Cache. Nicht großartig, besonders auf einem SMT-System (Hyperthreading), bei dem andere logische Kerne auf demselben physischen System miteinander konkurrieren ...vpaddd zmm
: 16 32-Bit-Elemente pro Befehl.Abgesehen von "Alles ist eine Datei" und damit der Benutzerfreundlichkeit, auf der die meisten anderen Antworten basieren, gibt es auch Leistungsprobleme, wie @ user5626466 erwähnt.
Um dies in der Praxis zu zeigen, erstellen wir ein einfaches Programm mit dem Namen
nullread.c
:und kompiliere es mit
gcc -O2 -Wall -W nullread.c -o nullread
(Hinweis: Wir können lseek (2) nicht für Rohre verwenden. Daher muss das Rohr nur abgelesen werden, bis es leer ist.)
wohingegen
/dev/null
wir mit der Standard- Dateiumleitung viel bessere Geschwindigkeiten erzielen (aufgrund der genannten Fakten: weniger Kontextwechsel, Kernel ignoriert nur Daten, anstatt sie zu kopieren usw.):(dies sollte dort ein Kommentar sein, ist aber zu groß dafür und wäre völlig unlesbar)
quelle
dd
Puffer + Empfangspuffer nicht passt. Sie haben wahrscheinlich mit Speicherbandbreite statt mit Cache-Bandbreite der letzten Ebene zu tun. Möglicherweise erhalten Sie eine bessere Bandbreite mit 512-KB-Puffern oder sogar 64-KB-Puffern. (Lautstrace
auf meinem Desktop,write
undread
wir geben 1048576 zurück. Ich denke, das bedeutet, dass wir die Kosten für den <-> Benutzer-Kernel für die TLB-Ungültigmachung + Branch-Prediction-Flush nur einmal pro MB und nicht pro 64 KB bezahlen, @sourcejedi. Es ist Spectre Ich denke, die Minderung hat die meisten Kosten)syscall
RückgabeENOSYS
bei Skylake mit aktivierter Spectre-Schadensbegrenzung ~ 1800 Zyklen, wobei der größte Teil laut @ BeeOnRope-Testswrmsr
die BPU ungültig macht . Bei deaktivierter Schadensbegrenzung beträgt die Benutzer-> Kernel-> Benutzerumlaufzeit ~ 160 Zyklen. Wenn Sie jedoch viel Gedächtnis berühren, ist die Meltdown-Reduzierung ebenfalls von Bedeutung. Riesenseiten sollten helfen (weniger TLB-Einträge müssen neu geladen werden).Ihre Frage wird gestellt, als ob etwas vielleicht in der Einfachheit gewonnen würde, indem ein Nullprogramm anstelle einer Datei verwendet wird. Vielleicht können wir den Begriff "magische Dateien" loswerden und stattdessen nur "gewöhnliche Pfeifen" haben.
Aber bedenken Sie, eine Pipe ist auch eine Datei . Sie werden normalerweise nicht benannt und können daher nur über ihre Dateideskriptoren bearbeitet werden.
Betrachten Sie dieses etwas konstruierte Beispiel:
Mit der Prozessersetzung von Bash können wir dasselbe auf viel einfachere Weise erreichen:
Ersetzen Sie die
grep
fürecho
und wir können unter der Decke sehen:Das
<(...)
Konstrukt wird einfach durch einen Dateinamen ersetzt, und grep glaubt, dass es eine alte Datei öffnet. Es wird einfach nur benannt/dev/fd/63
. Hier/dev/fd
ist ein magisches Verzeichnis, das Named Pipes für jeden Dateideskriptor erstellt, der von der Datei, die darauf zugreift, verwendet wird.Wir könnten es weniger magisch
mkfifo
machen, eine Named Pipe zuls
erstellen , die in und in allem auftaucht , genau wie eine gewöhnliche Datei:Anderswo:
und siehe da, grep wird ausgeben
foo
.Ich denke , wenn Sie Rohre und normale Dateien und spezielle Dateien wie / dev / null realisieren sind alle nur Dateien, es ist offensichtlich ein Null - Programm Umsetzung ist mehr komplex. Der Kernel muss Schreibvorgänge in eine Datei so oder so behandeln, aber im Fall von / dev / null kann er die Schreibvorgänge einfach auf den Boden fallen lassen, wohingegen er mit einer Pipe die Bytes tatsächlich an ein anderes Programm übertragen muss, das dies dann tun muss Lesen Sie sie tatsächlich.
quelle
/dev/fd/63
?Ich würde argumentieren, dass es ein Sicherheitsproblem gibt, das über historische Paradigmen und Leistungen hinausgeht. Die Begrenzung der Anzahl von Programmen mit privilegierten Ausführungsberechtigungen, egal wie einfach, ist ein grundlegender Grundsatz der Systemsicherheit. Ein Ersatz
/dev/null
wäre sicherlich erforderlich, um solche Berechtigungen aufgrund der Verwendung durch Systemdienste zu haben. Moderne Sicherheits-Frameworks leisten hervorragende Arbeit, um Exploits zu verhindern, sind jedoch nicht narrensicher. Ein kernelgetriebenes Gerät, auf das als Datei zugegriffen wird, ist viel schwieriger auszunutzen.quelle
/dev/null
oder ein vorgeschlagenen eingabe Verwerfen Programm, das Angriffsvektor gleich sein würde: Holen Ihnen ein Skript oder Programm , das als root läuft zu tun versuchen , etwas seltsam (wie zulseek
in/dev/null
oder offen es mehrmals aus dem gleichen Prozess oder IDK was. Oder/bin/null
mit einer seltsamen Umgebung aufrufen , oder was auch immer).Wie andere bereits ausgeführt haben,
/dev/null
handelt es sich um ein Programm, das aus einer Handvoll Codezeilen besteht. Es ist nur so, dass diese Codezeilen Teil des Kernels sind.Um es klarer zu machen, hier ist die Linux-Implementierung: Ein Zeichengerät ruft Funktionen auf, wenn es gelesen oder geschrieben wird. Schreiben in
/dev/null
Aufrufe write_null , während Lesen in Aufrufe read_null , die hier registriert sind .Buchstäblich eine Handvoll Codezeilen: Diese Funktionen tun nichts. Sie benötigen nur dann mehr Codezeilen als Finger auf Ihren Händen, wenn Sie andere Funktionen als Lesen und Schreiben zählen.
quelle
Ich hoffe, dass Sie auch die / dev / chargen / dev / zero und andere wie diese kennen, einschließlich / dev / null.
LINUX / UNIX stellt einige davon zur Verfügung, damit die Benutzer gut geschriebene Codefragen nutzen können.
Chargen wurde entwickelt, um ein spezifisches und sich wiederholendes Zeichenmuster zu generieren. Es ist recht schnell und würde die Grenzen serieller Geräte überschreiten und beim Debuggen von seriellen Protokollen helfen, die geschrieben wurden und bei einem Test oder einem anderen fehlgeschlagen sind.
Mit Zero können Sie eine vorhandene Datei füllen oder eine ganze Reihe von Nullen ausgeben
/ dev / null ist nur ein weiteres Tool mit der gleichen Idee.
Alle diese Tools in Ihrem Toolkit bedeuten, dass Sie die halbe Chance haben, ein vorhandenes Programm dazu zu bringen, etwas Einzigartiges zu tun, ohne Rücksicht auf ihre (Ihre speziellen Bedürfnisse) als Geräte oder zum Ersetzen von Dateien
Lass uns einen Wettbewerb veranstalten, um zu sehen, wer mit nur wenigen Zeichengeräten in deiner LINUX-Version das aufregendste Ergebnis erzielen kann.
quelle