EnglischFranzösischSpanisch

Ad


OnWorks-Favicon

erklären_lca2010 – Online in der Cloud

Führen Sie „explain_lca2010“ im kostenlosen Hosting-Anbieter OnWorks über Ubuntu Online, Fedora Online, den Windows-Online-Emulator oder den MAC OS-Online-Emulator aus

Dies ist der Befehl „explain_lca2010“, der beim kostenlosen Hosting-Anbieter OnWorks mit einer unserer zahlreichen kostenlosen Online-Workstations wie Ubuntu Online, Fedora Online, dem Windows-Online-Emulator oder dem MAC OS-Online-Emulator ausgeführt werden kann

PROGRAMM:

NAME/FUNKTION


erklärt_lca2010 – Kein Medium gefunden: Wenn es an der Zeit ist, mit dem Lesen aufzuhören strerror(3) ist
Verstand.

MOTIVATION


Die Idee zu libexplain kam mir bereits Anfang der 1980er Jahre. Immer wenn ein Systemaufruf erfolgt
gibt einen Fehler zurück, der Kernel weiß genau, was schief gelaufen ist ... und komprimiert dies in
weniger als 8 Bit Fehler. Der Benutzerbereich hat Zugriff auf dieselben Daten wie der Kernel
Es sollte für den Benutzerbereich möglich sein, genau herauszufinden, was den Fehler verursacht hat
return und verwenden Sie dies, um gute Fehlermeldungen zu schreiben.

Könnte es so einfach sein?

Fehler Nachrichten as Finesse
Gute Fehlermeldungen sind oft die „Ein-Prozent“-Aufgaben, die im Zeitplan verworfen werden
Druck drückt Ihr Projekt zusammen. Allerdings kann eine gute Fehlermeldung eine große,
unverhältnismäßige Verbesserung des Benutzererlebnisses, wenn der Benutzer in Angst verfällt
unbekanntes Gebiet, das normalerweise nicht anzutreffen ist. Das ist keine leichte Aufgabe.

Als Larvenprogrammierer sah der Autor das Problem mit (völlig korrekten) Fehlern nicht
Nachrichten wie diese:
Floating-Ausnahme (Core-Dump)
bis die alternative Nicht-Programmierer-Interpretation aufgezeigt wurde. Aber das ist nicht der Fall
Das Einzige, was mit den Unix-Fehlermeldungen nicht stimmt. Wie oft sehen Sie Fehlermeldungen wie:
$ ./dumm
Datei kann nicht geöffnet werden
$
An dieser Stelle gibt es für einen Entwickler zwei Möglichkeiten:

1.
Sie können einen Debugger ausführen, z gdb(1) oder

2.
du kannst benutzen strace(1) oder Fachwerk(1) nach innen schauen.

