Wie unterscheidet Linux zwischen realen und nicht existierenden (zB: Geräte-) Dateien?

28

Dies ist eine eher einfache Frage, und ich verstehe, dass dies möglicherweise nicht der beste Ort ist, um sie zu stellen. Aber es schien angemessener zu sein als jede andere SE-Site.

Ich weiß, dass auf dem Linux-Dateisystem tatsächlich einige Dateien existieren , zum Beispiel: /usr/bin/bashist eine, die existiert. Allerdings (soweit ich es verstehe), einige haben auch nicht wirklich existieren als solche und sind virtuelle Dateien, zum Beispiel: /dev/sda, /proc/cpuinfousw. Meine Fragen sind (sie sind zwei, aber zu eng verwandt getrennten Fragen zu sein):

  • Wie ermittelt der Linux-Kernel, ob diese Dateien echt sind (und sie daher von der Festplatte lesen) oder nicht, wenn ein Lesebefehl (oder ein solcher) ausgegeben wird?
  • Wenn die Datei nicht echt ist: Als Beispiel gibt ein Lesevorgang /dev/randomzufällige Daten zurück und ein Lesevorgang /dev/nullwird zurückgegeben EOF. Wie wird ermittelt, welche Daten aus dieser virtuellen Datei gelesen werden sollen (und was ist daher zu tun, wenn / wenn Daten auch in die virtuelle Datei geschrieben werden)? oder sogar für das virtuelle Verzeichnis selbst? Ein Eintrag für /dev/nullkönnte also einfach einen zurückgeben EOF.
Joe
quelle
1
Wenn die Datei erstellt wird, zeichnet der Kernel ihren Typ auf. Normale Festplattendateien werden dann anders behandelt als Symlinks, Blockgeräte, Zeichengeräte, Verzeichnisse, Sockets, FIFOs usw. Es ist die Aufgabe des Kernels, dies zu wissen.
Jonathan Leffler
siehe den Mann für mknod pge
Jasen
Das ist wie die Frage "Woher weiß ein Lichtschalter, ob das Licht eingeschaltet ist?" Der Lichtschalter entscheidet, ob das Licht eingeschaltet ist.
Leichtigkeit Rennen mit Monica

Antworten:

25

Es gibt hier also grundsätzlich zwei verschiedene Arten von Dingen:

  1. Normale Dateisysteme, die Dateien in Verzeichnissen mit Daten und Metadaten in gewohnter Weise enthalten (einschließlich Softlinks, Hardlinks usw.). Diese werden häufig, aber nicht immer, von einem Blockgerät für dauerhaften Speicher gesichert (ein tmpfs befindet sich nur im RAM, ist aber ansonsten mit einem normalen Dateisystem identisch). Die Semantik von diesen ist vertraut; Lesen, Schreiben, Umbenennen und so weiter funktionieren alle so, wie Sie es erwarten.
  2. Verschiedene virtuelle Dateisysteme. /procund /syssind hier Beispiele, ebenso wie benutzerdefinierte FUSE-Dateisysteme wie sshfsoder ifuse. Es gibt viel mehr Vielfalt in diesen, weil sie sich in Wirklichkeit nur auf ein Dateisystem mit einer Semantik beziehen, die in gewissem Sinne "benutzerdefiniert" ist. Wenn Sie also aus einer Datei unter lesen /proc, greifen Sie nicht auf ein bestimmtes Datenelement zu, das von einer anderen Person gespeichert wurde, die es zuvor geschrieben hat, wie unter einem normalen Dateisystem. Sie führen im Wesentlichen einen Kernel-Aufruf durch und fordern einige Informationen an, die im laufenden Betrieb generiert werden. Und dieser Code kann alles tun, was er will, da er nur eine Funktion ist, die irgendwo readSemantik implementiert . So haben Sie das seltsame Verhalten von Dateien unter /proc, wie zum Beispiel vorzugeben, Symlinks zu sein, wenn sie nicht vorhanden sind.

