Wie werden / dev-Linux-Dateien erstellt?

112

Es gibt spezielle Dateien in Linux, die nicht wirklich Dateien sind.

Die bemerkenswertesten und klarsten Beispiele dafür sind im devOrdner "files":

  • /dev/null - Ignoriert alles, was Sie in die Datei schreiben
  • /dev/random - Gibt zufällige Daten anstelle des Inhalts einer Datei aus
  • /dev/tcp - Sendet alle Daten, die Sie in diese Datei schreiben, über das Netzwerk

Was ist der Name dieser Art von "Dateien", die wirklich eine Art Skript oder Binärdatei sind?

Zweitens, wie entstehen sie? Sind diese Dateien auf Kernel-Ebene in das System integriert, oder gibt es eine Möglichkeit, eine "magische Datei" selbst zu erstellen (wie wäre es mit einer /dev/rickroll)?

IQAndreas
quelle
1
Ich hatte keine Ahnung, wie ich diese Frage markieren sollte, zumal ich nicht weiß, wonach ich suche. Fühlen Sie sich frei, relevante Tags zu bearbeiten.
IQAndreas
15
Übrigens ist dies ein grundlegender Bestandteil des Designs von Unix- und Unix-ähnlichen Betriebssystemen: (fast) alles ist eine Datei oder kann so gestaltet werden, dass es wie eine Datei aussieht.
cas
5
Siehe auch: mknod (2) man 2 mknod
RobertL
4
Dies sind "Geräteknoten". Die von Ihnen genannten Geräte sind jedoch - im Gegensatz zu Laufwerken, Tastaturen, Mäusen, Audiokarten und anderen Geräten - sogenannte "Pseudogeräte", da sie keine "echten" Geräte sind und nur im Kernel vorhanden sind. Es ist möglich, neue zu erstellen, indem Sie einen geeigneten Gerätetreiber schreiben und ihn zum Kernel hinzufügen (z. B. ein Pseudogerät zur Überwachung bestimmter Aktivitäten auf dem Computer). Bevor das / dev-Verzeichnis auf der Festplatte existierte, handelt es sich heutzutage um ein virtuelles Dateisystem (vom Typ devfs), das vom Kernel erstellt wurde.
Baard Kopperud
10
Alle Dateien, auch "echte" Dateien, sind Software-Artefakte. Die Software hinter jedem Gerät, eine Datei, Buchse, spezielle Datei, oder etwas noch erfunden werden eine Tabelle von Funktionen zu handhaben open(), read(), close()usw. Danach ist es bis zur Software
waltinator

Antworten:

101

/dev/zeroist ein Beispiel für eine "spezielle Datei" - insbesondere ein "Geräteknoten". Normalerweise werden diese bei der Installation der Distribution erstellt, aber Sie können sie ganz selbst erstellen, wenn Sie möchten.

Wenn Sie fragen lsnach /dev/zero:

# ls -l /dev/zero
crw-rw-rw- 1 root root 1, 5  Nov 5 09:34 /dev/zero

Das "c" am Anfang zeigt an, dass es sich um ein "Zeichengerät" handelt. Der andere Typ ist "Block Device" (gedruckt mit ls"b"). Ganz grob gesagt sind Direktzugriffsgeräte wie Festplatten Blockgeräte, während sequenzielle Geräte wie Bandlaufwerke oder Ihre Soundkarte Zeichengeräte sind.

Der Teil "1, 5" ist die "Hauptgerätenummer" und die "Nebengerätenummer".

Mit diesen Informationen können wir den mknodBefehl verwenden, um unseren eigenen Geräteknoten zu erstellen:

# mknod foobar c 1 5

Dadurch wird foobarim aktuellen Ordner eine neue Datei mit dem Namen erstellt , die genau dasselbe tut wie /dev/zero. (Sie können natürlich verschiedene Berechtigungen festlegen, wenn Sie möchten.) Diese "Datei" enthält nur die drei oben genannten Elemente - Gerätetyp, Hauptnummer, Nebennummer. Sie können lsdie Codes für andere Geräte nachschlagen und diese auch neu erstellen. Wenn Sie sich langweilen, rmentfernen Sie einfach die Geräteknoten, die Sie gerade erstellt haben.