· Denken Sie daran, dass Ihre Benutzer möglicherweise nicht einmal Zugriff auf diese Tools haben, geschweige denn die Möglichkeit dazu
sie zu nutzen. (Es ist sehr lange her Unix Anfänger bedeutete „hat nur geschrieben dank One
Gerätetreiber".)

In diesem Beispiel jedoch mit strace(1) verrät
$ strace -e Trace=offen ./dumm
open("some/file", O_RDONLY) = -1 ENOENT (Keine solche Datei oder kein solches Verzeichnis)
Datei kann nicht geöffnet werden
$
Das sind wesentlich mehr Informationen, als die Fehlermeldung liefert. Typischerweise ist die
Der blöde Quellcode sieht so aus
int fd = open("etwas", O_RDONLY);
wenn (fd < 0)
{
fprintf(stderr, „Datei kann nicht geöffnet werden\n“);
wunsch(1);
}
Dem Benutzer wird nichts mitgeteilt welche Datei und teilt es dem Benutzer auch nicht mit welche Fehler. War die Datei
sogar dort? Gab es ein Berechtigungsproblem? Es zeigt Ihnen an, dass es versucht hat, ein zu öffnen
Datei, aber das war wahrscheinlich ein Zufall.

Schnappen Sie sich Ihren Hinweisstab und schlagen Sie damit den Larvenprogrammierer. Erzähl ihm davon Fehler(3).
Wenn Sie das Programm das nächste Mal verwenden, wird eine andere Fehlermeldung angezeigt:
$ ./dumm
open: Keine solche Datei oder kein solches Verzeichnis
$
Fortschritte, aber nicht das, was wir erwartet hatten. Wie kann der Benutzer das Problem beheben, wenn die Fehlermeldung angezeigt wird?
sagt ihm nicht, was das Problem war? Wenn wir uns die Quelle ansehen, sehen wir
int fd = open("etwas", O_RDONLY);
wenn (fd < 0)
{
perror("open");
wunsch(1);
}
Zeit für einen weiteren Lauf mit dem Hinweisstab. Diesmal dauert die Fehlermeldung einen Schritt
Vorwärts und einen Schritt zurück:
$ ./dumm
etwas: Keine solche Datei oder Ordner
$
Jetzt kennen wir die Datei, die es zu öffnen versuchte, werden aber nicht mehr darüber informiert, dass dies der Fall war XNUMXh geöffnet(2)
das ist gescheitert. In diesem Fall ist es wahrscheinlich nicht von Bedeutung, kann aber für wichtig sein
andere Systemaufrufe. Es könnte sein creat(2) stattdessen eine Operation, die dies impliziert
Es sind unterschiedliche Berechtigungen erforderlich.
const char *filename = "etwas";
int fd = open(filename, O_RDONLY);
wenn (fd < 0)
{
perror(Dateiname);
wunsch(1);
}
Der obige Beispielcode ist leider auch typisch für Nicht-Larven-Programmierer. Zeit
um unserem Padawan-Schüler davon zu erzählen strerror(3) Systemaufruf.
$ ./dumm
XNUMXh geöffnet etwas: Keine solche Datei oder Ordner
$
Dies maximiert die Informationen, die dem Benutzer präsentiert werden können. Der Code sieht aus wie
Dies:
const char *filename = "etwas";
int fd = open(filename, O_RDONLY);
wenn (fd < 0)
{
fprintf(stderr, "open %s: %s\n", Dateiname, strerror(errno));
wunsch(1);
}
Jetzt haben wir den Systemaufruf, den Dateinamen und die Fehlerzeichenfolge. Dies enthält alles
Informationen, die strace(1) gedruckt. Das ist so gut wie es nur geht.

Oder ist es?

Einschränkungen of Fehler und strerror
Das Problem, das der Autor bereits in den 1980er Jahren sah, bestand darin, dass die Fehlermeldung unvollständig war.
Bezieht sich „keine solche Datei oder kein solches Verzeichnis“ auf „einige”-Verzeichnis oder in das „Ding" Datei in
das "einige" Verzeichnis?

Ein kurzer Blick auf die Manpage für strerror(3) sagt:
strerror – Rückgabezeichenfolge, die die Fehlernummer beschreibt
Beachten Sie gut: Es beschreibt den Fehler Anzahl, nicht der Fehler.

Andererseits der Kernel kennt was der Fehler war. Es gab einen bestimmten Punkt in der
Kernel-Code, der durch eine bestimmte Bedingung verursacht wurde, bei der der Kernel-Code verzweigte und „Nein“ sagte.
Könnte ein User-Space-Programm den spezifischen Zustand herausfinden und einen besseren Fehler schreiben?
Botschaft?

Das Problem geht jedoch tiefer. Was passiert, wenn das Problem während des auftritt? lesen(2) System
Anruf, statt der XNUMXh geöffnet(2) anrufen? Es ist einfach für die damit verbundene Fehlermeldung
XNUMXh geöffnet(2) Um den Dateinamen einzuschließen, ist er genau dort. Aber um einen Dateinamen einfügen zu können
in dem damit verbundenen Fehler lesen(2) Systemaufruf, Sie müssen den gesamten Dateinamen übergeben
den Weg nach unten im Aufrufstapel sowie den Dateideskriptor.

Und hier ist das Problem: Der Kernel weiß bereits, wie die Datei heißt
Deskriptor zugeordnet ist. Warum sollte ein Programmierer alle redundanten Daten übergeben müssen?
den Weg nach unten in der Aufrufliste, nur um eine Fehlermeldung zu verbessern, die möglicherweise nie ausgegeben wird? In
In der Realität kümmern sich viele Programmierer nicht darum, und die daraus resultierenden Fehlermeldungen sind umso schlimmer
es.

Aber das war in den 1980er Jahren, auf einem PDP11, mit begrenzten Ressourcen und ohne gemeinsame Bibliotheken. Zurück
Dann ist kein Unix-Geschmack enthalten / proc sogar in rudimentärer Form, und die lsof(1) Programm
war über ein Jahrzehnt entfernt. Daher wurde die Idee als unpraktisch zurückgestellt.

Niveau Unendlichkeit Unterstützung
Stellen Sie sich vor, Sie befinden sich auf der Ebene der Unendlichkeitsunterstützung. In Ihrer Stellenbeschreibung steht, dass Sie das nie tun
jemals Ich muss mit den Benutzern sprechen. Warum gibt es dann immer noch einen ständigen Strom von Menschen, die etwas wollen?
Sie, der lokale Unix-Guru, um eine weitere Fehlermeldung zu entschlüsseln?

Seltsamerweise wurde es 25 Jahre später trotz eines einfachen Berechtigungssystems vollständig implementiert
Konsistenz haben die meisten Unix-Benutzer immer noch keine Ahnung, wie sie „Keine solche Datei oder kein solches Verzeichnis“ entschlüsseln sollen.
oder eine der anderen kryptischen Fehlermeldungen, die sie jeden Tag sehen. Oder zumindest kryptisch
Them.

Wäre es nicht schön, wenn der technische Support der ersten Ebene keine Fehlermeldungen entschlüsseln müsste?
Wäre es nicht schön, Fehlermeldungen zu haben, die Benutzer verstehen könnten, ohne anzurufen?
technischer Support?

Heutzutage / proc unter Linux ist mehr als in der Lage, die zum Dekodieren erforderlichen Informationen bereitzustellen
die überwiegende Mehrheit der Fehlermeldungen und weisen den Benutzer auf die unmittelbare Ursache hin
Problem. Auf Systemen mit einer begrenzten / proc Umsetzung, die lsof(1) Der Befehl kann ausgefüllt werden
viele der Lücken.

Im Jahr 2008 kam der Strom an Übersetzungsanfragen dem Autor viel zu oft entgegen. Es war
Zeit, diese 25 Jahre alte Idee noch einmal zu überdenken, und libexplain ist das Ergebnis.

VERWENDUNG BIBLIOTHEK


Die Schnittstelle zur Bibliothek versucht nach Möglichkeit konsistent zu sein. Beginnen wir mit einem
Beispiel mit strerror(3):
if (rename(old_path, new_path) < 0)
{
fprintf(stderr, "%s %s umbenennen: %s\n", alter_Pfad, neuer_Pfad,
strerror(errno));
wunsch(1);
}
Die Idee hinter libexplain besteht darin, eine bereitzustellen strerror(3) Äquivalent für jeder Systemaufruf,
speziell auf diesen Systemaufruf zugeschnitten, sodass ein detaillierterer Fehler bereitgestellt werden kann
Nachricht, die viele der Informationen enthält, die Sie unter der Abschnittsüberschrift „FEHLER“ sehen
2 und 3 Mann Seiten, ergänzt durch Informationen über tatsächliche Verhältnisse, tatsächliche Argumente
Werte und Systemgrenzen.

Das Einfacher Gehäuse
Das strerror(3) Ersatz:
if (rename(old_path, new_path) < 0)
{
fprintf(stderr, "%s\n", EXPLAIN_rename(old_path, new_path));
wunsch(1);
}

Das Fehler Gehäuse
Es ist auch möglich, ein explizites zu übergeben Fehler(3) Wert, wenn Sie zuerst etwas tun müssen
Verarbeitung, die stören würde Fehler, wie zum Beispiel Fehlerbehebung:
if (rename(old_path, new_path < 0))
{
int old_errno = errno;
...Code zur Verbesserung der Gesundheitsgerechtigkeit stört Fehler...
fprintf(stderr, „%s\n“, EXPLAIN_ERRNO_RENAME(OLD_ERRNO,
alter_Pfad, neuer_Pfad));
wunsch(1);
}

Das Multi Thread Projekte
Einige Anwendungen sind Multithread-Anwendungen und können daher die internen Daten von libexplain nicht gemeinsam nutzen
Puffer. Sie können Ihren eigenen Puffer mit bereitstellen
if (unlink(pathname))
{
char message[3000];
erklären_message_unlink(Nachricht, Größe von(Nachricht), Pfadname);
error_dialog(Nachricht);
Rückgabe -1;
}
Und der Vollständigkeit halber auch beides Fehler(3) und Thread-sicher:
ssize_t nbytes = read(fd, data, sizeof(data));
if (nbytes < 0)
{
char message[3000];
int old_errno = errno;
...Fehler Erholung...
EXPLAIN_message_errno_read(Nachricht, Größe von(Nachricht),
old_errno, fd, data, sizeof(data));
error_dialog(Nachricht);
Rückgabe -1;
}

Dies sind Ersatz für strerror_r(3), auf Systemen, die es haben.

Schnittstelle Zucker
Eine Reihe von Funktionen, die als Komfortfunktionen hinzugefügt wurden, um Programmierer für die Verwendung zu gewinnen
Die libexplain-Bibliothek ist die vom Autor am häufigsten verwendete libexplain-Funktion
Kommandozeilenprogramme:
int fd = EXPLAIN_creat_or_die(filename, 0666);
Diese Funktion versucht, eine neue Datei zu erstellen. Wenn dies nicht möglich ist, wird eine Fehlermeldung ausgegeben und
wird mit EXIT_FAILURE beendet. Wenn kein Fehler vorliegt, wird der neue Dateideskriptor zurückgegeben.

Eine verwandte Funktion:
int fd = EXPLAIN_creat_on_error(filename, 0666);
gibt die Fehlermeldung bei einem Fehler aus, gibt aber auch das ursprüngliche Fehlerergebnis zurück und
Fehler(3) ist ebenfalls unbehelligt.

Alle Sonstiges System Anrufe
Im Allgemeinen verfügt jeder Systemaufruf über eine eigene Include-Datei
#enthaltenName.h>
das Funktionsprototypen für sechs Funktionen definiert:

· erklären_Name,

·explain_errno_Name,

·explain_message_Name,

· EXPLAIN_message_errno_Name,

· erklären_Name_oder_sterben und

· erklären_Name_on_error.

Jeder Funktionsprototyp verfügt über eine Doxygen-Dokumentation und diese Dokumentation is nicht abgestreift
wenn die Include-Dateien installiert sind.

Das warten(2) Systemaufrufe (und Freunde) haben einige zusätzliche Varianten, die ebenfalls Fehler interpretieren
ein Exit-Status sein, der nicht EXIT_SUCCESS ist. Dies gilt für System(3) und pschließen(3) als
Gut.

Die Abdeckung umfasst 221 Systemaufrufe und 547 Ioctl-Anfragen. Es gibt noch viele weitere Systeme
Aufrufe, die noch umgesetzt werden müssen. Systemaufrufe, die nie zurückkehren, wie z wunsch(2) sind nicht vorhanden
in der Bibliothek und wird es auch nie sein. Der exec Familie von Systemaufrufen sind unterstützt, weil
Sie kehren zurück, wenn ein Fehler auftritt.

Katze
So könnte ein hypothetisches „Katzen“-Programm mit vollständiger Fehlerberichterstattung aussehen.
mit libexplain.
#enthalten
#einschließen
#enthalten
Es gibt ein Include für libexplain sowie die üblichen Verdächtigen. (Wenn Sie die reduzieren möchten
Präprozessorlast, Sie können die spezifische verwendenName.h> beinhaltet.)
Statische Leere
Prozess(DATEI *fp)
{
für (;;)
{
Zeichenpuffer [4096];
size_t n =explain_fread_or_die(buffer, 1, sizeof(buffer), fp);
if (!n)
break;
erklären_fwrite_or_die(buffer, 1, n, stdout);
}
}
Das Prozessdefinierung Die Funktion kopiert einen Dateistream in die Standardausgabe. Sollte ein Fehler auftreten
Beim Lesen oder Schreiben wird es gemeldet (und der Pfadname wird in die Datei aufgenommen).
Fehler) und der Befehl wird mit EXIT_FAILURE beendet. Wir kümmern uns nicht einmal um die Nachverfolgung
Pfadnamen oder deren Weitergabe über den Aufrufstapel.
int
main(int argc, char **argv)
{
für (;;)
{
int c = getopt(argc, argv, "o:");
if (c == EOF)
break;
Schalter (c)
{
Fall 'o':
erklären_freopen_or_die(optarg, "w", stdout);
break;
Der lustige Teil dieses Codes ist, dass libexplain Fehler melden kann einschließlich Pfadname sogar
wenn du nicht Öffnen Sie stdout explizit erneut, wie hier beschrieben. Wir machen uns darüber nicht einmal Sorgen
Verfolgung des Dateinamens.
Standard:
fprintf(stderr, „Verwendung: %ss [ -o ] ...\N",
argv[0]);
return EXIT_FAILURE;
}
}
if (optind == argc)
Prozess(stdin);
sonst
{
while (optind < argc)
{
DATEI *fp = erklären_fopen_or_die(argv[optind]++, "r");
Prozess(fp);
erklären_fclose_or_die(fp);
}
}
Die Standardausgabe wird implizit geschlossen, aber zu spät für einen Fehlerbericht
ausgegeben, also machen wir das hier, nur für den Fall, dass die gepufferte E/A noch nichts geschrieben hat, und
Es liegt ein ENOSPC-Fehler oder ähnliches vor.
erklären_fflush_or_die(stdout);
return EXIT_SUCCESS;
}
Das ist alles. Vollständige Fehlerberichterstattung, klarer Code.

