Was ist mit "einem Systemaufruf" gemeint, wenn nicht die Implementierung in der Programmiersprache?

14

Ich möchte den Begriff "Systemaufruf" verstehen. Ich weiß, dass Systemaufrufe verwendet werden, um Kernel-Services von einer Userspace-Anwendung abzurufen.

Der Teil, den ich klären muss, ist der Unterschied zwischen einem "Systemaufruf" und einer "C-Implementierung des Systemaufrufs".

Hier ist ein Zitat, das mich verwirrt:

Auf Unix-ähnlichen Systemen ist diese API in der Regel Teil einer Implementierung der C-Bibliothek (libc) wie glibc, die Wrapper-Funktionen für die Systemaufrufe bereitstellt, die häufig denselben Namen haben wie die von ihnen aufgerufenen Systemaufrufe

Was sind die "Systemaufrufe, die sie aufrufen"? Wo ist ihre Quelle? Kann ich sie direkt in meinen Code einfügen?

Ist der "Systemaufruf" im Allgemeinen nur eine POSIX-definierte Schnittstelle, aber um die Implementierung tatsächlich zu sehen, könnte man die C-Quelle untersuchen und darin sehen, wie der tatsächliche Benutzerbereich zur Kernel-Kommunikation tatsächlich verläuft?

Hintergrundinformation: Ich versuche zu verstehen, ob am Ende jede c-Funktion mit Geräten von interagiert /dev.

TheMeaningfulEngineer
quelle

Antworten:

21

Systemaufrufe an sich sind ein Konzept. Sie stellen Aktionen dar, zu deren Ausführung Prozesse den Kernel auffordern können.

Diese Systemaufrufe werden im Kernel des UNIX-ähnlichen Systems implementiert. Diese Implementierung (in C und in asm für Kleinteile geschrieben) führt die Aktion tatsächlich im System aus.

Dann verwenden Prozesse eine Schnittstelle, um das System nach der Ausführung der Systemaufrufe zu fragen. Diese Schnittstelle wird von POSIX festgelegt. Dies ist eine Reihe von Funktionen der C-Standardbibliothek. Sie sind tatsächlich Wrapper, führen möglicherweise einige Überprüfungen durch und rufen dann eine systemspezifische Funktion im Kernel auf, die sie anweist, die für den Systemaufruf erforderlichen Aktionen auszuführen. Und der Trick ist, dass die Funktionen, die die Schnittstelle darstellen, die gleichen Namen haben wie die Systemaufrufe selbst und häufig direkt als "die Systemaufrufe" bezeichnet werden.

Sie können die Funktion im Kernel aufrufen, die den Systemaufruf direkt über den systemspezifischen Mechanismus ausführt. Das Problem ist, dass es Ihren Code absolut nicht portierbar macht.

Ein Systemaufruf lautet also:

  • Ein Konzept, eine Abfolge von Aktionen, die vom Kernel ausgeführt werden, um einem Benutzerprozess einen Dienst anzubieten
  • die Funktion der C-Standardbibliothek, die Sie in Ihrem Code verwenden sollten, um diesen Dienst vom Kernel zu erhalten.
lgeorget
quelle
1
Haben Sie ein Beispiel für die "Wrapper-Funktion" und einen tatsächlichen Systemaufruf? (Dateipfade unter Linux oder Links zu Quellen)
TheMeaningfulEngineer
3
Dies ist beispielsweise die Implementierung des getpidSystemaufrufs im Linux-Kernel: lxr.free-electrons.com/source/kernel/timer.c?v=2.6.35#L1337 . Und dies ist die Wrapper-Funktion in der GNU C-Standardbibliothek glibc-2.19: fossies.org/dox/glibc-2.19/… .
Lgeorget
@Igeorget: Ihre Links funktionieren nicht mehr. Aktualisierter Link für die Kernel-Implementierung: github.com/torvalds/linux/blob/… . Ich konnte nicht finden, was glibc heutzutage macht, dieser Code ist unmöglich zu navigieren.
rchard2scout
6