Grundsätzlich teilt die Hauptnummer dem Linux-Kernel mit, mit welchem ​​Gerätetreiber gesprochen werden soll, und die Nebennummer teilt dem Gerätetreiber mit, über welches Gerät Sie sprechen. (ZB haben Sie wahrscheinlich einen SATA-Controller, aber möglicherweise sind mehrere Festplatten angeschlossen.)

Wenn Sie neue Geräte erfinden möchten , die etwas Neues bewirken, müssen Sie den Quellcode für den Linux-Kernel bearbeiten und Ihren eigenen Kernel kompilieren. Also lass uns das nicht tun! :-) Sie können jedoch Gerätedateien hinzufügen, die die bereits vorhandenen duplizieren. Ein automatisiertes System wie udev wartet im Grunde nur auf Geräteereignisse und ruft automatisch mknod/ rmfür Sie an. Nichts magischeres als das.

Es gibt noch andere Arten von Spezialdateien:

  • Linux betrachtet ein Verzeichnis als eine spezielle Art von Datei. (Normalerweise können Sie ein Verzeichnis nicht direkt öffnen, aber wenn Sie könnten, würden Sie feststellen, dass es sich um eine normale Datei handelt, die Daten in einem speziellen Format enthält und dem Kernel mitteilt, wo alle Dateien in diesem Verzeichnis zu finden sind.)

  • Ein Symlink ist eine spezielle Datei. (Ein fester Link ist es nicht.) Mit dem ln -sBefehl können Sie Symlinks erstellen . (Schlagen Sie die Manpage nach.)

  • Es gibt auch eine sogenannte "Named Pipe" oder "FIFO" (First-In, First-Out Queue). Sie können eine mit erstellen mkfifo. Ein FIFO ist eine magische Datei, die von zwei Programmen gleichzeitig geöffnet werden kann - ein Lesen, ein Schreiben. In diesem Fall funktioniert es wie ein normales Mantelrohr. Sie können aber jedes Programm einzeln starten ...

Eine Datei, die in keiner Weise "speziell" ist, wird als "reguläre Datei" bezeichnet. Gelegentlich wird dies in der Unix-Dokumentation erwähnt. Das ist was es bedeutet; Eine Datei, die kein Geräteknoten oder Symlink oder was auch immer ist. Nur eine normale, alltägliche Datei ohne magische Eigenschaften.

MathematicalOrchid
quelle
4
Es gibt noch eine weitere Art von Spezialdatei, einen Unix-Domain-Socket, der an das Dateisystem gebunden ist.
Brian Bi
8
Wenn Sie mitspielen möchten mknod, rufen cat /proc/devicesSie die Hauptnummern aller Treiber auf. Das bringt uns zu einer weiteren Art von Spezialdatei, dem /procDateisystem ( diese Antwort spricht darüber).
Ugoren
8
Andere Unices haben ihre eigenen Spezialdateien erfunden, z . B. Solaris hatte Türen .
Kevin
6
Kleiner Trottel: Sie müssen den Kernel nicht neu kompilieren, um ein neues Zeichen / Block-Gerät zu schreiben :) crashcourse.ca/introduction-linux-kernel-programming/… Ansonsten ist dies eine wirklich gute Antwort, +1!
Kommandant Koriander Salamander
1
@MathematicalOrchid: Ein Schritt, auf den Ihre Antwort fehlt (oder der zumindest implizit besagt), ist die Tatsache, dass diese speziellen Dateien überhaupt keine getarnten Shell-Skripte oder Binärdateien sind (wie die Frage impliziert), sondern eine Schnittstelle für den Zugriff auf vorhandene Funktionen im OS-Kernel.
Träumer
34

Die meisten /devEinträge sind Blockgeräte-Inodes oder Zeichengeräte-Inodes. Wikipedia hat viele Details darüber, die ich nicht wiederholen werde.

Aber /dev/tcpwas in Ihrer Frage erwähnt wird, wird durch keine der vorhandenen Antworten erklärt. /dev/tcpund /dev/udpunterscheiden sich von den meisten anderen /devEinträgen. Die Block- und Zeichengeräte werden vom Kernel implementiert, aber /dev/tcpund /dev/udpwerden im Benutzermodus implementiert.