Rusty's Skalieren of Schnittstelle Güte
Für diejenigen unter Ihnen, die es nicht kennen: „How Do I Make This Hard to Misuse?“ von Rusty Russel.
Diese Seite ist ein Muss für API-Designer.
http://ozlabs.org/~rusty/index.cgi/tech/2008‐03‐30.html

10 Es ist unmöglich zu bekommen falsch.

Ziele müssen hoch und ehrgeizig hoch gesetzt werden, damit man sie nicht erreicht und glaubt, es zu schaffen
fertig, wenn du es nicht bist.

Die libexplain-Bibliothek erkennt gefälschte Zeiger und viele andere gefälschte Systemaufrufparameter.
und versucht im Allgemeinen, Segfaults selbst unter den schwierigsten Umständen zu vermeiden.

Die libexplain-Bibliothek ist threadsicher konzipiert. Wahrscheinlicher ist eine stärkere Nutzung in der realen Welt
Zeigen Sie Orte auf, an denen dies verbessert werden kann.

Das größte Problem sind die eigentlichen Funktionsnamen selbst. Weil C nicht hat
Für Namensräume verwendet die libexplain-Bibliothek immer ein EXPLAIN_-Namenspräfix. Dies ist das
traditionelle Art, einen Pseudonamensraum zu erstellen, um Symbolkonflikte zu vermeiden.
Dies führt jedoch zu einigen unnatürlich klingenden Namen.

9. Das Compiler or Linker wird nicht lassen U bekommen it falsch.

Ein häufiger Fehler besteht darin, „explain_open“ anstelle von „explain_open_or_die“ zu verwenden.
Glücklicherweise gibt der Compiler an dieser Stelle häufig einen Typfehler aus (z.B kann nicht zugeordnet werden
const char * rvalue zu einem int lvalue).

8. Das Compiler werden wir warnen if U bekommen it falsch.

Wenn „explain_rename“ verwendet wird, obwohl „explain_rename_or_die“ vorgesehen war, kann dies zu anderen Ursachen führen
Probleme. GCC verfügt über ein nützliches Funktionsattribut warn_unused_result und die libexplain
Die Bibliothek hängt es an alle EXPLAIN_-Dateien an.Name Funktionsaufrufe, um eine Warnung zu erzeugen, wenn Sie
mache diesen Fehler. Kombinieren Sie dies mit gcc -Fehler um dies auf Güte der Stufe 9 zu bringen.

7. Das offensichtlich - is (wahrscheinlich) und beseitigen Muskelschwäche eins.

Die Funktionsnamen wurden so gewählt, dass sie ihre Bedeutung vermitteln, was jedoch nicht immer der Fall ist
erfolgreich. Während erklären_Name_oder_sterben und erklären_Name_on_error sind ziemlich beschreibend,
Die weniger verwendeten Thread-sicheren Varianten sind schwieriger zu dekodieren. Die Funktionsprototypen helfen dabei
Compiler zum Verständnis beitragen, und die Doxygen-Kommentare in den Header-Dateien helfen dem Benutzer
hin zum Verständnis.

6. Das Name erzählt U wie zu - es.

Es ist besonders wichtig, „explain_“ zu lesen.Name_oder_sterben als „erklären (Name oder stirb)".
Die Verwendung eines konsistenten EXPLAIN_-Namensraumpräfixes hat einige unglückliche Nebenwirkungen im
auch die Offensichtlichkeitsabteilung.

Die Reihenfolge der Wörter in den Namen gibt auch die Reihenfolge der Argumente an. Das Argument
Listen immer Ende mit denselben Argumenten, die an den Systemaufruf übergeben wurden; alle of Sie. Wenn
_errno_ erscheint im Namen, sein Argument steht immer vor den Systemaufrufargumenten. Wenn
_message_ erscheint im Namen, seine beiden Argumente stehen immer an erster Stelle.

5. Do it Recht or it werden wir brechen at Laufzeit.

Die libexplain-Bibliothek erkennt gefälschte Zeiger und viele andere gefälschte Systemaufrufparameter.
und versucht im Allgemeinen, Segfaults selbst unter den schwierigsten Umständen zu vermeiden. Es sollte
bricht nie zur Laufzeit ab, aber mehr reale Nutzung wird dies zweifellos verbessern.

Einige Fehlermeldungen richten sich wie diese an Entwickler und Betreuer und nicht an Endbenutzer
kann bei der Fehlerbehebung behilflich sein. Nicht so sehr „zur Laufzeit unterbrechen“, sondern „informativ sein bei“.
runtime“ (nach dem Systemaufruf barfs).

4. Folgen Sie uns verbreitet Konvention und du wirst bekommen it Recht.

Da C keine Namensräume hat, verwendet die libexplain-Bibliothek immer einen EXPLAIN_-Namen
Präfix. Dies ist die traditionelle Art, einen Pseudo-Namensraum zu erstellen, um dies zu vermeiden
Symbolkonflikte.

Die abschließenden Argumente aller libexplain-Aufrufe sind identisch mit dem Systemaufruf, den sie aufrufen
beschreiben. Dies soll eine konsistente Konvention gemeinsam mit der bieten
System ruft sich auf.

3. Lesen Sie mehr Dokumentation und du wirst bekommen it Recht.

Die libexplain-Bibliothek zielt darauf ab, für jeden einzelnen eine vollständige Doxygen-Dokumentation zu haben
öffentlicher API-Aufruf (und auch intern).

NACHRICHT INHALT


Die Arbeit an libexplain ist ein bisschen so, als würde man sich die Unterseite eines eingeschalteten Autos ansehen
die Hebebühne beim Mechaniker. Da ist ein paar hässliche Sachen darunter, außerdem Schlamm und Dreck und
Benutzer sehen es selten. Eine gute Fehlermeldung muss auch für einen Benutzer informativ sein
hatte das Glück, nicht so oft auf die Unterseite schauen zu müssen, und das auch
informativ für den Mechaniker, der sich die Beschreibung des Benutzers am Telefon anhört. Das ist
keine leichte Aufgabe.

Schauen wir uns noch einmal unser erstes Beispiel an: Der Code würde so aussehen, wenn er libexplain verwendet:
int fd = EXPLAIN_open_or_die("some/thing", O_RDONLY, 0);
wird mit einer Fehlermeldung wie dieser fehlschlagen
open(pathname = "some/file", flags = O_RDONLY) fehlgeschlagen, keine solche Datei oder kein solches Verzeichnis
(2, ENOENT), da es im aktuellen Verzeichnis kein Verzeichnis „some“ gibt
Dies zerfällt in drei Teile
Systemaufruf gescheitert, Systemfehler weil
Erklärung

Vorher Parce que
Es ist möglich, dass der Teil der Nachricht vor „weil“ als zu technisch bis untechnisch angesehen wird.
Technische Benutzer, meist aufgrund des genauen Druckens des Systems, rufen sich selbst auf
Beginn der Fehlermeldung. Und es sieht so aus strace(1) Ausgabe, für Bonus-Geek
Punkte.
open(pathname = "some/file", flags = O_RDONLY) fehlgeschlagen, keine solche Datei oder kein solches Verzeichnis
(2, ENOENT)
Dieser Teil der Fehlermeldung ist für den Entwickler beim Schreiben des Codes von wesentlicher Bedeutung.
und ebenso wichtig für den Betreuer, der Fehlerberichte lesen und Fehler beheben muss
Code. Da steht genau, was fehlgeschlagen ist.

Wenn dieser Text dem Benutzer nicht angezeigt wird, kann er ihn nicht kopieren und in eine einfügen
Fehlerbericht, und wenn es nicht im Fehlerbericht enthalten ist, kann der Betreuer nicht wissen, was tatsächlich passiert ist
falsch.

