Linux System Call Tutorial mit c

Linux System Call Tutorial mit c
In unserem letzten Artikel über Linux -Systemaufrufe habe ich einen Systemaufruf definiert, die Gründe erörtert, aus denen man sie in einem Programm verwenden könnte, und in ihre Vor- und Nachteile eingewiesen wurde. Ich habe sogar ein kurzes Beispiel in der Montage innerhalb von C gegeben. Es illustrierte den Punkt und beschrieben, wie man den Anruf tätigte, tat aber nichts Produktives. Nicht gerade eine aufregende Entwicklungsübung, aber sie illustrierte den Punkt.

In diesem Artikel werden wir tatsächliche Systemaufrufe verwenden, um echte Arbeiten in unserem C -Programm zu erledigen. Wir werden zunächst überprüft, ob Sie einen Systemanruf verwenden müssen, und dann ein Beispiel mithilfe des Anrufs sendFile () angeben, mit dem die Leistung der Dateikopie drastisch verbessert werden kann. Schließlich gehen wir einige Punkte durch.

Benötigen Sie einen Systemanruf??

Obwohl es unvermeidlich ist, verwenden Sie irgendwann in Ihrer C -Entwicklungskarriere einen Systemaufruf Deine Bedürfnisse.

Die GLIBC-Standardbibliothek bietet ein plattformübergreifendes, gut getestetes Framework, um Funktionen auszuführen, die sonst systemspezifische Systemaufrufe erfordern würden. Zum Beispiel können Sie eine Datei mit fSCANF (), fread (), getC () usw. lesen., oder Sie können den Read () Linux -Systemaufruf verwenden. Die Glibc -Funktionen bieten mehr Funktionen (i.e. Bessere Fehlerhandhabung, formatierte IO usw.) und arbeitet an jedem System, das glibc unterstützt.

Andererseits gibt es Zeiten, in denen kompromisslose Leistung und genaue Ausführung kritisch sind. Die Wrapper, die Fread () bietet. Darüber hinaus möchten oder benötigen Sie die zusätzlichen Funktionen, die der Wrapper bietet. In diesem Fall werden Sie am besten mit einem Systemanruf bedient.

Sie können auch Systemaufrufe verwenden, um Funktionen auszuführen, die noch nicht von GLIBC unterstützt werden. Wenn Ihre Kopie von GLIBC auf dem neuesten Stand ist, ist dies kaum ein Problem, aber die Entwicklung von älteren Verteilungen mit neueren Kerneln erfordert möglicherweise diese Technik.

Nachdem Sie die Haftungsausschlüsse, Warnungen und potenziellen Umleitungen gelesen haben, lassen Sie uns nun einige praktische Beispiele auselt.

Auf welcher CPU sind wir?

Eine Frage, die die meisten Programme wahrscheinlich nicht denken, aber dennoch eine gültige. Dies ist ein Beispiel für einen Systemaufruf, der nicht mit GLIBC dupliziert werden kann und nicht mit einem GLIBC -Wrapper bedeckt ist. In diesem Code rufen wir den GetCPU () -Aufruf direkt über die Funktion syScall () auf. Die Systemsystemfunktion funktioniert wie folgt:

syscall (sys_call, arg1, arg2,…);

Das erste Argument, SYS_CALL, ist eine Definition, die die Anzahl des Systemaufrufs darstellt. Wenn Sie Sys/Syscall einschließen.H, diese sind enthalten. Der erste Teil ist sys_ und der zweite Teil ist der Name des Systemaufrufs.

Argumente für den Anruf gehen in Arg1, Arg2 oben. Einige Anrufe erfordern mehr Argumente, und sie werden in der Reihenfolge ihrer Mannseite fortfahren. Denken Sie daran, dass die meisten Argumente, insbesondere für Renditen, Hinweise benötigen, um Arrays zu charieren oder über die Malloc -Funktion zugeordnet zu werden.

Beispiel 1.C