Die Bash-Shell ist ein Programm, das eine Implementierung von /dev/tcpund /dev/udp(kopiert von ksh93) hat. Wenn Sie versuchen, einen Pfad unter denen mit Bash-Umleitungsoperatoren zu öffnen, wird kein gewöhnlicher openSystemaufruf ausgeführt. Stattdessen erstellt bash einen TCP-Socket und verbindet ihn mit dem angegebenen Port.

Dies ist im Benutzermodus und nur in einigen Programmen implementiert, wie im folgenden Beispiel zu sehen ist, das den Unterschied zwischen dem Lassen bashund dem catVersuch des Öffnens demonstriert/dev/tcp/::1/22

$ cat /dev/tcp/::1/22
cat: /dev/tcp/::1/22: No such file or directory
$ cat < /dev/tcp/::1/22
SSH-2.0-OpenSSH_6.6.1p1 Ubuntu-2ubuntu2.3

Ein Unterschied zu ksh93ist, dass bashnur diese TCP-Verbindungen mit Umleitungsoperatoren ausgeführt werden, nicht an den anderen Stellen, an denen Dateien wie die sourceoder die .eingebauten geöffnet werden können.

Kasperd
quelle
Auch GNU awk hat gawkähnliche Sonderfälle /inet{,4,6}/{tcp,udp}/$port/$remote/$rport, seit ungefähr 2010 (ich erinnere mich nicht genau und kann keine Versionshinweise finden).
Dave_Thompson_085
6
IMO, ein besserer Weg, um den Punkt darzulegen, /dev/tcpist, dass es KEINE Datei ist. Es gibt niemals eine Datei mit diesem Namen. Bashs Syntax zum Öffnen von Sockets verwendet die Zeichenfolge /dev/tcp/addresswie einen Dateinamen, aber die Bezeichnung "Datei im Benutzerbereich implementiert" klingt nur seltsam. Interessant, dass kshdiese Dateinamen für alles verwendet werden, nicht nur für Weiterleitungen. Das ist näher an "Implementieren einer Datei".
Peter Cordes
@ PeterCordes Ich glaube, UWIN richtet diese als tatsächliche Dateien. und ich denke, 3dfs macht das gleiche. Denken Sie daran, bashnur dieses Verhalten kopiert, aber es hat einen anderen Ursprung.
mikeserv
19

Neben dem Geräteknoten in anderen Antworten erklärt (erstellt mit mknod (2) oder von einigen geliefert devfs ), hat Linux andere „magische“ Dateien durch spezielle bereitgestellt virtuelle Dateisysteme , insbesondere in /proc/(siehe proc (5) , lesen Sie über procfs ) und in /sys/(lies über sysfs ).

Diese Pseudodateien (die stat (2) als normale Dateien und nicht als Geräte erscheinen) sind eine vom Kernel bereitgestellte virtuelle Ansicht. insbesondere das Lesen von /proc/(zB mit cat /proc/$$/mapsoder durch Öffnen (2) -ing /proc/self/statusin Ihrem Programm) beinhalten im Allgemeinen keine physikalische I / O von der Festplatte oder im Netzwerk, so ziemlich schnell.

Um eine zusätzliche Pseudodatei zu erstellen, /proc/sollten Sie in der Regel Ihr eigenes Kernelmodul schreiben und laden (siehe z . B. dieses ).

Basile Starynkevitch
quelle
3
AFAIK die Informationen zum Erweitern / Proc ist veraltet. Obwohl technisch noch möglich, sollte / proc (oder besser procfs) nur Informationen zu laufenden Prozessen enthalten. Alle anderen Pseudodateien, einschließlich derer, die Laufzeitinformationen oder Konfigurationsoptionen für den Kernel enthalten, sollten in / sys (sysfs) abgelegt werden. Aus Kompatibilitätsgründen befinden sich noch einige nicht prozessbezogene Pseudodateien in / proc (z. B. meminfo, cpuinfo). Neue Pseudodateien sollten jedoch in sysfs gespeichert werden.
Träumer
13

Sie werden Geräteknoten genannt und entweder manuell mit mknododer automatisch von erstellt udev. Sie sind typischerweise dateiähnliche Schnittstellen zu Zeichen- oder Blockgeräten mit Treibern im Kernel - z. B. sind Festplatten Blockgeräte, ttys und serielle Ports usw. sind Zeichengeräte.

