Wie finde ich die Implementierungen von Linux-Kernel-Systemaufrufen?


375

Ich versuche zu verstehen, wie eine Funktion funktioniert, mkdirindem ich mir die Kernelquelle anschaue. Dies ist ein Versuch, die Kernel-Interna zu verstehen und zwischen verschiedenen Funktionen zu navigieren. Ich weiß, mkdirist definiert in sys/stat.h. Ich habe den Prototyp 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 es versucht

ack "int mkdir"

welche angezeigt

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

  1. Welche Datei hat die mkdirImplementierung?
  2. Wie kann ich bei einer Funktionsdefinition wie der obigen herausfinden, welche Datei die Implementierung hat? Gibt es ein Muster, nach dem der Kernel Methoden definiert und implementiert?

ANMERKUNG: Ich verwende den Kernel 2.6.36-rc1 .


2
Schauen Sie sich das an: voinici.ceata.org/~tct/resurse/utlk.pdf
Tom Brito

Antworten:


386

Systemaufrufe werden nicht wie normale Funktionsaufrufe behandelt. Für den Übergang vom User-Space zum Kernel-Space wird spezieller Code benötigt, im Grunde ein bisschen Inline-Assembler-Code, der an der aufrufenden Site in Ihr Programm eingefügt wird. Der Kernel-Side-Code, der den Systemaufruf "abfängt", ist ebenfalls ein Teil auf niedriger Ebene, den Sie wahrscheinlich zumindest auf den ersten Blick nicht genau verstehen müssen.

In include/linux/syscalls.hIhrem Kernel-Quellverzeichnis finden Sie Folgendes:

asmlinkage long sys_mkdir(const char __user *pathname, int mode);

Dann /usr/include/asm*/unistd.hfinden Sie in:

#define __NR_mkdir                              83
__SYSCALL(__NR_mkdir, sys_mkdir)

Dieser Code besagt, dass mkdir(2)es sich um den Systemaufruf Nr. 83 handelt. Das heißt, Systemaufrufe werden nach Nummer und nicht nach Adresse aufgerufen, wie bei einem normalen Funktionsaufruf in Ihrem eigenen Programm oder bei einer Funktion in einer Bibliothek, die mit Ihrem Programm verknüpft ist. Der oben erwähnte Inline-Assembler-Code verwendet diesen Code, um den Übergang vom Benutzer- zum Kernel-Space durchzuführen, wobei Ihre Parameter mitgeführt werden.

Ein weiterer Beweis dafür, dass die Dinge hier etwas seltsam sind, ist, dass es nicht immer eine strikte Parameterliste für Systemaufrufe gibt: open(2)Beispielsweise können 2 oder 3 Parameter verwendet werden. Das heißt, es open(2)ist überladen , eine Funktion von C ++, nicht von C, aber die Syscall-Schnittstelle ist C-kompatibel. (Dies ist nicht dasselbe wie die varargs-Funktion von C , mit der eine einzelne Funktion eine variable Anzahl von Argumenten annehmen kann.)

Zur Beantwortung Ihrer ersten Frage existiert keine einzige Datei mkdir(). Linux unterstützt viele verschiedene Dateisysteme und jedes hat eine eigene Implementierung der Operation "mkdir". Die Abstraktionsschicht, die es dem Kernel ermöglicht, alles, was sich hinter einem einzelnen Systemaufruf verbirgt, wird als VFS bezeichnet . Also möchten Sie wahrscheinlich anfangen fs/namei.c, mit zu graben vfs_mkdir(). Die tatsächlichen Implementierungen des Änderungscodes für das Dateisystem auf niedriger Ebene befinden sich an anderer Stelle. Zum Beispiel heißt die ext4-Implementierung ext4_mkdir(), definiert in fs/ext4/namei.c.

Bei Ihrer zweiten Frage gibt es zwar Muster, aber keine einzige Regel. Was Sie tatsächlich brauchen, ist ein ziemlich umfassendes Verständnis der Funktionsweise des Kernels, um herauszufinden, wo Sie nach einem bestimmten Systemaufruf suchen sollten. Nicht alle Systemaufrufe beziehen das VFS mit ein, sodass ihre kernelseitigen Aufrufketten nicht alle in beginnen fs/namei.c. mmap(2)Beginnt beispielsweise in mm/mmap.c, weil es Teil des Speicherverwaltungssubsystems ("mm") des Kernels ist.