#enthalten
#enthalten
#enthalten
#enthalten
int main ()
nicht signierte CPU, Knoten;
// den aktuellen CPU -Kern und den Numa -Knoten über einen Systemaufruf abrufen
// Beachten Sie, dass dies keine GLIBC -Wrapper hat
syScall (sys_getCpu & cpu & node, null);
// Informationen anzeigen
printf ("Dieses Programm wird auf CPU Core %U und Numa Node %U ausgeführt.\ n \ n ", CPU, Knoten);
Rückkehr 0;

Zu kompilieren und zu rennen:
GCC Beispiel1.C -o Beispiel1
./Beispiel 1

Für interessantere Ergebnisse können Sie Threads über die PThreads -Bibliothek drehen und diese Funktion dann aufrufen, um zu sehen, welcher Prozessor Ihr Thread ausgeführt wird.

SendFile: Überlegene Leistung

SendFile bietet ein hervorragendes Beispiel für die Verbesserung der Leistung durch Systemaufrufe. Die Funktion "sendFile () kopiert Daten von einem Dateideskriptor zu einem anderen. Anstatt mehrere Funktionen von Freead () und FWrite () zu verwenden.

In diesem Beispiel werden wir 64 MB Daten von einer Datei in eine andere kopieren. In einem Test verwenden wir die Standard -Lese-/Schreibmethoden in der Standardbibliothek. Im anderen.

Test1.C (Glibc)

#enthalten
#enthalten
#enthalten
#enthalten
#define buffer_size 67108864
#define buffer_1 "buffer1"
#define buffer_2 "buffer2"
int main ()
Datei *Fout, *Fin;
printf ("\ ni/o Test mit herkömmlichen Glibc -Funktionen.\ n \ n ");
// Schnapp dir einen Puffer Buffer_size.
// Der Puffer enthält zufällige Daten, aber das kümmert uns nicht darum.
printf ("64 -MB -Puffer zuweisen:");
char *buffer = (char *) malloc (buffer_size);
printf ("Done \ n");
// Schreiben Sie den Puffer in Fout
printf ("Daten zum ersten Puffer schreiben:");
fout = fopen (buffer_1, "wb");
fWrite (Puffer, sizeof (char), buffer_size, fout);
fcLose (fout);
printf ("Done \ n");
printf ("Kopieren von Daten von der ersten Datei auf die zweite Datei:");
Fin = fopen (buffer_1, "rb");
fout = fopen (buffer_2, "wb");
FreeD (Puffer, Sizeof (char), buffer_size, fin);
fWrite (Puffer, sizeof (char), buffer_size, fout);
fcLose (Fin);
fcLose (fout);
printf ("Done \ n");
printf ("Freeing Puffer:");
frei (Puffer);
printf ("Done \ n");
printf ("Dateien löschen:");
entfernen (buffer_1);
entfernen (buffer_2);
printf ("Done \ n");
Rückkehr 0;

Test2.C (Systemaufrufe)

#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#enthalten
#define buffer_size 67108864
int main ()
Int Fout, Fin;
printf ("\ ni/o Test mit SendFile () und zugehörigen Systemaufrufen.\ n \ n ");
// Schnapp dir einen Puffer Buffer_size.
// Der Puffer enthält zufällige Daten, aber das kümmert uns nicht darum.
printf ("64 -MB -Puffer zuweisen:");
char *buffer = (char *) malloc (buffer_size);
printf ("Done \ n");
// Schreiben Sie den Puffer in Fout
printf ("Daten zum ersten Puffer schreiben:");
fout = open ("buffer1", o_rdonly);
write (fout, & buffer, buffer_size);
schließen (fout);
printf ("Done \ n");
printf ("Kopieren von Daten von der ersten Datei auf die zweite Datei:");
fin = open ("buffer1", o_rdonly);
fout = open ("buffer2", o_rdonly);
sendFile (fout, fin, 0, buffer_size);
schließen (Fin);
schließen (fout);
printf ("Done \ n");
printf ("Freeing Puffer:");
frei (Puffer);
printf ("Done \ n");
printf ("Dateien löschen:");
Unglink ("Buffer1");
UNLINK ("Buffer2");
printf ("Done \ n");
Rückkehr 0;