Häufig wird technisches Personal eingesetzt strace(1) oder Fachwerk(1) um diese genauen Informationen zu erhalten, aber
Dieser Weg steht Ihnen beim Lesen von Fehlerberichten nicht offen. Das System des Bug-Reporters ist weit entfernt
weg und inzwischen in einem ganz anderen Zustand. Daher müssen diese Informationen in der sein
Fehlerbericht, was bedeutet, dass er in der Fehlermeldung enthalten sein muss.

Die Systemaufrufdarstellung liefert auch Kontext für den Rest der Nachricht. Falls nötig
Wenn ein Problem auftritt, kann das fehlerhafte Systemaufrufargument in der Erklärung namentlich erwähnt werden
nach „weil“. Darüber hinaus sind alle Zeichenfolgen vollständig in Anführungszeichen gesetzte und mit Escapezeichen versehene C-Zeichenfolgen
Eingebettete Zeilenumbrüche und nicht druckbare Zeichen führen nicht dazu, dass das Terminal des Benutzers abstürzt
haywire.

Das Systemfehler ist das, was dabei herauskommt strerror(2) plus das Fehlersymbol. Ungeduldig und
Erfahrene Systemadministratoren könnten an dieser Stelle mit dem Lesen aufhören, die bisherigen Erfahrungen des Autors jedoch schon
dass es sich lohnt, weiterzulesen. (Wenn es sich nicht lohnt, ist es wahrscheinlich ein Bereich von
libexplain, das verbessert werden kann. Code-Beiträge sind natürlich willkommen.)

Nach der Parce que
Dies ist der Teil der Fehlermeldung, der sich an technisch nicht versierte Benutzer richtet. Es blickt darüber hinaus
die einfachen Systemaufrufargumente und sucht nach etwas Spezifischerem.
Im aktuellen Verzeichnis gibt es kein „some“-Verzeichnis
In diesem Teil wird versucht, die unmittelbare Ursache des Fehlers im Klartext zu erklären
Hier ist die Internationalisierung unerlässlich.

Im Allgemeinen besteht die Richtlinie darin, so viele Informationen wie möglich aufzunehmen, damit der Benutzer
muss nicht danach suchen (und lässt es nicht aus dem Fehlerbericht weg).

Internationalisierung
Die meisten Fehlermeldungen in der libexplain-Bibliothek wurden internationalisiert. Dort
Es gibt noch keine Lokalisierungen. Wenn Sie also die Erklärungen in Ihrer Muttersprache wünschen,
Bitte tragen Sie bei.

Das obige Qualifikationsmerkmal „das meiste von“ bezieht sich auf die Tatsache, dass der Proof-of-Concept durchgeführt wurde
Die Umsetzung beinhaltete keine Internationalisierungsunterstützung. Die Codebasis ist
Nach und nach überarbeitet, normalerweise als Ergebnis der Umgestaltung von Nachrichten, sodass jeder Fehler auftritt
Die Nachrichtenzeichenfolge kommt im Code genau einmal vor.

Es wurden Vorkehrungen für Sprachen getroffen, die die Teile zusammensetzen müssen
Systemaufruf gescheitert, Systemfehler weil Erklärung
in unterschiedlicher Reihenfolge für korrekte Grammatik in lokalisierten Fehlermeldungen.

Postmortem
Es gibt Zeiten, in denen ein Programm libexplain noch nicht verwendet hat und Sie es nicht verwenden können strace(1)
entweder. Da ist ein erklären(1) In libexplain enthaltener Befehl, der verwendet werden kann
Fehlermeldungen entschlüsseln, wenn sich der Zustand des zugrunde liegenden Systems nicht zu sehr geändert hat.
$ erklären umbenennen foo /tmp/bar/baz -e ENOENT
rename(oldpath = "foo", newpath = "/tmp/bar/baz") ist fehlgeschlagen, keine solche Datei oder kein solches Verzeichnis
(2, ENOENT), da im neuen Pfad kein „bar“-Verzeichnis vorhanden ist./ Tmp" Verzeichnis
$
Beachten Sie, wie die Pfadmehrdeutigkeit mithilfe des Systemaufrufargumentnamens gelöst wird. Von
Natürlich müssen Sie den Fehler und den Systemaufruf kennen erklären(1) nützlich sein. Als
Abgesehen davon ist dies eine der Methoden, mit denen die automatische Testsuite libexplain dies überprüft
libexplain funktioniert.

Philosophie
„Erzähl mir alles, auch Dinge, von denen ich nicht wusste, dass ich danach suchen sollte.“

Die Bibliothek ist so implementiert, dass bei statischer Verknüpfung nur der Code angezeigt wird, den Sie erhalten
tatsächliche Nutzung wird verlinkt. Dies wird dadurch erreicht, dass pro Quelldatei eine Funktion vorhanden ist.
wann immer möglich.

Wenn es möglich ist, weitere Informationen bereitzustellen, wird libexplain dies tun. Je weniger der Benutzer
aufspüren muss, desto besser. Dies bedeutet, dass UIDs von der begleitet werden
Benutzername, GIDs werden vom Gruppennamen begleitet, PIDs werden vom Prozess begleitet
Name, Dateideskriptoren und Streams werden vom Pfadnamen begleitet, etc.

Wenn beim Auflösen von Pfaden eine Pfadkomponente nicht vorhanden ist, sucht libexplain nach ähnlichen
Namen, um Alternativen für Tippfehler vorzuschlagen.

Die libexplain-Bibliothek versucht, so wenig Heap wie möglich zu verwenden, normalerweise gar keinen. Das ist
um eine Störung des Prozesszustands so weit wie möglich zu vermeiden, auch wenn dies manchmal der Fall ist
unvermeidlich.

Die libexplain-Bibliothek versucht, Thread-sicher zu sein, indem sie globale Variablen vermeidet und beibehält
Zustand so weit wie möglich auf dem Stapel. Es gibt einen einzigen gemeinsamen Nachrichtenpuffer und den
Funktionen, die es verwenden, gelten als nicht threadsicher.

Die libexplain-Bibliothek stört die Signalhandler eines Prozesses nicht. Das macht
Bestimmen, ob ein Zeiger eine Herausforderung segfault würde, aber nicht unmöglich.

Wenn Informationen sowohl über einen Systemaufruf als auch über a verfügbar sind / proc
Eintrag wird der Systemaufruf bevorzugt. Dadurch soll eine Störung des Prozesszustands vermieden werden.
Es gibt auch Zeiten, in denen keine Dateideskriptoren verfügbar sind.

Die libexplain-Bibliothek ist mit Unterstützung für große Dateien kompiliert. Es gibt kein Groß/Klein
Schizophrenie. Wenn sich dies auf die Argumenttypen in der API auswirkt, wird ein Fehler ausgegeben
wenn die notwendigen großen Dateidefinitionen fehlen.

FIXME: Es sind Arbeiten erforderlich, um sicherzustellen, dass Dateisystemkontingente im Code berücksichtigt werden. Das
trifft auf einige zu getrlimit(2) auch Grenzen.

Es gibt Fälle, in denen Verwandtepfade nicht aussagekräftig sind. Zum Beispiel: Systemdämonen,
Server und Hintergrundprozesse. In diesen Fällen werden im Fehler absolute Pfade verwendet
Erklärungen

PATH RESOLUTION


Kurzfassung: siehe path_resolution(7).

Lange Version: Die meisten Benutzer haben noch nie davon gehört path_resolution(7) und viele fortgeschrittene Benutzer
habe es noch nie gelesen. Hier ist eine kommentierte Version:

Schritt 1: Startseite of Auflösung Prozessdefinierung
Wenn der Pfadname mit dem Schrägstrich („/“) beginnt, ist das das Startverzeichnis für die Suche
das Stammverzeichnis des aufrufenden Prozesses.

Wenn der Pfadname nicht mit dem Schrägstrich („/“) beginnt, erfolgt die Startsuche
Das Verzeichnis des Auflösungsprozesses ist das aktuelle Arbeitsverzeichnis des Prozesses.

Schritt 2: Gehen entlang Weg
Legen Sie das aktuelle Suchverzeichnis auf das Start-Suchverzeichnis fest. Nun, für jedes Nicht‐
letzte Komponente des Pfadnamens, wobei eine Komponente eine durch einen Schrägstrich („/“) getrennte Teilzeichenfolge ist
Zeichen wird diese Komponente im aktuellen Suchverzeichnis gesucht.