Ein Systemaufruf ist eine Möglichkeit, Ihr Betriebssystem (Kernel) aufzufordern, bestimmte Vorgänge für Ihr Programm auszuführen, die das Programm selbst nicht ausführen kann (oder die einfach unpraktisch sind). Der Grund dafür, dass einige Operationen nicht ausgeführt werden können, besteht normalerweise darin, dass die Integrität des Systems beeinträchtigt werden kann, wenn ein zufälliges Programm sie ausführt, z.

POSIX definiert eine Schnittstelle für Programme, bestimmte Funktionen, die Ihr Programm aufrufen kann. Einige davon werden mehr oder weniger direkt in Systemaufrufe übersetzt, andere erfordern eine genauere Bearbeitung. Es ist die Laufzeit für Ihre Sprache, z. B. die C-Bibliothek, die für die Bereitstellung der POSIX-Schnittstelle verantwortlich ist und die Argumente paketiert und die Ergebnisse an den Aufrufer zurückgibt.

Unixy-Systeme bieten POSIX-Schnittstellen mehr oder weniger direkt als Systemaufrufe an. Normalerweise gibt es eine Möglichkeit, Systemaufrufe direkt aufzurufen. syscall(2)Informieren Sie sich über die Verwendung dieser Funktion unter Linux.

vonbrand
quelle
1
Sie berühren einen wichtigen Punkt, den die anderen Antworten nur auffrischen. Jede Funktion , die ein einigermaßen fähig Programmierer für sich selbst schreiben können (wie strlen, strcpy, sqrtund qsort) kann und wahrscheinlich ist im User - Space, aus einer Bibliothek geladen. (Meistens libc; mathematische Funktionen wie sqrtund die trigonometrischen und hyperbolischen Funktionen befinden sich wahrscheinlich in libm, der mathematischen Bibliothek.)… (Fortsetzung)
Scott
1
(Fortsetzung) ... Aber es gibt keine Möglichkeit , einen Benutzer seine eigene schreiben kann fork, killoder openFunktion, da diese den Zugang benötigen , um den Betriebssystemkern Speicherplatz ( zum Beispiel der Prozesstabelle) oder privilegierte Befehle (zB I / O). Daher muss sich der Code, der diese Funktionen ausführt, im Betriebssystemkern befinden. daher Systemfunktionen oder Systemaufrufe.
Scott
5

Sicher, lass uns die vielen Richtungen machen, aus denen wir diesen Elefanten betrachten können. Sache.

Der eigentliche Systemaufruf ist in Ihrem erstellten Programm der Maschinenbefehl, der die Privilegieneskalation in den Kernelmodus auslöst, und im Kernel selbst ist es der Code, den der Befehl aufruft. Der libc-Code (und jede Sprachlaufzeit) richtet die Maschinenregister und speicherinternen Parameter ein, von denen der Kernel-Code erwartet, dass sie gefunden werden. Dies kann aufgrund von Einschränkungen bei dieser Maschinenanweisung zu merkwürdigen Fehlern führen.

Einmal im OS-Code selbst, gibt es ein bisschen Spiegelung der maschinenspezifischen Dinge, die die Userland-Laufzeit erledigt hat, und dann einen ganz normalen Unterprogrammaufruf.
Wenn Sie genau sehen möchten, wie dies in einem vollständigen Betriebssystem funktioniert, ziehen Sie die Kernelquelle ( git clone https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/) und führen Sie z git grep -i system\ call. Ziehen Sie die Glibc-Quelle und tun Sie es ebenfalls.

jdoch
quelle
Stimmt, aber in Linux oder Glibc zu stöbern ist etwas
schwierig
3

Zumindest unter Linux funktioniert der Systemaufrufmechanismus unter den meisten Architekturen, indem einige speziell formatierte Daten (normalerweise eine Art Struktur) entweder in einigen Registern oder in vordefinierten Speicheradressen abgelegt werden.