Der Schlüssel ist, dass /deves sich in der Regel um eine der ersten Arten handelt. In modernen Distributionen /devist es normal, so etwas wie ein tmpfs zu haben, aber in älteren Systemen war es normal, dass es ein einfaches Verzeichnis auf der Festplatte ohne spezielle Attribute war. Der Schlüssel ist, dass die Dateien darunter /devGeräteknoten sind, eine Art Spezialdatei, die FIFOs oder Unix-Sockets ähnelt. Ein Geräteknoten hat eine Haupt- und eine Nebennummer. Wenn Sie diese lesen oder schreiben, rufen Sie einen Kerneltreiber auf. Ähnlich wie beim Lesen oder Schreiben eines FIFOs wird der Kernel aufgerufen, um Ihre Ausgabe in einer Pipe zu puffern. Dieser Treiber kann tun, was er will, berührt jedoch normalerweise die Hardware, z. B. um auf eine Festplatte zuzugreifen oder den Ton in den Lautsprechern wiederzugeben.

So beantworten Sie die ursprünglichen Fragen:

  1. Es gibt zwei relevante Fragen, ob die 'Datei existiert' oder nicht; Dies ist, ob die Geräteknotendatei buchstäblich existiert und ob der Kernel-Code, der sie sichert, von Bedeutung ist. Ersteres wird wie alles in einem normalen Dateisystem aufgelöst. Moderne Systeme verwenden udevoder ähnliches, um Hardwareereignisse zu beobachten und die Geräteknoten unter den /deventsprechenden Bedingungen automatisch zu erstellen und zu zerstören . Bei älteren Systemen oder leichten benutzerdefinierten Builds können jedoch alle Geräteknoten buchstäblich auf der Festplatte gespeichert und vorab erstellt werden. Währenddessen rufen Sie beim Lesen dieser Dateien den Kernel-Code auf, der von den Haupt- und Nebengerätenummern bestimmt wird. Wenn dies nicht zumutbar ist (wenn Sie beispielsweise versuchen, ein nicht vorhandenes Block-Gerät zu lesen), wird nur eine Art E / A-Fehler ausgegeben.

  2. Die Art und Weise, wie ermittelt wird, welcher Kernelcode für welche Gerätedatei aufzurufen ist, variiert. Für virtuelle Dateisysteme /procimplementieren sie ihre eigenen readund writeFunktionen. Der Kernel ruft diesen Code nur auf, je nachdem, auf welchem ​​Mount-Punkt er sich befindet. Den Rest erledigt die Implementierung des Dateisystems. Bei Gerätedateien erfolgt der Versand basierend auf den Haupt- und Nebengerätenummern.

Tom Hunt
quelle
Wenn also beispielsweise die Stromversorgung eines alten Systems unterbrochen würde, /devwären die darin enthaltenen Dateien immer noch vorhanden, aber ich vermute, sie würden gelöscht, wenn das System gestartet wird.
Joe
2
Wenn ein altes System (eines ohne dynamische Geräteerstellung) normal oder abnormal heruntergefahren wird, verbleiben die Geräteknoten wie alle Dateien auf der Festplatte. Beim nächsten Systemstart blieben sie dann ebenfalls auf der Festplatte, und Sie konnten sie wie gewohnt verwenden. Nur in modernen Systemen geschieht etwas Besonderes, wenn Geräteknoten erstellt und zerstört werden.
Tom Hunt
Ein moderneres System, das a nicht verwendet, tmpfswürde sie nach Bedarf dynamisch erstellen und löschen, z. B .: Booten und Herunterfahren?
Joe
3
devtmpfs, das /devDateisystem in modernem Linux, ähnelt a tmpfs, weist jedoch einige Unterschiede auf, die unterstützt werden müssen udev. (Der Kernel führt einige automatisierte Knotenerstellungen aus, bevor er an übergibt udev, um das Booten zu vereinfachen.) In all diesen Fällen befinden sich Geräteknoten nur im RAM und werden dynamisch erstellt und zerstört, wenn die Hardware dies erfordert. Vermutlich könnte man es auch udevauf einer normalen Festplatte verwenden /dev, aber ich habe das noch nie gesehen und es scheint keine guten Gründe dafür zu geben.
Tom Hunt
17

Hier ist eine Dateiliste von /dev/sda1meinem fast aktuellen Arch Linux-Server:

% ls -li /dev/sda1
1294 brw-rw---- 1 root disk 8, 1 Nov  9 13:26 /dev/sda1

Der Verzeichniseintrag in /dev/for sdahat also die Inode-Nummer 1294. Es ist eine echte Datei auf der Festplatte.