Wenn der Prozess keine Suchberechtigung für das aktuelle Suchverzeichnis hat, wird ein EACCES
Es wird ein Fehler zurückgegeben („Berechtigung verweigert“).
open(pathname = "/home/archives/.ssh/private_key", flags = O_RDONLY) fehlgeschlagen,
Berechtigung verweigert (13, EACCES), da der Prozess keine Suchberechtigung hat
zum Pfadnamen-Verzeichnis „/home/archives/.ssh“, die prozesswirksame GID 1000
„pmiller“ stimmt nicht mit dem Verzeichnisbesitzer 1001 „archives“ überein, also mit dem Besitzer
Der Berechtigungsmodus „rwx“ wird ignoriert, der andere Berechtigungsmodus ist „---“ und der
Der Prozess ist nicht privilegiert (verfügt nicht über die DAC_READ_SEARCH-Fähigkeit)

Wenn die Komponente nicht gefunden wird, wird ein ENOENT-Fehler zurückgegeben („Keine solche Datei oder kein solches Verzeichnis“).
unlink(pathname = "/home/microsoft/rubbish") fehlgeschlagen, keine solche Datei oder kein solches Verzeichnis (2,
ENOENT), da im Pfadnamen kein „Microsoft“-Verzeichnis vorhanden ist./ Home" Verzeichnis

Es gibt auch eine gewisse Unterstützung für Benutzer, wenn sie Pfadnamen falsch eingeben, und macht dann Vorschläge
ENOENT wird zurückgegeben:
open(pathname = "/user/include/fcntl.h", flags = O_RDONLY) fehlgeschlagen, Keine solche Datei oder
Verzeichnis (2, ENOENT), da im Pfadnamen „/“ kein „Benutzer“-Verzeichnis vorhanden ist
Meinten Sie stattdessen das Verzeichnis „usr“?

Wenn die Komponente gefunden wird, aber weder ein Verzeichnis noch ein symbolischer Link ist, ein ENOTDIR
Es wird ein Fehler zurückgegeben („Kein Verzeichnis“).
open(pathname = "/home/pmiller/.netrc/lca", flags = O_RDONLY) fehlgeschlagen, nicht a
Verzeichnis (20, ENOTDIR), da die reguläre Datei „.netrc“ im Pfadnamen enthalten ist
Das Verzeichnis „/home/pmiller“ wird als Verzeichnis verwendet, wenn dies nicht der Fall ist

Wenn die Komponente gefunden wird und ein Verzeichnis ist, legen wir das aktuelle Suchverzeichnis darauf fest
Verzeichnis und gehen Sie zur nächsten Komponente.

Wenn die Komponente gefunden wird und ein symbolischer Link (Symlink) ist, lösen wir zunächst diesen symbolischen Link auf
Link (mit dem aktuellen Suchverzeichnis als Start-Suchverzeichnis). Aus Versehen, das
Es wird ein Fehler zurückgegeben. Wenn das Ergebnis kein Verzeichnis ist, wird ein ENOTDIR-Fehler zurückgegeben.
unlink(pathname = "/tmp/dangling/rubbish") fehlgeschlagen, keine solche Datei oder kein solches Verzeichnis (2,
ENOENT), weil der „baumelnde“ symbolische Link im Pfadnamen „/ Tmp" Verzeichnis
bezieht sich auf „nirgendwo“, das nicht existiert
Wenn die Auflösung des Symlinks erfolgreich ist und ein Verzeichnis zurückgibt, setzen wir das aktuelle
Suchen Sie im Suchverzeichnis in dieses Verzeichnis und wechseln Sie zur nächsten Komponente. Notiere dass der
Der Lösungsprozess beinhaltet hier eine Rekursion. Um den Kernel vor Stack zu schützen
Überlauf und auch zum Schutz vor Denial-of-Service gibt es Obergrenzen für das Maximum
Rekursionstiefe und von der maximalen Anzahl symbolischer Links, denen gefolgt wird. Ein ELOOP-Fehler liegt vor
wird zurückgegeben, wenn das Maximum überschritten wird („Zu viele Ebenen symbolischer Links“).
open(pathname = "/tmp/dangling", flags = O_RDONLY) fehlgeschlagen, zu viele Ebenen von
symbolische Links (40, ELOOP), da in eine symbolische Link-Schleife festgestellt wurde
Pfadname, beginnend bei „/tmp/dangling“
Es ist auch möglich, einen ELOOP- oder EMLINK-Fehler zu erhalten, wenn zu viele Symlinks vorhanden sind, aber nein
Schleife wurde erkannt.
open(pathname = "/tmp/rabbit-hole", flags = O_RDONLY) fehlgeschlagen, zu viele Ebenen von
symbolische Links (40, ELOOP), da in zu viele symbolische Links gefunden wurden
Pfadname (8)
Beachten Sie, dass auch das tatsächliche Limit gedruckt wird.

Schritt 3: Finden Sie Finale Eintrag
Die Suche nach der letzten Komponente des Pfadnamens erfolgt wie bei allen anderen
Komponenten, wie im vorherigen Schritt beschrieben, mit zwei Unterschieden:

(i) Die letzte Komponente muss kein Verzeichnis sein (zumindest was die Pfadauflösung betrifft).
Prozess betroffen ist. Aus diesem Grund muss es sich möglicherweise um ein Verzeichnis oder ein Nicht-Verzeichnis handeln
den Anforderungen des jeweiligen Systemaufrufs).

(Ii)
Es ist nicht unbedingt ein Fehler, wenn die endgültige Komponente nicht gefunden wird; vielleicht sind wir einfach
es erschaffen. Die Details zur Behandlung des Schlusseintrags sind im beschrieben
Handbuchseiten der spezifischen Systemaufrufe.

(iii)
Es kann auch ein Problem mit der letzten Komponente geben, wenn es sich um einen symbolischen Link handelt
und es sollte nicht befolgt werden. Zum Beispiel mit der XNUMXh geöffnet(2) O_NOFOLLOW-Flag:
open(pathname = "a-symlink", flags = O_RDONLY | O_NOFOLLOW) fehlgeschlagen, zu viele Ebenen von
symbolische Links (ELOOP), da O_NOFOLLOW angegeben wurde, der Pfadname jedoch auf a verweist
symbolischer Link

(iv)
Es kommt häufig vor, dass Benutzer beim Eingeben von Pfadnamen Fehler machen. Die libexplain-Bibliothek
versucht, Vorschläge zu machen, wenn ENOENT zurückgegeben wird, zum Beispiel:
open(pathname = "/usr/include/filecontrl.h", flags = O_RDONLY) fehlgeschlagen, Keine solche Datei oder
Verzeichnis (2, ENOENT), da der Pfadname keine reguläre Datei „filecontrl.h“ enthält
"/ usr / include„Verzeichnis, meinten Sie stattdessen die reguläre Datei „fcntl.h“?

(v) Es ist auch möglich, dass die endgültige Komponente etwas anderes als a sein muss
reguläre Datei:
readlink(pathname = "just-a-file", data = 0x7F930A50, data_size = 4097) fehlgeschlagen,
Ungültiges Argument (22, EINVAL), da Pfadname eine reguläre Datei und kein symbolischer Link ist

(vi)
FIXME: Behandlung des „t“-Bits.

Limits
Es gibt eine Reihe von Einschränkungen hinsichtlich Pfadnamen und Dateinamen.

Längenbeschränkung für Pfadnamen
Es gibt eine maximale Länge für Pfadnamen. Wenn der Pfadname (oder ein Zwischenname)
Der beim Auflösen symbolischer Links erhaltene Pfadname ist zu lang, ein ENAMETOOLONG
Es wird ein Fehler zurückgegeben („Dateiname zu lang“). Beachten Sie, wie das Systemlimit enthalten ist
in der Fehlermeldung.
open(pathname = "sehr lang", flags = O_RDONLY) fehlgeschlagen, Dateiname zu lang (36,
ENAMETOOLONG), da der Pfadname die maximale Pfadlänge des Systems überschreitet (4096)

Längenbeschränkung für Dateinamen
Bei einigen Unix-Varianten ist die Anzahl der Bytes in jeder Pfadkomponente begrenzt.
Einige von ihnen gehen stillschweigend damit um, andere geben ENAMETOOLONG; die libexplain
Bibliothek nutzt Pfadconf(3) _PC_NO_TRUNC, um zu sagen, welches. Wenn dieser Fehler auftritt, wird die
Die libexplain-Bibliothek gibt das Limit in der Fehlermeldung an, das Limit beträgt
erhalten von Pfadconf(3) _PC_NAME_MAX. Beachten Sie, wie das Systemlimit enthalten ist
in der Fehlermeldung.
open(pathname = "system7/nur-hatte-14-Zeichen", flags = O_RDONLY) fehlgeschlagen, Datei
Name zu lang (36, ENAMETOOLONG), da die Komponente „nur 14 Zeichen hatte“ vorhanden ist
länger als die Systemgrenze (14)

