GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Best Practices für die Programmierung von Linux-Systemen in C-Sprache – Teil 1

Während der Entwicklung eines Programms muss der Programmierer mehrere Dinge im Auge behalten, wie zum Beispiel, dass der Code nicht komplex sein sollte, dh er sollte wartbar sein, Portabilität ist ein weiterer Bereich, der im Auge behalten werden muss. Wir sehen also, dass es einige gute Praktiken gibt, die der Programmierer befolgen sollte, um einen guten Code zu produzieren. Hier in diesem Artikel konzentrieren wir uns auf einige gute Praktiken, die der Programmierer befolgen sollte, während er mit Systemaufrufen unter Linux arbeitet.

Was ist ein Systemaufruf?

Ein Systemaufruf ist ein spezieller Funktionsaufruf, der ausgeführt wird, um einen Dienst vom Kernel anzufordern. Der angeforderte Dienst könnte darin bestehen, einen neuen Prozess zu erstellen, auf Hardware wie Festplatte usw. zuzugreifen. Wenn ein Systemaufruf getätigt wird, wechselt die Ausführung vom Benutzermodus in den Kernelmodus, und wenn der erforderliche Dienst vom Kernel bereitgestellt wird, dann der Ausführung wechselt zurück in den Benutzermodus. Beispiele für Systemaufrufe könnten fork(), read(), write() usw. sein.

Umgang mit Systemaufrufen

Die folgenden Punkte sollten beim Umgang mit Systemaufrufen beachtet werden:

  • Der Programmierer sollte über genaue Kenntnisse des Systemaufrufs verfügen. Wie genau es tut, welche Systemressourcen es verwendet, welche Art von Argumenten es erwartet und insbesondere in welchen Fällen es fehlschlägt.
  • Die meisten Linux-Systemaufrufe geben einen Fehlercode zurück, wenn sie fehlschlagen. Diese Fehlercodes können je nach Art des Fehlers, der den Fehler verursacht hat, variieren. Daher sollte eine ordnungsgemäße Fehlerbehandlung vorhanden sein, damit jede Art von Fehler ordnungsgemäß behandelt und klar eskaliert wird (entweder an den Benutzer oder das übergeordnete Modul).
  • Für die gründliche Kenntnis des Systemaufrufs und der Fehlercodes, die er zurückgibt, würde ich dringend empfehlen, die Manpage dieses spezifischen Systemaufrufs durchzugehen. Man-Seiten sind die besten Referenzen für den Anfang und entwickeln ein gutes grundlegendes Verständnis für jeden Systemaufruf in Linux.

Allgemeine Systemaufruffehler

Obwohl das Scheitern eines Systemaufrufs von der Art des Fehlers abhängen kann, der während der Ausführung des Systemaufrufs aufgetreten ist, ist hier eine Liste von Gründen, die hauptsächlich zu Systemaufruffehlern beitragen:

  •  Wenn ein Systemaufruf versucht, auf die Systemhardware zuzugreifen, und die Hardware aus irgendeinem Grund nicht verfügbar ist oder angenommen wird, dass die Hardware fehlerhaft ist, schlägt der Systemaufruf in diesem Fall fehl.
  • Wenn während der Ausführung eines Systemaufrufs ein Signal mit hoher Priorität auftritt, kann dies auch dazu führen, dass die Ausführung des Systemaufrufs fehlschlägt.
  • Es gibt Situationen, in denen ein Programm durch einen Systemaufruf versucht, eine bestimmte Aufgabe auszuführen, die spezielle oder Root-Rechte erfordert. Wenn das Programm diese Rechte nicht hat, schlägt auch der Systemaufruf fehl.
  • Das Übergeben ungültiger Argumente ist ein weiterer sehr häufiger Grund dafür, dass Systemaufrufe fehlschlagen.
  • Angenommen, ein Systemaufruf wird durchgeführt, um Speicher vom Heap anzufordern, und aus irgendeinem Grund ist das System nicht in der Lage, dem anfordernden Prozess, der den Systemaufruf getätigt hat, Speicher zuzuweisen, in diesem Fall schlägt auch der Systemaufruf fehl.
  • li>

Die obige Liste ist nicht vollständig, da es zahlreiche andere Gründe geben kann, aus denen ein Systemaufruf fehlschlagen kann.

Arbeiten mit Fehlercodes

Wie bereits besprochen, gibt jeder Systemaufruf einen spezifischen Fehlercode für jeden Fehlertyp zurück, auf den er gestoßen ist (der den Ausfall des Systemaufrufs verursacht hat). Daher ist das Identifizieren und Kommunizieren der Fehlerinformationen eine sehr wichtige Aufgabe der Programmierung. Im Allgemeinen geben die meisten Systemaufrufe bei Erfolg '0' und bei Fehlern ungleich Null zurück, aber die Systemaufrufe, die einen Zeiger auf einen Speicher zurückgeben (wie malloc() ), geben bei Fehlern '0' oder NULL und bei Erfolg einen Nicht-Null-Zeigerwert zurück .

HINWEIS:Die obige Beobachtung gilt möglicherweise nicht für alle Systemaufrufe. Es könnte durchaus einige Ausnahmen geben.