Es gibt auch andere "spezielle" Dateitypen, einschließlich Named Pipes und Fifos und Sockets.

cas
quelle
9

Wie andere Benutzer bereits ausführlich erklärt haben, benötigen spezielle Dateien Code, um sie zu sichern. Es scheint jedoch niemand erwähnt zu haben, dass Linux verschiedene Möglichkeiten bietet, diesen Code im Userspace zu schreiben:

A. Mit FUSE (Dateisystem in USErspace) können Sie so etwas wie schreiben, /procohne dass das Risiko eines Absturzes des Kernels besteht, und dies in einer Sprache / Laufzeit Ihrer Wahl, wie z. B. Go , Node.js , Perl , PHP , Python , Ruby , Rust . usw. .

Dies hat auch den Vorteil, dass FUSE-Dateisysteme bereitgestellt werden können, ohne sudodass sie ausgeführt werden, während der Benutzer die Bereitstellung vornimmt.

Hier sind einige Beispiele von Dingen, die Leute mit FUSE geschrieben haben:

B. Wenn Sie ein virtuelles Eingabegerät wie eine Tastatur, eine Maus, einen Joystick usw. erstellen möchten (z. B. um einen Userspace-Treiber für ein USB-Gerät zu schreiben, mit dem Sie sprechen libusb), gibt es eine Eingabe .

Bindungen dafür sind schwerer zu finden, aber ich weiß, dass sie für Go (nur Tastatur), Python und Ruby (2) existieren .

Beispiele für die Verwendung von Eingaben in der Praxis sind:

  • G15Daemon (Linux-Treiber für die LCD- und Gaming-Tasten der Logitech G15-Gaming-Tastaturen)
  • ds4drv (Treiber für Sony DualShock 4-Controller)
  • xboxdrv (Alternativer XBox 360-Controller-Treiber und Linux-Äquivalent zu x360ce, so schlecht gestaltete Spiele wie Runner2: Future Legend of Rhythm Alien können denken, sie sprechen mit einem echten XBox-Controller, wenn sie nicht sind)
  • Die alten Wiimote-Treiber wie cwiid , die benötigt wurden, bevor jemand einen Kernel-Wiimote-Treiber geschrieben hat, damit standardmäßig Unterstützung verfügbar ist.

C. Für generische Zeichengeräte gibt es CUSE (Zeichengeräte in USErspace). Es ist jedoch viel weniger beliebt.

Der einzige Benutzer des CUSE API , die ich persönlich bewusst bin , ist das gleiche Programm , das seine Schöpfung aufgefordert: osspd , die implementiert /dev/dsp, /dev/adspund /dev/mixer(das OSS Audio-API) in User - Space , damit sie mit Pulseaudio oder dmix geleitet werden.

Die einzige CUSE-Bindung, die ich finden konnte, ist cusepy , die seit 2010 nicht mehr aktualisiert wurde.

D. Möglicherweise benötigen Sie überhaupt keine neue Spezialdatei.

Sie können beispielsweise mit libusb (Liste der Bindungen auf der Seite) eine unformatierte Kommunikation mit einem beliebigen USB-Gerät herstellen und dann über einen anderen Mechanismus (TCP / UDP-Sockets, Lesen / Schreiben von stdin / stdout- oder normalen Dateien auf der Festplatte) mit anderen Programmen kommunizieren , usw.).

ssokolow
quelle
1
cusepy wurde vielleicht schon eine Weile nicht mehr aktualisiert (tatsächlich wurde es nie aktualisiert; es hat nur ein Commit!), aber nachdem ich vor ein paar Wochen ein Zeichengerät mit cusepy geschrieben habe, kann ich bestätigen, dass es immer noch funktioniert. Es fehlten einige Funktionen im Zusammenhang mit der Implementierung poll, aber da cusepy ctypes verwendet und die Bindungen auf der Grundlage von C-Header-Dateien automatisch generiert werden, müssen Sie der Liste der exportierten Funktionen in nur den gewünschten Funktionsnamen hinzufügen, um fehlende Funktionen zu beheben setup.py.
Aleksi Torhamo
1
Ein weiteres interessantes Beispiel für die Verwendung von FUSE ist sshfs . Sie können das Remote-Dateisystem so durchsuchen, als wäre es lokal, indem Sie darunter eine SSH-Verbindung verwenden.
Mr. Deathless
@ Mr.Deathless Ja. Ich habe das tatsächlich benutzt und wollte es erwähnen, aber ich habe es vergessen.
ssokolow
6