Leerer Pfadname
Im ursprünglichen Unix verwies der leere Pfadname auf das aktuelle Verzeichnis.
Heutzutage schreibt POSIX vor, dass ein leerer Pfadname nicht erfolgreich aufgelöst werden darf.
open(pathname = "", flags = O_RDONLY) fehlgeschlagen, Keine solche Datei oder kein solches Verzeichnis (2,
ENOENT), da POSIX vorschreibt, dass ein leerer Pfadname nicht aufgelöst werden darf
erfolgreich

Berechtigungen
Die Berechtigungsbits einer Datei bestehen aus drei Gruppen zu je drei Bits. Die erste Gruppe von
three wird verwendet, wenn die effektive Benutzer-ID des aufrufenden Prozesses mit der Besitzer-ID des Prozesses übereinstimmt
Datei. Die zweite Dreiergruppe wird verwendet, wenn die Gruppen-ID der Datei entweder gleich ist
effektive Gruppen-ID des aufrufenden Prozesses oder eine der ergänzenden Gruppen-IDs des
aufrufenden Prozess. Wenn keines der beiden gilt, wird die dritte Gruppe verwendet.
open(pathname = "/ etc / passwd", flags = O_WRONLY) fehlgeschlagen, Berechtigung verweigert (13,
EACCES), da der Prozess keine Schreibberechtigung für den regulären „passwd“ hat
Datei im Pfadnamen „/ Etc„Verzeichnis, die prozesswirksame UID 1000 „pmiller“
stimmt nicht mit dem regulären Dateieigentümer 0 „root“ überein, daher ist der Eigentümerberechtigungsmodus „rw-“
ignoriert wird, ist der andere Berechtigungsmodus „r--“ und der Prozess ist nicht privilegiert
(verfügt nicht über die DAC_OVERRIDE-Fähigkeit)
Dieser Erklärung wird ein beträchtlicher Raum eingeräumt, da die meisten Benutzer dies nicht wissen
So funktioniert das Berechtigungssystem. Insbesondere: der Eigentümer, die Gruppe und andere
Berechtigungen sind exklusiv, sie werden nicht durch „ODER“ verknüpft.

SELTSAM UND INTERESSANT SYSTEM ANRUFE


Der Prozess des Schreibens eines bestimmten Fehlerhandlers für jeden Systemaufruf zeigt oft Aufschluss
interessante Eigenheiten und Randbedingungen, oder unklar Fehler(3) Werte.

ENOMEDIUM, Nein mittlere gefunden
Der Titel dieses Aufsatzes stammt aus dem Akt des Kopierens einer CD.
$ dd if=/dev/cdrom of=fubar.iso
dd: Öffnen von „/dev/cdrom“: Kein Medium gefunden
$
Der Autor fragte sich, warum sein Computer ihm sagte, dass es keine Hellseher gibt
Mittel. Ganz abgesehen davon, dass dies bei vielen Muttersprachlern nicht der Fall ist
Ich bin mir sogar bewusst, dass „Medien“ ein Plural ist, ganz zu schweigen davon, dass „Medium“ sein Singular, die Zeichenfolge, ist
zurückgegeben von strerror(3) denn ENOMEDIUM ist so prägnant, dass es fast völlig frei davon ist
Inhalte.

Wann XNUMXh geöffnet(2) gibt ENOMEDIUM zurück. Es wäre schön, wenn die libexplain-Bibliothek a erweitern könnte
Darüber gibt es kaum etwas, je nachdem, um welche Art von Antrieb es sich handelt. Zum Beispiel:
... weil sich keine Diskette im Diskettenlaufwerk befindet
... weil sich keine CD im CD-ROM-Laufwerk befindet
... weil sich kein Band im Bandlaufwerk befindet
... weil sich im Kartenleser kein Memory Stick befindet

Und so geschah es...
open(pathname = "/dev/cdrom", flags = O_RDONLY) fehlgeschlagen, kein Medium gefunden (123,
ENOMEDIUM), da sich scheinbar keine CD im CD-ROM-Laufwerk befindet
Der Trick, den der Autor bisher nicht kannte, bestand darin, das Gerät mit dem zu öffnen
O_NONBLOCK-Flag, mit dem Sie ein Laufwerk öffnen können, in dem sich kein Medium befindet. Du dann
Problem gerätespezifisch ioctls(2) Anfragen, bis Sie herausgefunden haben, was zum Teufel es ist. (Nicht
Sicher, ob es sich dabei um POSIX handelt, aber es scheint laut Bericht auch in BSD und Solaris so zu funktionieren
Wodim(1) Quellen.)

Beachten Sie auch die unterschiedliche Verwendung von „Disk“ und „Disc“ im Kontext. Der CD-Standard entstand
in Frankreich, aber alles andere hat ein „k“.

EFAULT, Badewanne Adresse
Jeder Systemaufruf, der ein Zeigerargument akzeptiert, kann EFAULT zurückgeben. Die libexplain-Bibliothek
kann herausfinden, welches Argument fehlerhaft ist, und zwar ohne den Prozess zu stören
(oder Thread-)Signalverarbeitung.

Wenn verfügbar, die Mincore(2) Systemaufruf wird verwendet, um zu fragen, ob der Speicherbereich gültig ist.
Es können drei Ergebnisse zurückgegeben werden: zugeordnet, aber nicht im physischen Speicher, zugeordnet und im physischen Speicher
gespeichert und nicht zugeordnet. Beim Testen der Gültigkeit eines Zeigers lauten die ersten beiden „Ja“.
und das letzte ist „nein“.

Das Überprüfen von C-Strings ist schwieriger, da wir statt eines Zeigers und einer Größe nur einen verwenden
einen Zeiger haben. Um die Größe zu bestimmen, müssten wir die NUL finden, und das wäre möglich
Segfault, Catch-22.

Um dies zu umgehen, verwendet die libexplain-Bibliothek die lstat(2) Systemaufruf (mit bekanntem
gutes zweites Argument), um C-Strings auf Gültigkeit zu testen. Ein Fehler gibt && errno == EFAULT zurück
ist ein „Nein“, und alles andere ist ein „Ja“. Dies beschränkt die Zeichenfolgen natürlich auf PATH_MAX
Zeichen, aber das ist normalerweise kein Problem für die libexplain-Bibliothek, weil das so ist
fast immer die längsten Saiten, die es interessiert.

EMFILE, Too viele XNUMXh geöffnet Dateien
Dieser Fehler tritt auf, wenn in einem Prozess bereits die maximale Anzahl an Dateideskriptoren geöffnet ist.
Wenn das tatsächliche Limit gedruckt werden soll und die libexplain-Bibliothek dies versucht, können Sie es nicht öffnen
eine Datei in / proc um zu lesen, was es ist.
open_max = sysconf(_SC_OPEN_MAX);
Das wird nicht so schwierig sein, es gibt eins sysconf(3) Art und Weise, das Limit zu erhalten.

ENFILE, Too viele XNUMXh geöffnet Dateien in System
Dieser Fehler tritt auf, wenn das Systemlimit für die Gesamtzahl geöffneter Dateien überschritten wurde
erreicht. In diesem Fall gibt es kein Handy sysconf(3) Möglichkeit, das Limit zu erhalten.

Wenn man tiefer gräbt, entdeckt man vielleicht, dass es unter Linux eine gibt / proc Eintrag, den wir lesen konnten
diesen Wert erhalten. Catch-22: Wir haben keine Dateideskriptoren mehr und können daher keine Datei öffnen
Lesen Sie das Limit.

Unter Linux gibt es einen Systemaufruf, um es zu erhalten, aber es hat keine [e]glibc-Wrapper-Funktion, also
Sie müssen alles sehr sorgfältig durchführen:
lange
erklären_maxfile(void)
{
#ifdef __linux__
struct __sysctl_args args;
int32_t maxfile;
size_t maxfile_size = Größe von(maxfile);
int name[] = { CTL_FS, FS_MAXFILE };
memset(&args, 0, sizeof(struct __sysctl_args));
args.name = Name;
args.nlen = 2;
args.oldval = &maxfile;
args.oldlenp = &maxfile_size;
if (syscall(SYS__sysctl, &args) >= 0)
return maxfile;
#endif
Rückgabe -1;
}
Dadurch kann der Grenzwert in die Fehlermeldung aufgenommen werden, sofern verfügbar.

EINVAL "Ungültig Streit" vs ENOSYS "Funktion nicht umgesetzt“
Nicht unterstützte Aktionen (z. B Symlink(2) auf einem FAT-Dateisystem) werden nicht gemeldet
konsistent von einem Systemaufruf zum nächsten. Es ist möglich, entweder EINVAL oder zu haben
ENOSYS kehrte zurück.