Ich empfehle Ihnen eine Kopie von " Understanding the Linux Kernel " von Bovet und Cesati.


Sehr gute Antwort. Ein Punkt zu dem von Ihnen erwähnten Buch "Den Linux-Kernel verstehen". Ich habe es nicht, aber seit dem Erscheinungsdatum (2000) und dem Inhaltsverzeichnis (vor Ort) scheint es mir, dass es ungefähr 2,2 Kernel plus einige Erkenntnisse aus 2,4 Kerneln sind (aber ich irre mich). Meine Frage ist: gibt es ein gleichwertiges Buch, das 2.6 Kernel-Interna behandelt? (oder noch besser, dass 2.2, 2.4 und 2.6 abdecken)?
DavAlPi

2
@DavAlPi: Soweit ich weiß, ist Bovet & Cesati immer noch das beste Einzelbuch zu diesem Thema. Wenn ich es mit mehr aktuellem Material ergänzen muss, gehe ich in das DocumentationUnterverzeichnis des Quellbaums für den Kernel, mit dem ich arbeite.
Warren Young

1
Tatsächlich ist open (2) eine varargs-Funktion. Es gibt nur zwei Möglichkeiten, es aufzurufen. Die Manpage dokumentiert es auf diese Weise. Der eigentliche Prototyp ...enthält die Funktion varargs. Dies wird natürlich auf libc-Ebene implementiert. Es kann entweder 0 oder einen Garbage-Wert an die Kernel-ABI übergeben, wenn der dritte Parameter nicht verwendet wird.
Random832

"Es ist etwas, das Sie nicht verstehen müssen". World wäre ein besserer Ort, wenn diese Art von Satz im Stack-Exchange-Netzwerk nicht zu finden wäre.
Petr

84

Dies beantwortet Ihre Frage wahrscheinlich nicht direkt, aber ich fand stracees wirklich cool, zu versuchen, die zugrunde liegenden Systemaufrufe in Aktion zu verstehen, die selbst für die einfachsten Shell-Befehle gemacht sind. z.B

strace -o trace.txt mkdir mynewdir

Die Systemaufrufe für den Befehl mkdir mynewdirwerden zu Ihrer Freude in die Datei trace.txt geschrieben.


5
+1 Ordentlicher Trick! Ich hatte das noch nie benutzt
David Oneill

3
Besser noch, erstellen Sie die Ausgabedatei trace.strace und öffnen Sie sie in VIM. VIM hebt es hervor und erleichtert so das Lesen.
Marcin

55

Ein guter Ort, um die Linux -Kernelquelle zu lesen, ist der Linux-Querverweis (LXR) ¹. Suchanfragen liefern zusätzlich zu den Ergebnissen der Freitextsuche typisierte Übereinstimmungen (Funktionsprototypen, Variablendeklarationen usw.). Sie sind also handlicher als ein Grep (und auch schneller).

LXR erweitert keine Präprozessordefinitionen. Bei Systemaufrufen wird der Name vom Präprozessor überall verfälscht. Die meisten (alle?) Systemaufrufe werden jedoch mit einer SYSCALL_DEFINExder Makrofamilien definiert. Da es mkdirzwei Argumente braucht, führt eine Suche SYSCALL_DEFINE2(mkdirnach zur Deklaration des mkdirSyscalls :

SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)
{
    return sys_mkdirat(AT_FDCWD, pathname, mode);
}

OK, das sys_mkdiratheißt, es ist der mkdiratSyscall. Wenn Sie also darauf klicken, gelangen Sie nur zur Deklaration in include/linux/syscalls.h, aber die Definition befindet sich direkt darüber.

Die Hauptaufgabe von mkdiratist das Aufrufen vfs_mkdir(VFS ist die generische Dateisystemebene). Wenn Sie darauf klicken, werden zwei Suchergebnisse angezeigt: die Deklaration in include/linux/fs.hund die Definition einige Zeilen darüber. Die Hauptaufgabe vfs_mkdirist es, das Dateisystem-spezifische Implementierung zu nennen: dir->i_op->mkdir. Um herauszufinden, wie dies implementiert wird, müssen Sie sich an die Implementierung des einzelnen Dateisystems wenden, und es gibt keine feste Regel - es könnte sogar ein Modul außerhalb des Kernelbaums sein.