In dem Buch Linux Device Drivers (dringend empfohlen) wird dies ausführlich erläutert, und Sie haben sogar ein Kernelmodul erstellt, das dies als Beispiel dient. Kurz gesagt, jeder Gerätetreiber verfügt über spezifische Funktionen, die aufgerufen werden, wenn eine Datei geöffnet oder geschlossen wird , lesen, schreiben, etc. Die "speziellen" Dateien machen einfach etwas Besonderes in diesen Funktionen, anstatt auf die Speicherhardware auf einer Festplatte zuzugreifen.

Zum Beispiel /dev/nulltut die Schreibfunktion für einfach nichts und ignoriert die Bytes. Die Lesefunktion für /dev/randomgibt eine Zufallszahl zurück.

Karl Bielefeldt
quelle
1

mount -t devtmpfs

Es ist auch interessant zu sehen, dass es sich bei modernen Systemen /devnormalerweise um einen Dateisystemtyp handelt, der beliebig eingehängt werden kann. Ubuntu 16.04:

mkdir d
sudo mount -t devtmpfs none d
head -c 10 d/random
sudo umount d

Dies wird von aktiviert CONFIG_DEVTMPFS=yund ermöglicht es dem Kernel selbst, nach Bedarf Gerätedateien zu erstellen und zu zerstören.

CONFIG_DEVTMPFS_MOUNT=y

Durch diese Option wird der Kernel automatisch zum Einhängen von devtmpfs veranlasst /dev.

drivers/base/Kconfig Unterlagen:

config DEVTMPFS_MOUNT
    bool "Automount devtmpfs at /dev, after the kernel mounted the rootfs"
    depends on DEVTMPFS
    help
      This will instruct the kernel to automatically mount the
      devtmpfs filesystem at /dev, directly after the kernel has
      mounted the root filesystem. The behavior can be overridden
      with the commandline parameter: devtmpfs.mount=0|1.
      This option does not affect initramfs based booting, here
      the devtmpfs filesystem always needs to be mounted manually
      after the rootfs is mounted.
      With this option enabled, it allows to bring up a system in
      rescue mode with init=/bin/sh, even when the /dev directory
      on the rootfs is completely empty.

file_operations

Schließlich sollten Sie Ihr eigenes Character Device-Kernel-Modul erstellen, um genau zu sehen, was los ist.

Hier ist ein minimal ausführbares Beispiel: Grundlegendes zu Gerätedateien (oder speziellen Zeichendateien)

Der wichtigste Schritt ist das Einrichten der file_operationsStruktur, zB:

static const struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = read,
    .open = open,
};

static int myinit(void)
{
    major = register_chrdev(0, NAME, &fops);
    return 0;
}

enthält Funktionszeiger, die bei jedem dateibezogenen Systemaufruf aufgerufen werden.

Es wird dann offensichtlich, dass Sie diese dateibezogenen Systemaufrufe überschreiben, um zu tun, was Sie wollen, und so implementiert der Kernel Geräte wie /dev/zero.

/devEinträge erstellen um automatisch ohnemknod

Das letzte Rätsel ist, wie der Kernel automatisch /devEinträge erstellt .

Der Mechanismus kann durch eine Kernel - Modul beobachtet werden, dass sich tut , wie bei: https://stackoverflow.com/questions/5970595/how-to-create-a-device-node-from-the-init-module- Code-of-a-Linux-Kernel-Modul / 45531867 # 45531867 und kommt auf einen device_createAnruf.

Ciro Santilli ist ein Schauspieler
quelle
In OpenBSD gibt es ein Skript MAKEDEV, das dies ein wenig vereinfacht, siehe man.openbsd.org/MAKEDEV.8 Ich bin mir nicht sicher, warum Linux es nicht hat, außer es ist viel komplizierter. Vielleicht könnten Teile angepasst werden. Sie können zum Beispiel MKNOD tty sagen und es behandelt die Details.
Alan Corey