Gibt es unter Linux eine Ebene/ein Skript, das Programmanforderungen zum Öffnen von Dateien verarbeitet?
Wie wenn Sie einen Datei-Deskriptor in Bash öffnen: exec 3 <>/documents/foo.txt
oder Ihr Texteditor öffnet/documents/foo.txt
Ich kann nicht glauben, dass ein Editor „einfach eine Datei öffnen“ kann, um selbstständig Lese-/Schreibzugriff zu haben.
Ich stelle mir das eher als eine Anfrage an eine „Schicht“ vor (init.d-Skript?), das zunächst nur eine bestimmte Anzahl von Dateien öffnen kann und das geöffnete Dateien mit ihren Zugriffsarten, den Prozessen, mit denen sie geöffnet werden usw. im Auge behält.
Antwort1
Diese Schicht befindet sich bei Linux und anderen Systemen, die nicht zu weit vom historischen Unix-Design abweichen (und auch bei den meisten Nicht-Unix-Betriebssystemen), innerhalb des Kernels.
Dieser Teil des Kernels heißtVFS-Schicht (virtuelles Dateisystem)Die Rolle des VFS besteht darin, Informationen über offene Dateien zu verwalten (die Korrespondenz zwischenDateideskriptoren,Offene Dateibeschreibungenund Verzeichniseinträge), um Dateipfade zu analysieren (Interpretieren von /
, .
und ..
) und um Operationen an Verzeichniseinträgen an den richtigen Dateisystemtreiber zu verteilen.
Die meisten Dateisystemtreiber sind ebenfalls im Kernel enthalten, aber dieSICHERUNGDer Dateisystemtreiber ermöglicht die Delegierung dieser Funktionalität außerhalb des Kernels. Dateisystemoperationen können auch Benutzerlandcode beinhalten, wenn ein Speicher auf niedrigerer Ebene dies tut, beispielsweise wenn sich ein Festplattendateisystem auf einemLoop-Gerät.
Antwort2
Das Öffnen von Dateien unter Linux wird direkt vom Kernel übernommenSie können jedoch verschiedene Dinge tun, um den Prozess zu beeinflussen und zu untersuchen.
Systemaufrufe
Von oben beginnend können Sie sehen, dass die Schnittstelle, die Anwendungen zur Interaktion mit Dateien verwenden,Systemaufrufe.
Offen,lesenUndschreibentun, was Sie erwarten, währendStatistikGibt Informationen zu einer Datei zurück, ohne sie zu öffnen.
Sie können die Verwendung dateibezogener Systemaufrufe durch ein Programm mit strace untersuchen:
$ strace -e trace=%file /bin/ls /etc
[...]
stat("/etc", {st_mode=S_IFDIR|0755, ...}) = 0
openat(AT_FDCWD, "/etc", O_RDONLY...) = 3
Dadurch werden die durch verursachten Systemaufrufe analysiert ls /etc
und angezeigt, dass stat
und openat
für das Verzeichnis aufgerufen werden /etc
.
Sie fragen sich vielleicht, warum wir Dateioperationen für ein Verzeichnis aufrufen. Unter UNIX sind Verzeichnisse auch Dateien.alles ist eine Datei!
Dateideskriptoren
Sie fragen sich vielleicht, was openat() = 3
in der obigen Ausgabe steht.
Unter UNIX werden geöffnete Dateien durch einDateideskriptor, eine eindeutige Darstellung der geöffneten Datei durch einen bestimmten Prozess. Die Dateideskriptoren 0, 1 und 2 sind normalerweise reserviert fürStandard-Streams(Benutzereingabe/-ausgabe), daher ist die erste geöffnete Datei 3.
Sie können eine Liste der offenen Dateideskriptoren für einen bestimmten Prozess abrufen, indem Sie Folgendes verwenden:lsof
(michSTÖStiftFDateien):
$ cat /dev/urandom > /dev/null &
[1] 3242
$ lsof -p 3242
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
...
cat 3242 user 0u CHR 136,0 0t0 3 /dev/pts/0
cat 3242 user 1w CHR 1,3 0t0 1028 /dev/null
cat 3242 user 2u CHR 136,0 0t0 3 /dev/pts/0
cat 3242 user 3r CHR 1,9 0t0 1033 /dev/urandom
Die FD
Spalte zeigt Ihnen die Dateideskriptornummer zusammen mit dem Zugriff.
Sie können auchfuser
um nach Prozessen zu suchen, die bestimmte Dateien enthalten:
$ fuser /dev/urandom
/dev/urandom: ... 3242 ...
Prozessinformationen zum Pseudodateisystem – /proc
Jetzt fragen Sie sich vielleicht:aber woher lsof
weiß ich überhaupt, welche Dateien geöffnet sind??
Na, dann schauen wir doch mal!
$ strace -e trace=%file lsof -p 3242
...
stat("/proc/3242/", {st_mode=S_IFDIR|0555, st_size=0, ...}) = 0
openat(AT_FDCWD, "/proc/3242/stat", O_RDONLY) = 4
...
openat(AT_FDCWD, "/proc/3242/fd", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 4
readlink("/proc/3242/fd/0", "/dev/pts/0", 4096) = 10
lstat("/proc/3242/fd/0", {st_mode=S_IFLNK|0700, st_size=64, ...}) = 0
stat("/proc/3242/fd/0", {st_mode=S_IFCHR|0620, st_rdev=makedev(0x88, 0), ...}) = 0
openat(AT_FDCWD, "/proc/3242/fdinfo/0", O_RDONLY) = 7
...
So lsof
weiß er, welche Dateien geöffnet sind, indem er weitere Dateien liest! Insbesondere das Verzeichnis /proc/3242/fd
. Alles darunter /proc
ist ein „falsches“ Dateisystem, das vom Kernel verwaltet wird. Sie können ls -l
es verwenden, um seine Struktur anzuzeigen.
Beeinflussung des Dateiöffnens
Es gibt verschiedene Methoden, mit denen Sie das Öffnen von Dateien beeinflussen können. Allerdings ist es nicht so einfach, wie einfach ein Skript zu ersetzen.
Wenn Sie die Art und Weise ändern möchten, wie Dateien gespeichert oder abgerufen werden, z. B. durch Verschlüsselung, Zwischenspeicherung, Verteilung auf mehrere Festplatten oder ähnliches, besteht eine gute Chance, dass bereits eine vorhandeneGerätemapperdas Ihren Bedürfnissen entspricht.
Wenn Sie eine feinere Kontrolle über das Öffnen von Dateien in einem bestimmten Verzeichnis / Mount wünschen, können Sie einen einfachenSICHERUNGDateisystem und mounten Sie es.
Auf Programm-/Prozessebene können SieLD_PRELOADum die Aufrufe der C-Bibliothek zu ändern und zu verhindern, dass sie die normalen Systemaufrufe durchführen.
Der schwierigste, aber flexibelste Weg wäre, einen eigenen Dateisystemtreiber zu schreiben.
Antwort3
Die Verwaltung des Dateizugriffs ist die erste und wichtigste Funktion eines Betriebssystems. DOS, eines der ältesten Betriebssysteme auf PCs, steht für Disk Operating System. Es ermöglichte Programmen größtenteils den direkten Zugriff auf die Hardware, jedoch nicht den Zugriff auf Dateien. Programme mussten DOS-Aufrufe verwenden und DOS verwaltete das Einfügen und Auslesen von Daten in Dateien für das Programm. Nur Festplattendienstprogramme konnten unter DOS direkt auf die Festplatte und Dateien zugreifen.
Moderne Betriebssysteme im geschützten Modus wie Linux handhaben den Dateizugriff wie DOS, erfordern aber auch, dass jeder Zugriff auf irgendetwas außerhalb des Programms selbst (oder jedes anderen Programms, mit dem es laut Konfiguration den Speicher teilt) über den Kernel erfolgt (Linux ist ein Kernel).
Ihr Programm unter Linux kann eine Funktion in der C-Bibliothek aufrufen, um Daten in eine Datei zu lesen oder darin zu schreiben. Die C-Bibliothek übernimmt dann ihren Teil der Organisation des Zugriffs auf die Daten in der Datei, während sie weiterhin im selben Kontext wie Ihr Programm ausgeführt wird. Anschließend ruft die C-Bibliothek den Kernel (Linux) mit der richtigen Funktion auf, um auf eine Datei zuzugreifen, wodurch die CPU in Ring 0 oder den privilegierten Modus versetzt wird. Die CPU führt jetzt den Linux-Dateisystemtreiber und die Festplattentreibersoftware im privilegierten Modus aus, der direkt auf die Hardware zugreift, um auf die Datei zuzugreifen. Die Daten werden in den Speicherbereich kopiert, in dem die C-Bibliothek Linux angewiesen hat, die Daten abzulegen. Die CPU wird wieder in den Benutzermodus mit dem Sicherheitskontext Ihres Programms geschaltet. Die C-Bibliothek wird fortgesetzt und führt alle erforderlichen Verarbeitungsvorgänge an diesen Daten aus und kehrt dann zur Ausführung Ihres Programms zurück.
Antwort4
Kurz gesagt, das passiert, wenn ein Programm in eine Datei schreibt
- Das Programm fordert den Kernel
open
zum Schreiben in eine durch einen Pfad angegebene Datei auf. - Der Kernel richtet einige interne Strukturen ein und delegiert einen Teil der Aufgabe des Öffnens der Datei an einen für den Dateisystemtyp spezifischen Treiber. Der Kernel gibt dann einen Dateideskriptor, der nur eine Ganzzahl (z. B. 3) ist, an das Programm zurück.
- Das Programm fordert den Kernel auf,
write
eine Bytefolge (z. B. einen String) an die durch den Dateideskriptor referenzierte Datei weiterzugeben. - Der Kernel delegiert die Arbeit erneut an den Treiber.
- Die Schritte 3 und 4 werden wahrscheinlich mehrmals wiederholt.
- Das Programm fordert den Kernel zur
close
Datei auf, auf die der Dateideskriptor verweist. - Der Kernel delegiert erneut Arbeit an den Treiber und zerstört anschließend die internen Strukturen.
Hier ist ein recht minimalistisches Assemblerprogramm, das "Hallo Welt!" in die Datei greeting.txt schreibt:
.text
.globl _start
_start:
# Open and possible create file
mov $2, %rax # syscall 'open'
mov $path_start, %rdi # path
mov $0101, %rsi # create + write
mov $400, %edx # only user gets read permissions
syscall
mov %rax, %r10 # file descriptor
# Write string to file
mov $1, %rax # syscall 'write'
mov %r10, %rdi # file descriptor
mov $msg_start, %rsi # start of data
mov $msg_length, %edx # length of data
syscall # perform syscall
# Close file
mov $3, %rax # syscall 'close'
mov %r10, %rdi # file descriptor
syscall
# Exit program
mov $60, %rax # syscall 'exit'
syscall # perform syscall
.section .rodata
path_start:
.string "greeting.txt\0"
path_end:
path_length = path_end - path_start
msg_start:
.string "Hello World!\n"
msg_end:
msg_length = msg_end - msg_start
Speichern Sie den Code in write.s und erstellen Sie ihn mit
as -o write.o write.s
ld -o write write.o
und dann laufen mit
./write
Hoffentlich klappt alles.
(Hinweis: Ich führe keine Fehlerbehandlung durch. Dies ist nur Spielzeugcode.)