Um also auf die Fehlercodes zurückzukommen, können sie, wie besprochen, wichtige Informationen über die Ursache des Ausfalls eines Systemaufrufs liefern. Da nun jeder Fehlercode einem bestimmten Grund zugeordnet ist, kann das Programm eine Karte der Fehlercodes und den Text haben, der die Ursache des Fehlers beschreibt. Dies ist jedoch sehr ineffizient und nicht praktikabel, da dies zu einer Menge Zuordnung für jeden im Programm verwendeten Systemaufruf führen würde. Nun stellt sich also die Frage, wie dies effizienter erreicht werden könnte?

Die ‘errno’-Variable

Aus der Manpage dieser Variablen:

Die -Headerdatei definiert die Integer-Variable errno, die von Systemaufrufen und einigen Bibliotheksfunktionen im Fehlerfall gesetzt wird, um anzuzeigen, was schief gelaufen ist. Sein Wert ist nur dann von Bedeutung, wenn der Rückgabewert des Aufrufs einen Fehler anzeigt (d. h. -1 bei den meisten Systemaufrufen; -1 oder NULL bei den meisten Bibliotheksfunktionen); eine erfolgreiche Funktion darf die Fehlernummer ändern.
Gültige Fehlernummern sind alle ungleich Null; errno wird niemals durch einen Systemaufruf oder eine Bibliotheksfunktion auf Null gesetzt. Für einige Systemaufrufe und Bibliotheksfunktionen (z. B. getpriority) ist -1 eine gültige Rückgabe bei Erfolg. In solchen Fällen kann eine erfolgreiche Rückgabe von einer Fehlerrückgabe unterschieden werden, indem errno vor dem Aufruf auf Null gesetzt wird und dann, wenn der Aufruf einen Status zurückgibt, der darauf hinweist, dass möglicherweise ein Fehler aufgetreten ist, überprüft wird, ob errno einen nicht- Null Wert. errno ist vom ISO-C-Standard als modifizierbarer lvalue vom Typ int definiert und darf nicht explizit deklariert werden; errno kann ein Makro sein. errno ist Thread-lokal; das Setzen in einem Thread hat keinen Einfluss auf seinen Wert in einem anderen Thread.

So. Aus der obigen Beschreibung wird deutlich, dass es ein sehr praktisches Werkzeug ist, wenn es um die Fehlerbehandlung von Systemaufrufen unter Linux geht, und uns viel harte Arbeit ersparen kann. Hüten Sie sich jedoch davor, diese Variable in einem Multithread-Programm zu verwenden, da sie lokal für einen Thread ist und daher auf jede Änderung des Werts von errno in einem Thread nicht in einem anderen Thread zugegriffen werden kann.

Die strerror()-API

Nun, ein Problem bei der Verwendung von nur errno ist, dass es immer noch nur ein ganzzahliger Wert ist. Eine Beschreibung ist immer hilfreicher beim Protokollieren oder bei der Weitergabe der Fehlerursache an den Benutzer. Es muss also eine Karte mit Fehlercodes und der Ursache geben, der sie zugeordnet sind. Hier kommt die ‘strerror()’-API. Diese Funktion nimmt die errno-Variable als Argument und gibt einen Zeiger auf einen String zurück, der die Beschreibung der Ursache enthält, der der Fehlercode zugeordnet ist.

#include <string.h>
char *strerror(int errnum);

Andere Varianten dieser Funktion sind ebenfalls verfügbar. Weitere Informationen finden Sie auf der Manpage für diese API.

HINWEIS:Interessierte Leser können auch die perror()-API durchlaufen. Es wird verwendet, um die Fehlermeldung für einen Systemaufruffehler bei Standardfehler auszugeben.

Ein Beispiel

Nehmen wir ein Beispiel, um die Verwendung von errno und strerror()

zu demonstrieren
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
    int fd = -1;

    // Always Reset errno before use.
    errno = 0;

    // Make sure you are opening a file that does not exist
    fd = open("abcd",O_RDONLY);
    if(fd == -1)
    {
        // Seems like some error occured. Use strerror to print it
        printf("\nStrerror() says -> [%s]\n",(char*)strerror(errno));
        return 1;
    }
    return 0;
}

Im Code oben:

  • errno wird mit '0' initialisiert, da es nicht garantiert ist, dass es anfangs null ist.
  • Öffne eine nicht existierende Datei, damit der Systemaufruf open() fehlschlägt.
  • Jetzt wird die strerror()-API verwendet, um die Fehlermeldung basierend auf dem Fehlercode auszugeben.

Wenn das obige Programm ausgeführt wird:

$ ./strerror
Strerror() says -> [No such file or directory]

Wir sehen also, dass wir in der Ausgabe eine aussagekräftige Fehlermeldung anstelle eines Fehlercodes sehen.


Linux
  1. Die 22 besten Linux-Texteditoren zum Programmieren und Codieren

  2. Linux-Server-Härtung – Best Practices

  3. Wählen Sie das beste Dateisystem für Ihr Linux

  4. 10 Bestes IPTV für Linux/Ubuntu-Systeme im Jahr 2022

  5. Linux-Systemaufruftabelle oder Cheatsheet für Assembly

Die 20 besten Openbox-Designs für Linux-Systeme

Top 10 der besten Geometriesoftware für Linux-Systeme

Die 15 besten Physik-Tools für Linux-Systeme zum Physikunterricht

Die 15 besten Dokumentenverwaltungssysteme für Linux-Systeme

Top 15 der besten Biologie-Tools für Linux-Systeme

15 beste Videobearbeitungssoftware für Linux-Systeme