Daher muss diesen Fehlerfällen besondere Aufmerksamkeit gewidmet werden, um sie richtig zu beheben
da sich EINVAL auch auf Probleme mit einem oder mehreren Systemaufrufargumenten beziehen könnte.

Note zur Verbesserung der Gesundheitsgerechtigkeit Fehler(3) is nicht immer kompensieren
Manchmal ist es notwendig, die [e]glibc-Quellen zu lesen, um festzustellen, wie und
wenn bei einigen Systemaufrufen Fehler zurückgegeben werden.

Lehen(3) Dateinr(3)
Oft wird davon ausgegangen, dass diese Funktionen keinen Fehler zurückgeben können. Dies gilt nur, wenn
Strom Das Argument ist gültig, sie sind jedoch in der Lage, ein ungültiges Argument zu erkennen
Zeiger.

fpathconf(3) Pfadconf(3)
Der Rückgabewert von fpathconf(2) und Pfadconf(2) könnte berechtigterweise -1 sein, das ist es auch
notwendig, um zu sehen, ob Fehler(3) wurde explizit festgelegt.

ioctls(2)
Der Rückgabewert von ioctls(2) könnte berechtigterweise -1 sein, daher muss geprüft werden, ob
Fehler(3) wurde explizit festgelegt.

Readdir(3)
Der Rückgabewert von Readdir(3) ist NULL sowohl für Fehler als auch für das Dateiende. Es ist
notwendig, um zu sehen, ob Fehler(3) wurde explizit festgelegt.

setbuf(3) Satzpuffer(3) setlinebuf(3) setvbuf(3)
Alle bis auf die letzte dieser Funktionen geben void zurück. Und setvbuf(3) ist nur dokumentiert als
Bei einem Fehler wird „ungleich Null“ zurückgegeben. Es ist notwendig zu sehen, ob Fehler(3) wurde ausdrücklich erwähnt
gesetzt.

strtod(3) Strtol(3) strold(3) strtoll(3) Strtul(3) strtoll(3)
Diese Funktionen geben im Fehlerfall 0 zurück, aber das ist auch ein legitimer Rückgabewert. Es ist
notwendig, um zu sehen, ob Fehler(3) wurde explizit festgelegt.

unget(3)
Während der ANSI-C-Standard nur ein einziges Backup-Zeichen vorschreibt, ändert sich dies
heraus, dass [e]glibc mehr zulässt ... aber das bedeutet, dass es mit ENOMEM fehlschlagen kann. Es kann
schlägt auch mit EBADF fehl, wenn fp ist falsch. Am schwierigsten ist es, wenn Sie EOF einen Fehler übergeben
return erfolgt, aber errno ist nicht gesetzt.

Die libexplain-Bibliothek erkennt alle diese Fehler korrekt, auch in Fällen, in denen die
Fehlerwerte sind, wenn überhaupt, nur unzureichend dokumentiert.

ENOSPC, Nein Raum links on Gerät
Wenn sich dieser Fehler auf eine Datei in einem Dateisystem bezieht, gibt die libexplain-Bibliothek den Mount aus
Stelle des Dateisystems mit dem Problem. Dies kann die Fehlerquelle erheblich beeinträchtigen
klarer.
write(fildes = 1 "example", data = 0xbfff2340, data_size = 5) fehlgeschlagen, kein Platz mehr
auf dem Gerät (28, ENOSPC), da das Dateisystem Dateien enthält ("/ Home") hat kein
mehr Platz für Daten
Da weitere spezielle Geräteunterstützung hinzugefügt wird, wird erwartet, dass Fehlermeldungen das Gerät umfassen
Name und tatsächliche Größe des Geräts.

EROFS, Schreibgeschützt Datei System
Wenn sich dieser Fehler auf eine Datei in einem Dateisystem bezieht, gibt die libexplain-Bibliothek den Mount aus
Stelle des Dateisystems mit dem Problem. Dies kann die Fehlerquelle erheblich beeinträchtigen
klarer.

Da weitere spezielle Geräteunterstützung hinzugefügt wird, wird erwartet, dass Fehlermeldungen das Gerät umfassen
Name und Typ.
open(pathname = "/dev/fd0", O_RDWR, 0666) fehlgeschlagen, schreibgeschütztes Dateisystem (30, EROFS)
weil auf der Diskette die Schreibschutzlasche gesetzt ist

...weil eine CD-ROM nicht beschreibbar ist
...weil auf der Speicherkarte die Schreibschutzlasche gesetzt ist
...weil das ½-Zoll-Magnetband keinen Schreibring hat

umbenennen
Das umbenennen(2) Ein Systemaufruf wird verwendet, um den Speicherort oder Namen einer Datei zu ändern und sie zu verschieben
zwischen Verzeichnissen, falls erforderlich. Wenn der Zielpfadname bereits vorhanden ist, ist er vorhanden
atomar ersetzt, so dass es keinen Punkt gibt, an dem ein anderer Prozess versucht, dies zu tun
Wenn Sie darauf zugreifen, wird es fehlen.

Allerdings gibt es Einschränkungen: Sie können nur ein Verzeichnis über einem anderen umbenennen
Verzeichnis, wenn das Zielverzeichnis nicht leer ist.
rename(oldpath = "foo", newpath = "bar") fehlgeschlagen, Verzeichnis nicht leer (39,
ENOTEMPTY), da newpath kein leeres Verzeichnis ist; das heißt, es enthält Einträge
außer "." Und ".."
Sie können ein Verzeichnis auch nicht über einem Nicht-Verzeichnis umbenennen.
rename(oldpath = "foo", newpath = "bar") fehlgeschlagen, kein Verzeichnis (20, ENOTDIR)
weil oldpath ein Verzeichnis ist, newpath jedoch eine reguläre Datei und kein Verzeichnis
Auch der umgekehrte Fall ist nicht zulässig
rename(oldpath = "foo", newpath = "bar") fehlgeschlagen, ist ein Verzeichnis (21, EISDIR)
weil newpath ein Verzeichnis ist, oldpath jedoch eine reguläre Datei und kein Verzeichnis

Dies macht die Arbeit der libexplain-Bibliothek natürlich komplizierter, weil die
Verknüpfung aufheben(2) oder rmdir(2) Der Systemaufruf wird implizit von aufgerufen umbenennen(2) und so weiter
Verknüpfung aufheben(2) oder rmdir(2) Fehler müssen ebenfalls erkannt und behandelt werden.

dup2
Das dup2(2) Der Systemaufruf wird verwendet, um einen zweiten Dateideskriptor zu erstellen, der auf die Datei verweist
dasselbe Objekt wie der erste Dateideskriptor. Normalerweise wird dies verwendet, um Shell-Eingaben zu implementieren
und Ausgabeumleitung.

Das Lustige daran ist, genau wie umbenennen(2) kann eine Datei zusätzlich zu einer atomar umbenennen
bestehende Datei und entfernen Sie die alte Datei, dup2(2) kann dies für eine bereits geöffnete Datei tun
Descriptor.

Dies macht die Arbeit der libexplain-Bibliothek noch einmal komplizierter, weil die schließen(2)
Der Systemaufruf wird implizit von aufgerufen dup2(2) und so weiter schließen(2)'s Fehler müssen sein
erkannt und behandelt.

ABENTEUER IN IOCTL SUPPORT


Das ioctls(2) Der Systemaufruf bietet Gerätetreiberautoren eine Möglichkeit zur Kommunikation
Benutzerbereich, der nicht in die vorhandene Kernel-API passt. Sehen ioctl_list(2).

Decoding PREISANFRAGE (Request) Zahlen
Von einem flüchtigen Blick auf die ioctls(2) Die Schnittstelle scheint groß, aber begrenzt zu sein
Anzahl möglich ioctls(2) Anfragen. Jeder anders ioctls(2) Der Antrag ist wirksam
ein weiterer Systemaufruf, aber ohne jegliche Typsicherheit – der Compiler kann nicht helfen a
Programmierer machen das richtig. Dies war wahrscheinlich die Motivation dahinter tcflush(3) und
Freunde.

Der erste Eindruck ist, dass man es entschlüsseln könnte ioctls(2) Anfragen über einen großen Schalter
Stellungnahme. Das erweist sich als undurchführbar, weil man sehr schnell herausfindet, dass es so ist
Es ist unmöglich, alle notwendigen Systemheader einzuschließen, die die verschiedenen definieren ioctls(2)
Anfragen, weil es ihnen schwerfällt, gut miteinander zu spielen.