Kompilieren und Ausführen von Tests 1 & 2

Um diese Beispiele zu erstellen, benötigen Sie die auf Ihrer Verteilung installierten Entwicklungstools. Auf Debian und Ubuntu können Sie dies mit:

APT Installieren Sie Build-Wesentials

Dann kompilieren mit:

GCC Test1.C -o Test1 && GCC Test2.C -o Test2

Um beide auszuführen und die Leistung zu testen, rennen Sie:

Zeit ./test1 && Zeit ./test2

Sie sollten solche Ergebnisse erzielen:

E/A -Test mit herkömmlichen GLIBC -Funktionen.

Zuweisung von 64 MB -Puffer: fertig
Daten zum ersten Puffer schreiben: fertig
Kopieren von Daten von der ersten Datei auf die zweite Kopie: Fertig
Befreiungspuffer: fertig
Dateien löschen: fertiggestellt
echt 0m0.397s
Benutzer 0m0.000s
sys 0m0.203s
E/A -Test mit SendFile () und zugehörigen Systemaufrufen.
Zuweisung von 64 MB -Puffer: fertig
Daten zum ersten Puffer schreiben: fertig
Kopieren von Daten von der ersten Datei auf die zweite Kopie: Fertig
Befreiungspuffer: fertig
Dateien löschen: fertiggestellt
echt 0m0.019s
Benutzer 0m0.000s
sys 0m0.016S

Wie Sie sehen können, wird der Code, der die Systemaufrufe verwendet, viel schneller als das GLIBC -Äquivalent.

Dinge, an die man sich erinnern sollte

Systemaufrufe können die Leistung erhöhen und zusätzliche Funktionen liefern, aber sie sind nicht ohne ihre Nachteile. Sie müssen die Vorteile, die Systemanrufe gegen die mangelnde Plattform -Portabilität und manchmal reduzierte Funktionen im Vergleich zu Bibliotheksfunktionen abgeben, abwägen.

Wenn Sie einige Systemaufrufe verwenden, müssen Sie darauf achten, Ressourcen zu verwenden, die von Systemanrufen zurückgegeben werden, anstatt Bibliotheksfunktionen. Beispielsweise sind die für die Funktionen von GLIBC verwendete Dateistruktur für GLIBCs FOPEN (), FREED (), FWRITE () und FCLOSE () nicht die gleichen wie die Dateideskriptornummer des Open () -Systemaufrufs (als Ganzzahl zurückgegeben). Das Mischen dieser kann zu Problemen führen.

Im Allgemeinen haben Linux -Systemaufrufe weniger Stoßfänger als GLIBC -Funktionen. Während es wahr ist, dass Systemaufrufe einige Fehlerbehandlungen und Berichterstattung haben, erhalten Sie detailliertere Funktionen von einer GLIBC -Funktion.

Und schließlich ein Wort zur Sicherheit. Systemaufrufe direkt mit dem Kernel Schnittstelle. Der Linux -Kernel hat einen umfangreichen Schutz gegen Shenanigans aus dem Benutzerland, aber unentdeckte Fehler gibt es. Vertrauen Sie nicht, dass ein Systemanruf Ihre Eingaben validiert oder Sie von Sicherheitsproblemen isoliert wird. Es ist ratsam, sicherzustellen, dass die Daten, die Sie einem Systemaufruf übergeben. Dies ist natürlich ein guter Rat für einen API -Anruf, aber Sie können nicht vorsichtig sein, wenn Sie mit dem Kernel arbeiten.

Ich hoffe, Sie haben diesen tieferen Tauchgang in das Land des Linux -Systems genossen. Eine vollständige Liste von Linux -Systemaufrufen finden Sie in unserer Masterliste.