Sehen Sie sich an, wo normalerweise die Dateigröße angezeigt wird. Stattdessen wird "8, 1" angezeigt. Dies ist eine Haupt- und Nebengerätenummer. Beachten Sie auch das 'b' in den Dateiberechtigungen.

Die Datei /usr/include/ext2fs/ext2_fs.henthält diese (Fragment-) C-Struktur:

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
    __u16   i_mode;     /* File mode */

Diese Struktur zeigt uns die Festplattenstruktur des Inodes einer Datei. In dieser Struktur stecken viele interessante Dinge; werfen Sie einen langen Blick darauf.

Das i_modeElement von struct ext2_inodehat 16 Bits und verwendet nur 9 für Benutzer / Gruppe / Andere, Lese- / Schreib- / Ausführungsberechtigungen und weitere 3 für Setuid, Setgid und Sticky. Es gibt 4 Bits zur Unterscheidung zwischen den Typen "Plain File", "Link", "Directory", "Named Pipe", "Unix Family Socket" und "Block Device".

Der Linux-Kernel kann dem üblichen Verzeichnissuchalgorithmus folgen und dann basierend auf den Berechtigungen und Flags im i_modeElement eine Entscheidung treffen . Bei 'b', Gerätedateien blockieren, werden die Haupt- und Nebengerätenummern gefunden, und traditionell wird anhand der Hauptgerätenummer ein Zeiger auf eine Kernelfunktion (einen Gerätetreiber) gesucht, die sich mit Datenträgern befasst. Die untergeordnete Gerätenummer wird normalerweise als beispielsweise die SCSI-Bus-Gerätenummer oder die EIDE-Gerätenummer oder ähnliches verwendet.

Einige andere Entscheidungen, wie mit einer Datei umgegangen /proc/cpuinfowerden soll, werden basierend auf dem Dateisystemtyp getroffen. Wenn Sie Folgendes tun:

% mount | grep proc 
proc on /proc type proc (rw,nosuid,nodev,noexec,relatime)

Sie können sehen, dass /procdas Dateisystem den Typ "proc" hat. Das Einlesen einer Datei /procbewirkt, dass der Kernel je nach Typ des Dateisystems etwas anderes ausführt, als das Öffnen einer Datei in einem ReiserFS- oder DOS-Dateisystem dazu führt, dass der Kernel verschiedene Funktionen zum Suchen von Dateien und zum Auffinden von Daten der Datei verwendet Dateien.

Bruce Ediger
quelle
Sind Sie sicher, dass nur "echte Dateien auf der Festplatte" eine Inode-Nummer haben? Ich verstehe, 4026531975 -r--r--r-- 1 root root 0 Nov 14 18:41 /proc/mdstatwas eindeutig keine "echte Datei" ist.
guntbert
7

Am Ende des Tages sind sie alle Dateien für Unix, das ist das Schöne an der Abstraktion.

Die Art und Weise, wie der Kernel mit den Dateien umgeht, ist eine andere Geschichte.

/ proc und heutzutage / dev und / run (aka / var / run) sind virtuelle Dateisysteme im RAM. / proc ist eine Schnittstelle / ein Fenster zu Kernelvariablen und -strukturen.

Ich empfehle The Linux Kernel http://tldp.org/LDP/tlk/tlk.html und Linux Device Drivers, Third Edition https://lwn.net/Kernel/LDD3/ zu lesen .

Mir hat auch das Design und die Implementierung des FreeBSD-Betriebssystems gefallen http://www.amazon.com/Design-Implementation-FreeBSD-Operating-System/dp/0321968972/ref=sr_1_1

Schauen Sie sich die relevante Seite an, die zu Ihrer Frage gehört.

http://www.tldp.org/LDP/tlk/dd/drivers.html

Rui F Ribeiro
quelle
danke, ich habe die erste frage leicht geändert, nachdem du das kommentiert hast.
Joe
Lies bitte den letzten Kommentar.
Rui F Ribeiro
5

Zusätzlich zu den Antworten von @ RuiFRibeiro und @ BruceEdiger unterscheidet sich der Kernel nicht genau. Tatsächlich gibt es verschiedene Arten von Dateien: normale Dateien, Verzeichnisse, symbolische Links, Geräte, Sockets (und ich vergesse immer einige, damit ich nicht versuche, eine vollständige Liste zu erstellen). Sie können die Informationen über den Typ einer Datei erhalten, lsindem Sie Folgendes eingeben : Es ist das erste Zeichen in der Zeile. Beispielsweise:

$ls -la /dev/sda
brw-rw---- 1 root disk 8, 0 17 nov.  08:29 /dev/sda

Das 'b' ganz am Anfang signalisiert, dass diese Datei ein Blockgerät ist. Ein Bindestrich bedeutet eine reguläre Datei, eine symbolische Verknüpfung und so weiter. Diese Informationen werden in den Metadaten der Datei gespeichert und sind beispielsweise über den Systemaufruf zugänglich stat, sodass der Kernel beispielsweise eine Datei und einen symbolischen Link unterschiedlich lesen kann.

Dann machen Sie eine weitere Unterscheidung zwischen "echten" /bin/bashund "virtuellen" Dateien, /proc/cpuinfoaber geben lsbeide als reguläre Dateien an, sodass der Unterschied anders ist:

ls -la /proc/cpuinfo /bin/bash
-rwxr-xr-x 1 root root  829792 24 août  10:58 /bin/bash
-r--r--r-- 1 root wheel      0 20 nov.  16:50 /proc/cpuinfo

Was passiert ist, dass sie zu verschiedenen Dateisystemen gehören. /procist der Einhängepunkt eines Pseudo-Dateisystems, procfswohingegen er /bin/bashsich auf einem regulären Festplatten-Dateisystem befindet. Wenn Linux eine Datei öffnet (dies ist je nach Dateisystem unterschiedlich), wird eine Datenstruktur aufgefüllt, filedie unter anderem eine Struktur aus mehreren Funktionszeigern enthält, die die Verwendung dieser Datei beschreiben. Daher können unterschiedliche Verhaltensweisen für verschiedene Dateitypen implementiert werden.

Dies sind beispielsweise die Vorgänge, die angekündigt werden von /proc/meminfo:

static int meminfo_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, meminfo_proc_show, NULL);
}

static const struct file_operations meminfo_proc_fops = {
    .open       = meminfo_proc_open,
    .read       = seq_read,
    .llseek     = seq_lseek,
    .release    = single_release,
};

Wenn Sie sich die Definition von ansehen meminfo_proc_open, können Sie sehen, dass diese Funktion einen Puffer im Speicher mit den von der Funktion zurückgegebenen Informationen füllt meminfo_proc_show, deren Aufgabe es ist, Daten über die Speichernutzung zu sammeln. Diese Informationen können dann normal gelesen werden. Bei jedem Öffnen der Datei wird die Funktion meminfo_proc_openaufgerufen und die Informationen zum Speicher werden aktualisiert.

lgeorget
quelle
3

Alle Dateien in einem Dateisystem sind "real" in dem Sinne, dass sie Datei-E / A ermöglichen. Wenn Sie eine Datei öffnen, erstellt der Kernel einen Dateideskriptor, bei dem es sich um ein Objekt (im Sinne einer objektorientierten Programmierung) handelt, das sich wie eine Datei verhält. Wenn Sie die Datei lesen, führt der Dateideskriptor seine Lesemethode aus, die das Dateisystem (sysfs, ext4, nfs usw.) nach Daten aus der Datei fragt. Die Dateisysteme bieten eine einheitliche Benutzeroberfläche und wissen, wie mit Lese- und Schreibvorgängen umzugehen ist. Die Dateisysteme fordern wiederum andere Ebenen auf, ihre Anforderungen zu bearbeiten. Bei einer regulären Datei auf beispielsweise einem ext4-Dateisystem müssen die Datenstrukturen des Dateisystems nachgeschlagen werden (dies kann Festplattenlesevorgänge umfassen), und schließlich muss ein Lesevorgang von der Festplatte (oder dem Cache) durchgeführt werden, um Daten in den Lesepuffer zu kopieren. Für eine Datei in say sysfs, Es ist im Allgemeinen nur sprintf () etwas in den Puffer. Bei einem Block-Dev-Knoten fordert er den Plattentreiber auf, einige Blöcke zu lesen und in den Puffer zu kopieren (die Haupt- und Nebennummern teilen dem Dateisystem mit, an welchen Treiber Anforderungen gestellt werden sollen).

jpkotta
quelle