Ein genauerer Blick zeigt, dass es eine Reihe „privater“ Anfragenummern und Geräte gibt
Treiberautoren werden ermutigt, sie zu verwenden. Das bedeutet, dass es eine weitaus größere Möglichkeit gibt
Eine Reihe von Anfragen mit mehrdeutigen Anfragenummern, die sofort erkennbar sind. Auch,
Es gibt auch einige historische Unklarheiten.

Wir wussten bereits, dass der Schalter unpraktisch war, aber jetzt wissen wir, dass wir ihn auswählen müssen
Bei entsprechender Anfragebezeichnung und Erklärung müssen wir nicht nur die Anfragenummer, sondern auch berücksichtigen
auch der Dateideskriptor.

Die Implementierung von ioctls(2) Die Unterstützung innerhalb der libexplain-Bibliothek besteht darin, eine Tabelle zu haben
Zeiger auf ioctls(2) Anforderungsdeskriptoren. Jeder dieser Deskriptoren enthält einen optionalen
Zeiger auf eine Begriffsklärungsfunktion.

Jede Anfrage wird tatsächlich in einer separaten Quelldatei implementiert, sodass das Notwendige gewährleistet ist
Include-Dateien sind von der Verpflichtung entbunden, gut mit anderen zu spielen.

Vertretung
Die Philosophie hinter der libexplain-Bibliothek besteht darin, so viele Informationen wie möglich bereitzustellen
möglich, einschließlich einer genauen Darstellung des Systemaufrufs. Im Fall von
ioctls(2) Dies bedeutet, dass die korrekte Antragsnummer (namentlich) und auch eine korrekte (bzw
(zumindest nützliche) Darstellung des dritten Arguments.

Das ioctls(2) Prototyp sieht so aus:
int ioctl(int fildes, int request, ...);
Das sollte Ihre Typensicherheitsalarme auslösen. Intern in [e]glibc wird dies aktiviert
in verschiedene Formen:
int __ioctl(int fildes, int request, long arg);
int __ioctl(int fildes, int request, void *arg);
und die Linux-Kernel-Systemaufrufschnittstelle erwartet
asmlinkage long sys_ioctl(unsigned int fildes, unsigned int request, unsigned long
arg);
Die extreme Variabilität des dritten Arguments ist eine Herausforderung, wenn die libexplain-Bibliothek verwendet wird
versucht, eine Darstellung dieses dritten Arguments zu drucken. Allerdings einmal die Anfragenummer
wurde eindeutig, jeder Eintrag in der ioctl-Tabelle der libexplain-Bibliothek hat eine
benutzerdefinierte print_data-Funktion (OO manuell durchgeführt).

Erläuterungen
Es gibt weniger Probleme bei der Bestimmung der zu verwendenden Erklärung. Einmal die Anfragenummer
wurde eindeutig, jeder Eintrag in der ioctl-Tabelle der libexplain-Bibliothek hat eine benutzerdefinierte
print_explanation-Funktion (wieder OO manuell durchgeführt).

Im Gegensatz zu Systemaufrufen in Abschnitt 2 und Abschnitt 3, die meisten ioctls(2) Anfragen haben keine Fehler
dokumentiert. Das heißt, um gute Fehlerbeschreibungen zu geben, ist es notwendig, den Kernel zu lesen
Quellen zu entdecken

· Was Fehler(3) Werte können zurückgegeben werden, und

· die Ursache jedes Fehlers.

Aufgrund der OO-Natur der Funktionsaufrufverteilung innerhalb des Kernels müssen Sie Folgendes lesen
alle Quellen, die das umsetzen ioctls(2) Anfrage, nicht nur die generische Implementierung. Es
Es ist zu erwarten, dass verschiedene Kernel unterschiedliche Fehlernummern aufweisen und subtil sein werden
verschiedene Fehlerursachen.

EINVAL vs ENOTTY
Die Situation ist noch schlimmer ioctls(2) Anfragen als für Systemaufrufe, mit EINVAL und
ENOTTY wird beides verwendet, um darauf hinzuweisen, dass ein ioctls(2) Die Anfrage ist insofern unangemessen
Kontext und gelegentlich ENOSYS, ENOTSUP und EOPNOTSUPP (zur Verwendung für Sockets gedacht) als
Also. Es gibt Kommentare in den Linux-Kernel-Quellen, die auf ein progressives Vorgehen hinweisen
Die Bereinigung ist im Gange. Für noch mehr Chaos fügt BSD der Verwirrung ENOIOCTL hinzu.

Daher muss diesen Fehlerfällen besondere Aufmerksamkeit gewidmet werden, um sie richtig zu beheben
da sich EINVAL auch auf Probleme mit einem oder mehreren Systemaufrufargumenten beziehen könnte.

intptr_t
Der C99-Standard definiert einen Integer-Typ, der garantiert jeden Zeiger enthalten kann
ohne Repräsentationsverlust.

Der obige Funktions-Systemaufruf-Prototyp wäre besser geschrieben
long sys_ioctl(unsigned int fildes, unsigned int request, intptr_t arg);
Das Problem ist die kognitive Dissonanz, die durch gerätespezifische oder dateisystemspezifische Faktoren hervorgerufen wird
ioctls(2) Implementierungen, wie zum Beispiel:
long vfs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
Die Mehrheit ioctls(2) Anfragen haben tatsächlich ein int *arg drittes Argument. Aber es zu haben
als long deklariert führt dazu, dass der Code dies als long *arg behandelt. Dies ist auf 32-Bit harmlos
(sizeof(long) == sizeof(int)), aber böse auf 64-Bit (sizeof(long) != sizeof(int)).
Abhängig von der Endian-Qualität erhalten Sie den erwarteten Wert oder nicht, aber Sie immer bekommen
auch ein Memory Scribble oder Stack Scribble.

Schreiben Sie all dies als
int ioctl(int fildes, int request, ...);
int __ioctl(int fildes, int request, intptr_t arg);
long sys_ioctl(unsigned int fildes, unsigned int request, intptr_t arg);
long vfs_ioctl(struct file *filp, unsigned int cmd, intptr_t arg);
betont, dass die ganze Zahl nur eine ganze Zahl ist, um eine Größe darzustellen, die nahezu ist
immer ein unabhängiger Zeigertyp.

FAZIT


Verwenden Sie libexplain, Ihre Benutzer werden es mögen.

COPYRIGHT


libexplain Version 1.4
Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Peter Miller

Nutzen Sie EXPLAIN_lca2010 online über die Dienste von onworks.net


Kostenlose Server & Workstations

Laden Sie Windows- und Linux-Apps herunter

  • 1
    SCHLUCK
    SCHLUCK
    SWIG ist ein Softwareentwicklungstool
    das verbindet in C geschriebene Programme und
    C++ mit einer Vielzahl von High-Level
    Programmiersprachen. SWIG wird mit verwendet
    anders...
    SWIG herunterladen
  • 2
    WooCommerce Nextjs Reaktionsthema
    WooCommerce Nextjs Reaktionsthema
    Reagieren Sie mit dem WooCommerce-Theme, das mit erstellt wurde
    Als nächstes JS, Webpack, Babel, Node und
    Express mit GraphQL und Apollo
    Klient. WooCommerce-Shop in React(
    enthält: Produkte...
    Laden Sie WooCommerce Nextjs React Theme herunter
  • 3
    archlabs_repo
    archlabs_repo
    Paket-Repo für ArchLabs Dies ist eine
    Anwendung, die auch abgerufen werden kann
    für
    https://sourceforge.net/projects/archlabs-repo/.
    Es wurde in OnWorks gehostet in...
    Laden Sie archlabs_repo herunter
  • 4
    Zephyr-Projekt
    Zephyr-Projekt
    Das Zephyr Project ist eine neue Generation
    Echtzeitbetriebssystem (RTOS), das
    unterstützt mehrere Hardware
    Architekturen. Es basiert auf einer
    Kernel mit kleinem Fußabdruck ...
    Laden Sie das Zephyr-Projekt herunter
  • 5
    SCons
    SCons
    SCons ist ein Software-Konstruktionstool
    das ist eine überlegene Alternative zum
    klassisches "Make"-Build-Tool, das
    wir alle kennen und lieben. SCons ist
    implementiert ein...
    Laden Sie SCons herunter
  • 6
    PSInt
    PSInt
    PSeInt ist ein Pseudocode-Interpreter für
    spanischsprachige Programmierstudenten.
    Sein Hauptzweck ist es, ein Werkzeug für
    Grundlegendes lernen und verstehen
    Konzept...
    PSeInt herunterladen
  • Mehr »

Linux-Befehle

Ad