Ich versuche zu verstehen, wie eine Funktion, sagen wir mkdir
, funktioniert durch Betrachten der Kernelquelle. Dies ist ein Versuch, die Interna des Kernels zu verstehen und zwischen verschiedenen Funktionen zu navigieren. Ich kenne mkdir
ist in sys/stat.h
definiert . Ich habe den Prototypen gefunden:
/* Create a new directory named PATH, with permission bits MODE. */
extern int mkdir (__const char *__path, __mode_t __mode)
__THROW __nonnull ((1));
Jetzt muss ich sehen, in welcher C-Datei diese Funktion implementiert ist. Aus dem Quellverzeichnis habe ich versucht
ack "int mkdir"
was angezeigt wird
security/inode.c
103:static int mkdir(struct inode *dir, struct dentry *dentry, int mode)
tools/perf/util/util.c
4:int mkdir_p(char *path, mode_t mode)
tools/perf/util/util.h
259:int mkdir_p(char *path, mode_t mode);
Aber keiner von ihnen entspricht der Definition in sys/stat.h
.
Fragen
- Welche Datei hat den
mkdir
Umsetzung? - Wie kann ich bei einer Funktionsdefinition wie der obigen herausfinden, welche Datei die Implementierung enthält? Gibt es ein Muster, dem der Kernel beim Definieren und Implementieren von Methoden folgt?
HINWEIS:Ich verwende Kernel 2.6.36-rc1.
Akzeptierte Antwort:
Systemaufrufe werden nicht wie normale Funktionsaufrufe behandelt. Für den Übergang vom User Space zum Kernel Space ist ein spezieller Code erforderlich, im Grunde genommen ein bisschen Inline-Assembler-Code, der an der Aufrufseite in Ihr Programm eingefügt wird. Der Kernel-Seitencode, der den Systemaufruf „abfängt“, ist ebenfalls Low-Level-Zeug, das Sie wahrscheinlich nicht tief verstehen müssen, zumindest am Anfang.
In include/linux/syscalls.h
Unter Ihrem Kernel-Quellverzeichnis finden Sie Folgendes:
asmlinkage long sys_mkdir(const char __user *pathname, int mode);
Dann in /usr/include/asm*/unistd.h
, finden Sie Folgendes:
#define __NR_mkdir 83
__SYSCALL(__NR_mkdir, sys_mkdir)
Dieser Code sagt mkdir(2)
ist Systemaufruf #83. Das heißt, Systemaufrufe werden nach Nummer aufgerufen, nicht nach Adresse wie bei einem normalen Funktionsaufruf innerhalb Ihres eigenen Programms oder einer Funktion in einer mit Ihrem Programm verknüpften Bibliothek. Der oben erwähnte Inline-Assembly-Glue-Code verwendet dies, um den Übergang vom Benutzer- zum Kernel-Space zu vollziehen, wobei Ihre Parameter mitgenommen werden.
Ein weiterer Beweis dafür, dass die Dinge hier etwas seltsam sind, ist, dass es nicht immer eine strenge Parameterliste für Systemaufrufe gibt:open(2)
, zum Beispiel, kann entweder 2 oder 3 Parameter annehmen. Das bedeutet open(2)
ist überladen, ein Feature von C++, nicht C, aber die Syscall-Schnittstelle ist C-kompatibel. (Dies ist nicht dasselbe wie die varargs-Funktion von C, die es einer einzelnen Funktion ermöglicht, eine variable Anzahl von Argumenten zu akzeptieren.)
Um Ihre erste Frage zu beantworten, es gibt keine einzelne Datei, in der mkdir()
existiert. Linux unterstützt viele verschiedene Dateisysteme und jedes hat seine eigene Implementierung der „mkdir“-Operation. Die Abstraktionsschicht, die es dem Kernel ermöglicht, all das hinter einem einzigen Systemaufruf zu verbergen, wird VFS genannt. Sie möchten also wahrscheinlich anfangen, in fs/namei.c
zu graben , mit vfs_mkdir()
. Die tatsächlichen Implementierungen des Low-Level-Dateisystem-Änderungscodes befinden sich woanders. Beispielsweise heißt die ext4-Implementierung ext4_mkdir()
, definiert in fs/ext4/namei.c
.
Was Ihre zweite Frage betrifft, ja, es gibt Muster für all dies, aber keine einzige Regel. Was Sie tatsächlich brauchen, ist ein ziemlich breites Verständnis der Funktionsweise des Kernels, um herauszufinden, wo Sie nach einem bestimmten Systemaufruf suchen sollten. Nicht alle Systemaufrufe beinhalten das VFS, daher beginnen ihre Aufrufketten auf der Kernelseite nicht alle in fs/namei.c
. mmap(2)
beginnt beispielsweise in mm/mmap.c
, weil es Teil des Speicherverwaltungs-Subsystems („mm“) des Kernels ist.
Ich empfehle Ihnen, sich eine Kopie von „Understanding the Linux Kernel“ von Bovet und Cesati zu besorgen.