Das Problem besteht jedoch darin, dass die CPU gezwungen wird, in den Kernel-Speicher zu wechseln, damit sie den privilegierten Kernel-Code ausführen kann, um den Aufruf zu bearbeiten. Dies geschieht, indem ein Fehler erzwungen wird (ein Fehler ist eine Division durch 0, ein undefinierter Überlauf oder ein Segfault usw.), wodurch der Kernel gezwungen wird, die Ausführung zu übernehmen, um den Fehler zu behandeln.

Normalerweise behandelt der Kernel Fehler, indem er entweder den verursachenden Prozess beendet oder einen vom Benutzer bereitgestellten Handler ausführt. Im Fall eines Systemaufrufs werden stattdessen die vordefinierten Register und Speicherorte überprüft, und wenn sie eine Systemaufrufanforderung enthalten, wird diese unter Verwendung der vom Benutzerprozess in der In-Memory-Struktur bereitgestellten Daten ausgeführt. Dies muss normalerweise mit einer speziell von Hand gefertigten Baugruppe durchgeführt werden. Um dem Benutzer die Verwendung des Systemaufrufs zu erleichtern, muss die C-Bibliothek des Systems diesen als Funktion verpacken. Unter http://man7.org/linux/man-pages/man2/syscall.2.html finden Sie Informationen zur Funktionsweise von syscalls und wie Sie sie dann ohne C-Wrapper aufrufen können.

Dies wird zu stark vereinfacht. Dies gilt nicht für alle Architekturen (mips verfügt über eine spezielle Syscall-Anweisung) und funktioniert nicht unbedingt auf allen Betriebssystemen gleich. Wenn Sie dennoch Kommentare oder Fragen haben, wenden Sie sich bitte an.

Geändert: Beachten Sie, dass es sich bei Ihrem Kommentar zu Dingen in / dev / tatsächlich um eine übergeordnete Schnittstelle zum Kernel handelt, nicht um eine niedrigere. Diese Geräte verwenden tatsächlich (ungefähr) 4 Systemaufrufe darunter. Das Schreiben in sie ist dasselbe wie ein Write-Systemaufruf, das Lesen eines Read-Systemaufrufs, das Öffnen / Schließen derselben wie beim Open- und Close-Systemaufruf und das Ausführen eines Ioctl-Befehls bewirkt einen speziellen Ioctl-Systemaufruf, der an sich eine Schnittstelle für den Zugriff auf eines der vielen Ioctl-Befehle des Systems darstellt Anrufe (spezielle, normalerweise gerätespezifische Anrufe mit zu enger Verwendung, um einen vollständigen Systemaufruf für sie zu schreiben).

Wertigkeit
quelle
1

Jedem Systemaufruf ist eine Ganzzahl zugeordnet. Diese Ganzzahl ist eine Funktion des Rückgabewerts des Systemaufrufs, der Anzahl der Argumente für den Systemaufruf und des Typs der Argumente. Diese Systemaufrufnummer ist nichts anderes als ein Offset zum globalen Systemaufrufvektor. Dieser Vektor, auf den nur im privilegierten Modus zugegriffen werden kann, enthält einen Zeiger auf geeignete Handler. Beim Aufrufen eines Systemaufrufs würde ein Software-Interrupt (Trap-Interrupt) generiert, daher würde ein Trap-Handler ausgeführt, der festlegt, welcher Systemaufruf aufgerufen werden soll. Dann würde der Kernel die Argumente des vom Benutzer, der sich auf dem Stapel befindet, übergebenen Systemaufrufs in die Prozessorregister kopieren, und nach Beendigung des angeforderten Dienstes würden die Daten von den Prozessorregistern zurück in den Stapel kopiert. Dies ist einer der Gründe, warum es für Systemaufrufe nur wenige Argumente gibt.

Naresh
quelle
Jeder Anruf (Vorgang) wird intern durch eine Nummer identifiziert, wahr. Die Anzahl hängt jedoch von der Operation ab , nicht vom Rückgabewert oder der Anzahl der Argumente. Eine Erklärung, wie es früher von Userland auf x86 funktioniert hat, finden Sie hier
vonbrand