¹ LXR ist ein Indexierungsprogramm. Es gibt mehrere Websites, die eine Schnittstelle zu LXR bieten, mit geringfügig unterschiedlichen Sätzen bekannter Versionen und geringfügig unterschiedlichen Webschnittstellen. Sie neigen dazu, zu kommen und zu gehen. Wenn die von Ihnen verwendete also nicht verfügbar ist, führen Sie eine Websuche nach "Linux-Querverweis" durch, um eine andere zu finden.


Das ist eine verdammte Ressource. Gute Antwort.
Stabledog

"Internal Server Error" im Link von linux.no .
Fredrick Gauß

@FredrickGauss Für eine Weile war lxr.linux.no die schönste Schnittstelle zu LXR, aber es gab häufige Ausfallzeiten. Jetzt denke ich, dass es endgültig vorbei ist. Ich habe den ersten Link zu einer anderen LXR-Schnittstelle ausgetauscht.
Gilles

21

Systemaufrufe werden normalerweise in das SYSCALL_DEFINEx()Makro eingebunden, weshalb ein einfacher grepBenutzer sie nicht findet:

fs/namei.c:SYSCALL_DEFINE2(mkdir, const char __user *, pathname, int, mode)

Der endgültige Funktionsname nach dem Erweitern des Makros lautet sys_mkdir. Das SYSCALL_DEFINEx()Makro fügt Boilerplate-Elemente wie Trace-Code hinzu, den jede Syscall-Definition haben muss.


17

Hinweis: Die .h-Datei definiert die Funktion nicht. Es wird erklärt , dass H - Datei und (implementiert) an anderer Stelle definiert. Auf diese Weise kann der Compiler Informationen zur Funktionssignatur (Prototyp) einschließen, um die Typüberprüfung von Argumenten zu ermöglichen und die Rückgabetypen mit beliebigen aufrufenden Kontexten in Ihrem Code abzugleichen.

Im Allgemeinen werden .h (Header) -Dateien in C verwendet, um Funktionen zu deklarieren und Makros zu definieren.

mkdirInsbesondere handelt es sich um einen Systemaufruf. Möglicherweise gibt es einen GNU libc- Wrapper um diesen Systemaufruf (mit ziemlicher Sicherheit sogar). Die wahre Kernel-Implementierung von mkdirkann durch Durchsuchen der Kernel-Quellen und insbesondere der Systemaufrufe gefunden werden.

Beachten Sie, dass für jedes Dateisystem auch eine Art Verzeichniserstellungscode implementiert wird. Die VFS-Schicht (Virtual Filesystem) bietet eine gemeinsame API, in die die Systemaufrufschicht zugreifen kann. Jedes Dateisystem muss Funktionen registrieren, in die die VFS-Ebene aufgerufen werden kann. Auf diese Weise können verschiedene Dateisysteme ihre eigene Semantik für die Struktur von Verzeichnissen implementieren (z. B. wenn sie mithilfe eines Hash-Schemas gespeichert werden, um die Suche nach bestimmten Einträgen effizienter zu gestalten). Ich erwähne dies, weil Sie wahrscheinlich über diese dateisystemspezifischen Verzeichniserstellungsfunktionen stolpern, wenn Sie den Linux-Kernel-Quellbaum durchsuchen.


8

Keine der gefundenen Implementierungen stimmt mit dem Prototyp in sys / stat.h überein. Wäre es möglicherweise erfolgreicher, nach einer Include-Anweisung mit dieser Header-Datei zu suchen?


1
Die Implementierung (wie in sys / stat.h beschrieben) ist Sache von userland und libc. Das Kernel-interne Zeug (wie es wirklich gemacht wird) ist das Kernel-interne Geschäft. Bei aller Sorgfalt des Kernel-Hackers könnte die interne Funktion xyzzy heißen und 5 Parameter annehmen. Es ist die Aufgabe von libc, den Userland-Aufruf anzunehmen, ihn in die erforderlichen Kernel-Beschwörungsformeln zu übersetzen, ihn zu versenden und alle Ergebnisse zu sammeln.
Vonbrand

6

Hier sind ein paar wirklich großartige Blog-Posts, die verschiedene Techniken zum Aufspüren von Kernel-Quellcode auf niedriger Ebene beschreiben.


12
Bitte posten Sie nicht nur Links zu Blogs oder Foren, sondern fassen Sie deren Inhalt zusammen, damit die Leser sehen können, worum es geht, und lassen Sie nichts übrig, wenn die Websites verschwinden. In Ihrem ersten Link geht es auch um libc, was für diese Frage nicht relevant ist.
Gilles
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.