AnglaisFrançaisEspagnol

Ad


Icône de favori OnWorks

Explain_lca2010 - En ligne dans le Cloud

Exécutez Explain_lca2010 dans le fournisseur d'hébergement gratuit OnWorks sur Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS

Il s'agit de la commande Explain_lca2010 qui peut être exécutée dans le fournisseur d'hébergement gratuit OnWorks à l'aide de l'un de nos multiples postes de travail en ligne gratuits tels que Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS

PROGRAMME:

Nom


Explain_lca2010 - Aucun support trouvé : quand il est temps d'arrêter d'essayer de lire strerreur(3)
esprit.

MOTIVATION


L'idée de libexplain m'est venue au début des années 1980. Chaque fois qu'un appel système
renvoie une erreur, le noyau sait exactement ce qui s'est mal passé... et le compresse en
moins de 8 bits de errno. L'espace utilisateur a accès aux mêmes données que le noyau, il
devrait être possible pour l'espace utilisateur de comprendre exactement ce qui s'est passé pour provoquer l'erreur
return, et utilisez-le pour écrire de bons messages d'erreur.

Serait-ce aussi simple que cela?

Erreur messages as délicatesse
Les bons messages d'erreur sont souvent ces tâches "à un pour cent" qui sont abandonnées lors de la planification
la pression presse votre projet. Cependant, un bon message d'erreur peut faire un énorme,
amélioration disproportionnée de l'expérience utilisateur, lorsque l'utilisateur erre dans la peur
territoire inconnu que l'on ne rencontre généralement pas. Ce n'est pas une tache facile.

En tant que programmeur larvaire, l'auteur n'a pas vu le problème avec l'erreur (complètement précise)
des messages comme celui-ci :
exception flottante (core dumpé)
jusqu'à ce que l'interprétation alternative non-programmeur soit indiquée. Mais ce n'est pas le
seul problème avec les messages d'erreur Unix. À quelle fréquence voyez-vous des messages d'erreur tels que :
$ ./stupide
impossible d'ouvrir le fichier
$
Il y a deux options pour un développeur à ce stade :

1.
vous pouvez exécuter un débogueur, tel que gdb(1), ou

2.
vous pouvez utiliser strass(1) ou ferme(1) pour regarder à l'intérieur.

· N'oubliez pas que vos utilisateurs n'ont peut-être même pas accès à ces outils, sans parler de la capacité
de les utiliser. (Cela fait très longtemps que Unix débutant signifiait « a seulement écrit UN
pilote de périphérique".)

Dans cet exemple, cependant, en utilisant strass(1) révèle
$ strass -e trace=ouvrir ./stupide
open("some/file", O_RDONLY) = -1 ENOENT (Aucun tel fichier ou répertoire)
impossible d'ouvrir le fichier
$
C'est beaucoup plus d'informations que le message d'erreur n'en fournit. Typiquement, le
le code source stupide ressemble à ceci
int fd = ouvrir("quelque chose", O_RDONLY);
si (fd < 0)
{
fprintf(stderr, "impossible d'ouvrir le fichier\n");
sortie(1);
}
L'utilisateur n'est pas informé qui fichier, et ne parvient pas non plus à dire à l'utilisateur qui Erreur. Le fichier était-il
même là? Y a-t-il eu un problème d'autorisations ? Il vous dit qu'il essayait d'ouvrir un
fichier, mais c'était probablement par accident.

Prenez votre bâton d'indice et allez battre le programmeur larvaire avec. Parlez-lui de erreur (3).
La prochaine fois que vous utiliserez le programme, un message d'erreur différent s'affichera :
$ ./stupide
open : aucun fichier ou répertoire de ce type
$
Des progrès, mais pas ce que nous attendions. Comment l'utilisateur peut-il résoudre le problème si le message d'erreur
ne lui dit pas quel était le problème ? En regardant la source, on voit
int fd = ouvrir("quelque chose", O_RDONLY);
si (fd < 0)
{
perror("ouvrir");
sortie(1);
}
Il est temps pour une autre course avec le bâton d'indice. Cette fois, le message d'erreur fait un pas
en avant et un pas en arrière :
$ ./stupide
quelque chose: Aucun fichier ou répertoire de ce nom
$
Maintenant, nous connaissons le fichier qu'il essayait d'ouvrir, mais ne sommes plus informés qu'il a été ouvert(2)
qui a échoué. Dans ce cas, il n'est probablement pas significatif, mais il peut être significatif pour
autres appels système. Ça aurait pu creat(2) au lieu de cela, une opération impliquant que
différentes autorisations sont nécessaires.
const char *filename = "quelque chose";
int fd = open(nom de fichier, O_RDONLY);
si (fd < 0)
{
perror(nom de fichier);
sortie(1);
}
L'exemple de code ci-dessus est malheureusement aussi typique des programmeurs non larvaires. Temps
pour parler à notre apprenant de padawan de la strerreur(3) appel système.
$ ./stupide
ouvert quelque chose: Aucun fichier ou répertoire de ce nom
$
Cela maximise les informations qui peuvent être présentées à l'utilisateur. Le code ressemble
ce:
const char *filename = "quelque chose";
int fd = open(nom de fichier, O_RDONLY);
si (fd < 0)
{
fprintf(stderr, "open %s: %s\n", nom de fichier, strerror(errno));
sortie(1);
}
Nous avons maintenant l'appel système, le nom de fichier et la chaîne d'erreur. Celui-ci contient tous les
informations qui strass(1) imprimé. C'est aussi bon que possible.

Ou est-ce?

Limites of erreur ainsi que strerreur
Le problème que l'auteur a vu, dans les années 1980, était que le message d'erreur est incomplet.
Est-ce que « aucun fichier ou répertoire de ce type » fait référence au « quelques» répertoire, ou au «chose” fichier dans
la "quelques« annuaire ?

Un rapide coup d'œil à la page de manuel pour strerreur(3) dit :
strerror - chaîne de retour décrivant le numéro d'erreur
Notez bien: il décrit l'erreur nombre, pas l'erreur.

D'autre part, le noyau sait quelle était l'erreur. Il y avait un point précis dans le
le code du noyau, causé par une condition spécifique, où le code du noyau s'est ramifié et a dit « non ».
Un programme en espace utilisateur pourrait-il déterminer la condition spécifique et écrire une meilleure erreur
message?

Cependant, le problème est plus profond. Que faire si le problème survient pendant la lire(2) système
appeler, plutôt que le ouvert(2) appeler ? C'est simple pour le message d'erreur associé à
ouvert(2) pour inclure le nom du fichier, c'est juste là. Mais pour pouvoir inclure un nom de fichier
dans l'erreur associée à la lire(2) appel système, vous devez passer le nom de fichier à tous
le chemin vers le bas de la pile d'appels, ainsi que le descripteur de fichier.

Et voici le bit qui grince : le noyau sait déjà quel nom de fichier le fichier
le descripteur est associé. Pourquoi un programmeur devrait-il transmettre des données redondantes à tous
le chemin vers le bas de la pile d'appels juste pour améliorer un message d'erreur qui ne sera peut-être jamais émis ? Dans
réalité, de nombreux programmeurs ne s'en soucient pas, et les messages d'erreur qui en résultent sont les pires pour
le

Mais c'était dans les années 1980, sur un PDP11, avec des ressources limitées et aucune bibliothèque partagée. Arrière
alors, aucune saveur d'Unix incluse / proc même sous une forme rudimentaire, et le lsof(1) programme
était dans plus d'une décennie. L'idée a donc été écartée comme irréalisable.

Niveau Infinity Assistance
Imaginez que vous êtes au niveau du support à l'infini. Votre description de poste dit que vous ne
déjà doivent parler aux utilisateurs. Pourquoi, alors, y a-t-il encore un flot constant de personnes qui veulent
vous, le gourou local d'Unix, pour déchiffrer un autre message d'erreur ?

Bizarrement, 25 ans plus tard, malgré un système de permissions simple, mis en place avec des
cohérence, la plupart des utilisateurs d'Unix n'ont toujours aucune idée de comment décoder "Aucun tel fichier ou répertoire",
ou l'un des autres messages d'erreur cryptiques qu'ils voient tous les jours. Ou, du moins, cryptique pour
Eux.

Ne serait-il pas bien que le support technique de premier niveau n'ait pas besoin de déchiffrer les messages d'erreur ?
Ne serait-il pas agréable d'avoir des messages d'erreur que les utilisateurs pourraient comprendre sans appeler
support technique?

Ces jours-ci / proc sur Linux est plus que capable de fournir les informations nécessaires pour décoder
la grande majorité des messages d'erreur, et indiquent à l'utilisateur la cause immédiate de leur
problème. Sur les systèmes avec un nombre limité / proc mise en œuvre, le lsof(1) la commande peut remplir
beaucoup de lacunes.

En 2008, le flot de demandes de traduction arrivait trop souvent à l'auteur. C'était
Il est temps de réexaminer cette idée vieille de 25 ans, et libexplain en est le résultat.

EN UTILISANT LES BIBLIOTHÈQUE


L'interface avec la bibliothèque essaie d'être cohérente, dans la mesure du possible. Commençons par un
exemple utilisant strerreur(3):
if (renommer (ancien_chemin, nouveau_chemin) < 0)
{
fprintf(stderr, "renommer %s %s : %s\n", ancien_chemin, nouveau_chemin,
strerror(errno));
sortie(1);
}
L'idée derrière libexplain est de fournir un strerreur(3) équivalent pour chacun appel système,
spécifiquement adapté à cet appel système, afin qu'il puisse fournir une erreur plus détaillée
message, contenant une grande partie des informations que vous voyez sous l'en-tête « ERREURS » de la section
2 et 3 man pages, complétées par des informations sur les conditions réelles, l'argument réel
valeurs et les limites du système.

La étapes Boitier
La strerreur(3) remplacement :
if (renommer (ancien_chemin, nouveau_chemin) < 0)
{
fprintf(stderr, "%s\n", Explain_rename(old_path, new_path));
sortie(1);
}

La Errno Boitier
Il est également possible de passer un message explicite errno(3) valeur, si vous devez d'abord faire quelques
traitement qui perturberait errno, comme la récupération d'erreur :
if (renommer (ancien_chemin, nouveau_chemin < 0))
{
int ancien_errno = errno;
code qui dérange errno
fprintf(stderr, "%s\n", expliquer_errno_rename(old_errno,
ancien_chemin, nouveau_chemin));
sortie(1);
}

La Multi-thread Étuis
Certaines applications sont multi-thread et sont donc incapables de partager les fichiers internes de libexplain.
amortir. Vous pouvez fournir votre propre tampon en utilisant
if (unlink(chemin d'accès))
{
message de caractère[3000] ;
expliquer_message_unlink(message, taille de(message), chemin d'accès);
boîte de dialogue_erreur(un message);
retour -1 ;
}
Et pour être complet, les deux errno(3) et thread-safe :
ssize_t noctets = read(fd, data, sizeof(data));
si (noctets < 0)
{
message de caractère[3000] ;
int ancien_errno = errno;
erreur récupération
expliquer_message_errno_read(message, taille de(un message),
old_errno, fd, data, sizeof(data));
boîte de dialogue_erreur(un message);
retour -1 ;
}

Ce sont des remplaçants pour strerror_r(3), sur les systèmes qui l'ont.

Interfaces Sucre
Un ensemble de fonctions ajoutées en tant que fonctions de commodité, pour séduire les programmeurs afin qu'ils utilisent le
bibliothèque libexplain, s'avèrent être les fonctions libexplain les plus couramment utilisées par l'auteur dans
programmes en ligne de commande :
int fd = expliquer_créer_ou_die(nom de fichier, 0666);
Cette fonction tente de créer un nouveau fichier. S'il ne peut pas, il imprime un message d'erreur et
sort avec EXIT_FAILURE. S'il n'y a pas d'erreur, il renvoie le nouveau descripteur de fichier.

Une fonction connexe :
int fd = expliquer_créer_sur_erreur (nom de fichier, 0666);
imprime le message d'erreur en cas d'échec, mais renvoie également le résultat de l'erreur d'origine, et
errno(3) n'est pas non plus inquiété.

Tous le autre combustion propre en cours
En général, chaque appel système a son propre fichier d'inclusion
#comprendreprénom.h>
qui définit des prototypes de fonctions pour six fonctions :

· Explique_prénom,

· expliquer_errno_prénom,

· expliquer_message_prénom,

· expliquer_message_errno_prénom,

· Explique_prénom_ou_mourir et

· Explique_prénom_sur_erreur.

Chaque prototype de fonction a une documentation Doxygen, et cette documentation is ne sauraient dépouillé
lorsque les fichiers d'inclusion sont installés.

La attendez(2) l'appel système (et les amis) ont des variantes supplémentaires qui interprètent également l'échec
être un état de sortie qui n'est pas EXIT_SUCCESS. Ceci s'applique à combustion proprede Géographie (3) et avec la fermer(3) comme
Hé bien.

La couverture comprend 221 appels système et 547 requêtes ioctl. Il y a beaucoup plus de système
appels encore à mettre en œuvre. Appels système qui ne reviennent jamais, tels que sortie(2), ne sont pas présents
dans la bibliothèque, et ne le sera jamais. Les exec famille d'appels système pris en charge, car
ils reviennent quand il y a une erreur.

Cathy
Voici à quoi pourrait ressembler un programme « chat » hypothétique, avec un rapport d'erreur complet,
en utilisant libexplain.
#comprendre
#inclure
#comprendre
Il y a une inclusion pour libexplain, plus les suspects habituels. (Si vous souhaitez réduire le
charge du préprocesseur, vous pouvez utiliser leprénom.h> comprend.)
vide statique
traiter(FICHIER *fp)
{
pour (;;)
{
tampon de chars [4096];
size_t n = expliquer_fread_or_die(buffer, 1, sizeof(buffer), fp);
si (!n)
break;
Explain_fwrite_or_die(buffer, 1, n, stdout);
}
}
La processus La fonction copie un flux de fichier sur la sortie standard. En cas d'erreur
pour la lecture ou l'écriture, il est signalé (et le chemin d'accès sera inclus dans le
erreur) et la commande se termine avec EXIT_FAILURE. Nous ne nous soucions même pas du suivi des
chemins d'accès, ou en les transmettant dans la pile d'appels.
int
principal(int argc, char **argv)
{
pour (;;)
{
int c = getopt(argc, argv, "o:");
si (c == EOF)
break;
interrupteur (c)
{
cas « o » :
expliquer_freopen_or_die(optarg, "w", stdout);
break;
La partie amusante de ce code est que libexplain peut signaler des erreurs comme le chemin d'accès pair
si tu ne le font pas rouvrez explicitement stdout comme c'est fait ici. Nous ne nous soucions même pas de
suivi du nom du fichier.
par défaut:
fprintf(stderr, "Utilisation : %ss [ -o ] ...\n",
argv[0]);
renvoie EXIT_FAILURE ;
}
}
si (optind == argc)
processus (stdin);
d'autre
{
while (optind < argc)
{
FICHIER *fp = expliquer_fopen_or_die(argv[optind]++, "r");
processus (fp);
expliquer_fclose_or_die(fp);
}
}
La sortie standard sera fermée implicitement, mais trop tard pour qu'un rapport d'erreur soit
émis, nous le faisons donc ici, juste au cas où l'E/S en mémoire tampon n'aurait encore rien écrit, et
il y a une erreur ENOSPC ou quelque chose.
expliquer_fflush_or_die(stdout);
renvoie EXIT_SUCCESS ;
}
C'est tout. Rapport d'erreur complet, code clair.

Rusty Escaliers intérieurs of Interfaces Bonté
Pour ceux d'entre vous qui ne le connaissent pas, "How Do I Make This Hard to Misuse?" de Rusty Russel
est une lecture incontournable pour les concepteurs d'API.
http://ozlabs.org/~rusty/index.cgi/tech/2008‐03‐30.html

10. C'est impossible à obtenez faux.

Les objectifs doivent être fixés haut, de manière ambitieuse, de peur que vous ne les atteigniez et que vous pensiez
fini quand tu ne l'es pas.

La bibliothèque libexplain détecte les faux pointeurs et de nombreux autres faux paramètres d'appel système,
et essaie généralement d'éviter les erreurs de segmentation, même dans les circonstances les plus difficiles.

La bibliothèque libexplain est conçue pour être thread-safe. Une utilisation plus réelle dans le monde réel sera probablement
révéler des endroits cela peut être amélioré.

Le plus gros problème est avec les noms de fonction eux-mêmes. Parce que C n'a pas
espaces de noms, la bibliothèque libexplain utilise toujours un préfixe de nom Explain_. C'est le
manière traditionnelle de créer un pseudo-espace de noms afin d'éviter les conflits de symboles.
Cependant, il en résulte des noms à consonance anormale.

9. La compilateur or Liens ne sera pas laisser you obtenez it faux.

Une erreur courante consiste à utiliser Explain_open là où Explain_open_or_die était destiné.
Heureusement, le compilateur émet souvent une erreur de type à ce stade (par exemple ne peut pas attribuer
const char * rvalue à un int lvalue).

8. La compilateur sera prévenir if you obtenez it faux.

Si Explain_rename est utilisé lorsque Explain_rename_or_die était prévu, cela peut entraîner d'autres
problèmes. GCC a un attribut de fonction warn_unused_result utile, et le libexplain
la bibliothèque l'attache à tous les expliquer_prénom appels de fonction pour produire un avertissement lorsque vous
faire cette erreur. Combinez cela avec gcc -Werreur pour promouvoir cela au niveau 9 de bonté.

7. La évident utilisé is (Probablement) le correct une.

Les noms des fonctions ont été choisis pour transmettre leur signification, mais ce n'est pas toujours le cas.
à succès. Tout en expliquant_prénom_ou_mourir et expliquer_prénom_on_error sont assez descriptifs,
les variantes thread-safe les moins utilisées sont plus difficiles à décoder. Les prototypes de fonction aident le
compilateur vers la compréhension, et les commentaires Doxygen dans les fichiers d'en-tête aident l'utilisateur
vers la compréhension.

6. La prénom raconte you how à utilisé le

Il est particulièrement important de lire expliquer_prénom_ou_mourir comme « expliquer (prénom ou mourir)".
L'utilisation d'un préfixe d'espace de noms Explain_ cohérent a des effets secondaires malheureux dans le
département de l'évidence, ainsi.

L'ordre des mots dans les noms indique également l'ordre des arguments. L'argument
listes toujours fin avec les mêmes arguments que ceux passés à l'appel système ; TOUTE of le point de vue de . Si
_errno_ apparaît dans le nom, son argument précède toujours les arguments de l'appel système. Si
_message_ apparaît dans le nom, ses deux arguments viennent toujours en premier.

5. Do it bon or it sera pause at Durée.

La bibliothèque libexplain détecte les faux pointeurs et de nombreux autres faux paramètres d'appel système,
et essaie généralement d'éviter les erreurs de segmentation, même dans les circonstances les plus difficiles. Cela devrait
ne cassez jamais au moment de l'exécution, mais une utilisation plus réelle dans le monde réel améliorera sans aucun doute cela.

Certains messages d'erreur sont destinés aux développeurs et aux mainteneurs plutôt qu'aux utilisateurs finaux, car cela
peut aider à la résolution de bogues. Pas tant « pause au moment de l'exécution » que « être informatif à
runtime » (après l'appel système barfs).

4. FOLLOW commun congrès ainsi que vous aurez obtenez it droite.

Parce que C n'a pas d'espaces de noms, la bibliothèque libexplain utilise toujours un nom Explain_
préfixe. C'est la manière traditionnelle de créer un pseudo‐espace de nom afin d'éviter
conflits de symboles.

Les arguments de fin de tous les appels libexplain sont identiques à l'appel système qu'ils
décrivent. Ceci est destiné à fournir une convention cohérente en commun avec le
système s'appelle eux-mêmes.

3. Lire le Documentation ainsi que vous aurez obtenez it droite.

La bibliothèque libexplain vise à disposer d'une documentation Doxygen complète pour chaque
appel API public (et en interne également).

MESSAGE CONTENU


Travailler sur libexplain, c'est un peu comme regarder le dessous de votre voiture quand elle est allumée
le treuil chez le mécanicien. Il y a des trucs laids là-dessous, plus de la boue et de la crasse, et
les utilisateurs le voient rarement. Un bon message d'erreur doit être informatif, même pour un utilisateur qui
a eu la chance de ne pas avoir à regarder très souvent le dessous, et aussi
informatif pour le mécanicien écoutant la description de l'utilisateur par téléphone. C'est
pas de tâche facile.

En revenant sur notre premier exemple, le code aimerait ceci s'il utilise libexplain :
int fd = Explain_open_or_die("quelque chose/quelque chose", O_RDONLY, 0);
échouera avec un message d'erreur comme celui-ci
open(pathname = "some/file", flags = O_RDONLY) a échoué, aucun fichier ou répertoire de ce type
(2, ENOENT) car il n'y a pas de répertoire "some" dans le répertoire courant
Cela se décompose en trois morceaux
appel système échoué, erreur système car
explication

Avant Parce que
Il est possible de voir la partie du message avant « parce que » comme trop technique pour non‐
utilisateurs techniques, principalement en raison de l'impression précise de l'appel système lui-même au
début du message d'erreur. Et on dirait strass(1) sortie, pour bonus geek
des points.
open(pathname = "some/file", flags = O_RDONLY) a échoué, aucun fichier ou répertoire de ce type
(2, ÉNOENT)
Cette partie du message d'erreur est indispensable au développeur lorsqu'il écrit le code,
et tout aussi important pour le mainteneur qui doit lire les rapports de bogues et corriger les bogues dans le
code. Il dit exactement ce qui a échoué.

Si ce texte n'est pas présenté à l'utilisateur, celui-ci ne peut pas le copier-coller dans un
rapport de bogue, et si ce n'est pas dans le rapport de bogue, le responsable ne peut pas savoir ce qui s'est réellement passé
faux.

Le personnel technique utilisera fréquemment strass(1) ou ferme(1) pour obtenir cette information exacte, mais
cette avenue n'est pas ouverte lors de la lecture des rapports de bogues. Le système du rapporteur de bogues est loin
loin, et, maintenant, dans un état bien différent. Ainsi, cette information doit être dans le
rapport de bogue, ce qui signifie qu'il doit être dans le message d'erreur.

La représentation de l'appel système donne également un contexte au reste du message. Si besoin
survient, l'argument d'appel système incriminé peut être désigné par son nom dans l'explication
après « parce que ». De plus, toutes les chaînes sont des chaînes C entièrement entre guillemets et échappées, donc
Les sauts de ligne intégrés et les caractères non imprimables n'entraîneront pas le démarrage du terminal de l'utilisateur
détraqué.

La erreur système est ce qui sort de strerreur(2), plus le symbole d'erreur. Impatient et
les administrateurs système experts pourraient arrêter de lire à ce stade, mais l'expérience de l'auteur à ce jour est
que la lecture plus loin est enrichissante. (Si ce n'est pas gratifiant, c'est probablement un domaine de
libexplain qui peut être amélioré. Les contributions au code sont les bienvenues, bien sûr.)

Après Parce que
Il s'agit de la partie du message d'erreur destinée aux utilisateurs non techniques. Il regarde au-delà
les arguments d'appel système simples et recherche quelque chose de plus spécifique.
il n'y a pas de répertoire "some" dans le répertoire courant
Cette partie tente d'expliquer la cause proximale de l'erreur en langage clair, et il
C'est ici que l'internationalisation est essentielle.

En général, la politique consiste à inclure autant d'informations que possible, afin que l'utilisateur
n'a pas besoin d'aller le chercher (et ne le laisse pas en dehors du rapport de bogue).

Internationalisation
La plupart des messages d'erreur de la bibliothèque libexplain ont été internationalisés. Là
n'y a pas encore de localisations, donc si vous voulez les explications dans votre langue maternelle,
s'il vous plaît contribuer.

Le qualificatif « plus de » ci-dessus est lié au fait que la preuve de concept
la mise en œuvre n'incluait pas le soutien à l'internationalisation. La base de code est en train d'être
révisé progressivement, généralement à la suite de messages de refactorisation de sorte que chaque erreur
la chaîne de message apparaît dans le code exactement une fois.

Des dispositions ont été prises pour les langues qui doivent assembler les portions de
appel système échoué, erreur système car explication
dans des ordres différents pour une grammaire correcte dans les messages d'erreur localisés.

Autopsie
Il y a des moments où un programme n'a pas encore utilisé libexplain, et vous ne pouvez pas utiliser strass(1)
Soit. Il y a un expliquer(1) commande incluse avec libexplain qui peut être utilisée pour
déchiffrer les messages d'erreur, si l'état du système sous-jacent n'a pas trop changé.
$ expliquer rebaptiser foo /tmp/bar/baz -e ÉNONCÉ
rename(oldpath = "foo", newpath = "/tmp/bar/baz") a échoué, aucun fichier ou répertoire de ce type
(2, ENOENT) car il n'y a pas de répertoire "bar" dans le newpath "/ Tmp" répertoire
$
Notez comment l'ambiguïté du chemin est résolue en utilisant le nom de l'argument d'appel système. De
bien sûr, vous devez connaître l'erreur et l'appel système pour expliquer(1) pour être utile. En tant que
de côté, c'est l'un des moyens utilisés par la suite de tests automatiques libexplain pour vérifier que
libexplain fonctionne.

Philosophie
« Dis-moi tout, y compris les choses que je ne savais pas chercher. »

La bibliothèque est implémentée de telle manière que lorsqu'elle est liée de manière statique, seul le code que vous
l'utilisation réelle sera liée. Ceci est réalisé en ayant une fonction par fichier source,
chaque fois que possible.

Lorsqu'il sera possible de fournir plus d'informations, libexplain le fera. Moins l'utilisateur
doit rechercher par eux-mêmes, le mieux. Cela signifie que les UID sont accompagnés des
nom d'utilisateur, les GID sont accompagnés du nom du groupe, les PID sont accompagnés du processus
le nom, les descripteurs de fichiers et les flux sont accompagnés du chemin d'accès, etc.

Lors de la résolution des chemins, si un composant de chemin n'existe pas, libexplain recherchera des
noms, afin de proposer des alternatives aux erreurs typographiques.

La bibliothèque libexplain essaie d'utiliser le moins de tas possible, et généralement aucun. C'est
pour éviter autant que possible de perturber l'état du processus, bien qu'il soit parfois
inévitable.

La bibliothèque libexplain tente d'être thread-safe, en évitant les variables globales, en gardant
l'état sur la pile autant que possible. Il existe un seul tampon de messages commun, et le
les fonctions qui l'utilisent sont documentées comme n'étant pas thread-safe.

La bibliothèque libexplain ne perturbe pas les gestionnaires de signaux d'un processus. Cela fait
déterminer si un pointeur ferait une erreur de segmentation d'un défi, mais pas impossible.

Lorsque des informations sont disponibles via un appel système ainsi que via un / proc
entrée, l'appel système est préféré. Ceci afin d'éviter de perturber l'état du processus.
Il arrive également qu'aucun descripteur de fichier ne soit disponible.

La bibliothèque libexplain est compilée avec un support de fichiers volumineux. Il n'y a pas de grand/petit
schizophrénie. Lorsque cela affecte les types d'arguments dans l'API, et une erreur sera émise
si les définitions de fichier volumineuses nécessaires sont absentes.

FIXME : un travail est nécessaire pour s'assurer que les quotas du système de fichiers sont gérés dans le code. Cette
s'applique à certains getrlimit(2) les limites aussi.

Il y a des cas où les chemins des parents ne sont pas informatifs. Par exemple : les démons système,
serveurs et processus d'arrière-plan. Dans ces cas, des chemins absolus sont utilisés dans l'erreur
explications.

PATH RÉSOLUTION


Version courte : voir chemin_résolution (7).

Version longue : la plupart des utilisateurs n'ont jamais entendu parler de chemin_résolution(7), et de nombreux utilisateurs avancés
ne l'ai jamais lu. Voici une version annotée :

étapes 1: Accueil of le résolution processus
Si le chemin d'accès commence par la barre oblique ("/"), le répertoire de recherche de départ est
le répertoire racine du processus appelant.

Si le chemin d'accès ne commence pas par le caractère barre oblique ("/"), la recherche de départ
répertoire du processus de résolution est le répertoire de travail courant du processus.

étapes 2: Walk le long de le chemin
Définissez le répertoire de recherche actuel sur le répertoire de recherche de départ. Maintenant, pour chaque non‐
composant final du chemin d'accès, où un composant est une sous-chaîne délimitée par une barre oblique ("/")
caractères, ce composant est recherché dans le répertoire de recherche courant.

Si le processus n'a pas d'autorisation de recherche sur le répertoire de recherche actuel, un EACCES
l'erreur est renvoyée ("Autorisation refusée").
open(pathname = "/home/archives/.ssh/private_key", flags = O_RDONLY) a échoué,
Autorisation refusée (13, EACCES) car le processus n'a pas d'autorisation de recherche
vers le répertoire "/home/archives/.ssh", le processus effectif GID 1000
"pmiller" ne correspond pas au propriétaire du répertoire 1001 "archives" donc le propriétaire
le mode d'autorisation "rwx" est ignoré, les autres modes d'autorisation sont "---", et le
le processus n'est pas privilégié (n'a pas la capacité DAC_READ_SEARCH)

Si le composant n'est pas trouvé, une erreur ENOENT est renvoyée ("Aucun fichier ou répertoire de ce type").
unlink(pathname = "/home/microsoft/rubbish") a échoué, aucun fichier ou répertoire de ce type (2,
ENOENT) car il n'y a pas de répertoire "microsoft" dans le chemin "/ home" répertoire

Il existe également une assistance pour les utilisateurs lorsqu'ils tapent mal les noms de chemin, en faisant des suggestions lorsque
ENOENT est renvoyé :
open(pathname = "/user/include/fcntl.h", flags = O_RDONLY) a échoué, aucun fichier de ce type ou
répertoire (2, ENOENT) car il n'y a pas de répertoire "user" dans le chemin "/"
répertoire, vouliez-vous dire le répertoire "usr" à la place ?

Si le composant est trouvé, mais n'est ni un répertoire ni un lien symbolique, un ENOTDIR
une erreur est renvoyée ("Pas un répertoire").
open(pathname = "/home/pmiller/.netrc/lca", flags = O_RDONLY) a échoué, pas un
répertoire (20, ENOTDIR) car le fichier normal ".netrc" dans le nom de chemin
Le répertoire "/home/pmiller" est utilisé comme répertoire alors qu'il ne l'est pas

Si le composant est trouvé et qu'il s'agit d'un répertoire, nous définissons le répertoire de recherche actuel sur celui-ci
répertoire et passez au composant suivant.

Si le composant est trouvé et est un lien symbolique (symlink), nous résolvons d'abord ce symbolique
lien (avec le répertoire de recherche actuel comme répertoire de recherche de départ). En cas d'erreur, que
l'erreur est renvoyée. Si le résultat n'est pas un répertoire, une erreur ENOTDIR est renvoyée.
unlink(pathname = "/tmp/dangling/rubbish") a échoué, aucun fichier ou répertoire de ce type (2,
ENOENT) car le lien symbolique "pendant" dans le chemin "/ Tmp" répertoire
fait référence à "nulle part" qui n'existe pas
Si la résolution du lien symbolique est réussie et renvoie un répertoire, nous définissons le courant
répertoire de recherche dans ce répertoire et passez au composant suivant. Notez que le
Le processus de résolution implique ici la récursivité. Afin de protéger le noyau contre la pile
débordement, et aussi pour se protéger contre le déni de service, il y a des limites sur le maximum
profondeur de récursivité, et sur le nombre maximum de liens symboliques suivis. Une erreur ELOOP est
renvoyé lorsque le maximum est dépassé ("Trop de niveaux de liens symboliques").
open(pathname = "/tmp/dangling", flags = O_RDONLY) a échoué, trop de niveaux de
liens symboliques (40, ELOOP) car une boucle de lien symbolique a été rencontrée dans
chemin d'accès, commençant à "/tmp/dangling"
Il est également possible d'obtenir une erreur ELOOP ou EMLINK s'il y a trop de liens symboliques, mais non
boucle a été détectée.
open(pathname = "/tmp/rabbit‐hole", flags = O_RDONLY) a échoué, trop de niveaux de
liens symboliques (40, ELOOP) car trop de liens symboliques ont été rencontrés dans
chemin (8)
Remarquez comment la limite réelle est également imprimée.

étapes 3: Trouvez le finale entrée
La recherche du composant final du nom de chemin va comme celle de tous les autres
composants, comme décrit à l'étape précédente, avec deux différences :

(i) Le composant final n'a pas besoin d'être un répertoire (au moins en ce qui concerne la résolution du chemin
processus est concerné. Il peut s'agir d'un répertoire, ou d'un non‐répertoire, à cause de
les exigences de l'appel système spécifique).

(Ii)
Ce n'est pas nécessairement une erreur si le composant final n'est pas trouvé ; peut-être sommes-nous juste
le créer. Les détails sur le traitement de l'entrée finale sont décrits dans le
pages de manuel des appels système spécifiques.

(iii)
Il est également possible d'avoir un problème avec le dernier composant s'il s'agit d'un lien symbolique
et il ne doit pas être suivi. Par exemple, en utilisant le ouvert(2) Indicateur O_NOFOLLOW :
open(pathname = "a‐symlink", flags = O_RDONLY | O_NOFOLLOW) a échoué, Trop de niveaux de
liens symboliques (ELOOP) car O_NOFOLLOW a été spécifié mais le nom de chemin fait référence à un
lien symbolique

(iv)
Il est courant que les utilisateurs fassent des erreurs lors de la saisie des noms de chemin. La bibliothèque libexplain
tente de faire des suggestions lorsque ENOENT est renvoyé, par exemple :
open(pathname = "/usr/include/filecontrl.h", flags = O_RDONLY) a échoué, aucun fichier de ce type ou
répertoire (2, ENOENT) car il n'y a pas de fichier régulier "filecontrl.h" dans le chemin d'accès
"/ usr / include", vouliez-vous dire le fichier normal "fcntl.h" à la place ?

(v) Il est également possible que le composant final soit quelque chose d'autre qu'un
fichier normal :
readlink(pathname = "just-a-file", data = 0x7F930A50, data_size = 4097) a échoué,
Argument non valide (22, EINVAL) car le chemin d'accès est un fichier normal, pas un lien symbolique

(vi)
FIXME : gestion du bit "t".

Limites
Il existe un certain nombre de limites en ce qui concerne les chemins et les noms de fichiers.

Limite de longueur du nom de chemin
Il y a une longueur maximale pour les noms de chemin. Si le chemin d'accès (ou un intermédiaire
chemin obtenu lors de la résolution des liens symboliques) est trop long, un ENAMETOOLONG
une erreur est renvoyée ("Nom de fichier trop long"). Remarquez comment la limite du système est incluse
dans le message d'erreur.
open(chemin = "très long", flags = O_RDONLY) a échoué, nom de fichier trop long (36,
ENAMETOOLONG) car le chemin d'accès dépasse la longueur maximale du chemin d'accès du système (4096)

Limite de longueur de nom de fichier
Certaines variantes d'Unix ont une limite sur le nombre d'octets dans chaque composant de chemin.
Certains d'entre eux traitent cela en silence, et certains donnent ENAMETOOLONG; la libexplain
bibliothèque utilise cheminconf(3) _PC_NO_TRUNC pour dire lequel. Si cette erreur se produit, le
La bibliothèque libexplain indiquera la limite dans le message d'erreur, la limite est
obtenu à partir de cheminconf(3) _PC_NAME_MAX. Remarquez comment la limite du système est incluse
dans le message d'erreur.
open(chemin = "system7/seulement-avait-14-caractères", flags = O_RDONLY) a échoué, Fichier
nom trop long (36, ENAMETOOLONG) car le composant "only-had-14-characters" est
plus long que la limite du système (14)

Chemin d'accès vide
Dans l'Unix d'origine, le chemin vide renvoyait au répertoire courant.
De nos jours, POSIX décrète qu'un nom de chemin vide ne doit pas être résolu avec succès.
open(pathname = "", flags = O_RDONLY) a échoué, aucun fichier ou répertoire de ce type (2,
ENOENT) car POSIX décrète qu'un chemin vide ne doit pas être résolu
réussir

Permissions
Les bits d'autorisation d'un fichier se composent de trois groupes de trois bits. Le premier groupe de
trois est utilisé lorsque l'ID utilisateur effectif du processus appelant est égal à l'ID propriétaire du
déposer. Le deuxième groupe de trois est utilisé lorsque l'ID de groupe du fichier est égal au
ID de groupe effectif du processus appelant, ou est l'un des ID de groupe supplémentaires du
processus d'appel. Lorsque ni l'un ni l'autre ne tient, le troisième groupe est utilisé.
open(chemin = "/ Etc / passwd", flags = O_WRONLY) a échoué, Autorisation refusée (13,
EACCES) car le processus n'a pas d'autorisation d'écriture sur le "passwd" régulier
fichier dans le chemin d'accès "/ Etc" répertoire, le processus effectif UID 1000 " pmiller "
ne correspond pas au propriétaire de fichier normal 0 "root", donc le mode d'autorisation du propriétaire "rw-"
est ignoré, le mode d'autorisation des autres est "r--", et le processus n'est pas privilégié
(n'a pas la capacité DAC_OVERRIDE)
Une place considérable est accordée à cette explication, car la plupart des utilisateurs ne savent pas que cette
c'est ainsi que fonctionne le système d'autorisations. En particulier : le propriétaire, le groupe et les autres
les autorisations sont exclusives, elles ne sont pas « OU » ensemble.

ÉTRANGE ET INTÉRESSANT SYSTÈME APPELS


Le processus d'écriture d'un gestionnaire d'erreurs spécifique pour chaque appel système révèle souvent
bizarreries et conditions aux limites intéressantes, ou obscures errno(3) valeurs.

ÉNOMÉDIUM, Non moyenne trouvé
L'acte de copier un CD était la source du titre de cet article.
$ dd si=/dev/cdrom de=fubar.iso
dd : ouverture de « /dev/cdrom » : aucun support trouvé
$
L'auteur s'est demandé pourquoi son ordinateur lui disait qu'il n'existe pas de médium
moyen. Indépendamment du fait qu'un grand nombre d'anglophones natifs ne sont pas
même conscient que « média » est un pluriel, et encore moins que « médium » est son singulier, la chaîne
retourné par strerreur(3) pour ENOMEDIUM est si laconique qu'il est presque complètement exempt de
contenu.

Quand ouvert(2) renvoie ENOMEDIUM ce serait bien si la bibliothèque libexplain pouvait étendre un
peu à ce sujet, en fonction du type de lecteur dont il s'agit. Par exemple:
... car il n'y a pas de disquette dans le lecteur de disquette
... car il n'y a pas de disque dans le lecteur de CD‐ROM
... parce qu'il n'y a pas de bande dans le lecteur de bande
... car il n'y a pas de clé USB dans le lecteur de carte

Et c'est ainsi que c'est arrivé...
open(pathname = "/dev/cdrom", flags = O_RDONLY) a échoué, aucun support trouvé (123,
ENOMEDIUM) car il ne semble pas y avoir de disque dans le lecteur de CD‐ROM
L'astuce, que l'auteur ignorait auparavant, consistait à ouvrir l'appareil à l'aide du
Drapeau O_NONBLOCK, qui vous permettra d'ouvrir un lecteur sans support. Toi alors
problème spécifique à l'appareil ioctl(2) demandes jusqu'à ce que vous compreniez ce que c'est. (Pas
bien sûr s'il s'agit de POSIX, mais cela semble également fonctionner de cette façon dans BSD et Solaris, selon
le wodim(1) source.)

Notez également les différentes utilisations de « disque » et « disque » dans le contexte. La norme CD est née
en France, mais tout le reste a un « k ».

DÉFAUT, Mal propos
Tout appel système qui prend un argument de pointeur peut retourner EFAULT. La bibliothèque libexplain
peut déterminer quel argument est en cause, et il le fait sans perturber le processus
(ou thread) traitement du signal.

Lorsqu'il est disponible, le mineur(2) l'appel système est utilisé pour demander si la région mémoire est valide.
Il peut renvoyer trois résultats : mappé mais pas en mémoire physique, mappé et en mémoire physique
mémoire, et non mappé. Lors du test de validité d'un pointeur, les deux premiers sont « oui »
et le dernier est "non".

La vérification des chaînes C est plus difficile, car au lieu d'un pointeur et d'une taille, nous
avoir un pointeur. Pour déterminer la taille, il faudrait trouver le NUL, et cela pourrait
faute de segmentation, catch‐22.

Pour contourner ce problème, la bibliothèque libexplain utilise le lstat(2) appel système (avec un
bon deuxième argument) pour tester la validité des chaînes C. Un échec renvoie && errno == EFAULT
est un "non", et tout le reste est un "oui". Ceci limite bien sûr les chaînes à PATH_MAX
caractères, mais ce n'est généralement pas un problème pour la bibliothèque libexplain, car c'est
presque toujours les cordes les plus longues dont il se soucie.

EMFILE, Aussi de nombreuses ouvert fichiers
Cette erreur se produit lorsqu'un processus a déjà ouvert le nombre maximal de descripteurs de fichiers.
Si la limite réelle doit être imprimée et que la bibliothèque libexplain essaie de le faire, vous ne pouvez pas ouvrir
un fichier dans / proc pour lire ce que c'est.
open_max = sysconf(_SC_OPEN_MAX);
Celui-ci n'est pas si difficile, il y a un configuration système(3) moyen d'obtenir la limite.

ENFILER, Aussi de nombreuses ouvert fichiers in combustion propre
Cette erreur se produit lorsque la limite du système sur le nombre total de fichiers ouverts a été
parvenu. Dans ce cas, il n'y a pas de pratique configuration système(3) façon d'obtenir la limite.

En creusant plus profondément, on peut découvrir que sur Linux il existe un / proc entrée que nous pourrions lire à
obtenir cette valeur. Catch‐22 : nous n'avons plus de descripteurs de fichiers, nous ne pouvons donc pas ouvrir un fichier pour
lire la limite.

Sous Linux, il y a un appel système pour l'obtenir, mais il n'a pas de fonction wrapper [e]glibc, donc
vous devez tout cela très soigneusement:
Long
expliquer_maxfile(void)
{
#ifdef __linux__
struct __sysctl_args arguments ;
fichier_max int32_t ;
size_t maxfile_size = taille de(fichier max) ;
nom int[] = { CTL_FS, FS_MAXFILE } ;
memset(&args, 0, sizeof(struct __sysctl_args));
args.name = nom;
args.nlen = 2 ;
args.oldval = &maxfile;
args.oldlenp = &maxfile_size;
if (syscall(SYS__sysctl, &args) >= 0)
renvoyer maxfile ;
#endif
retour -1 ;
}
Cela permet d'inclure la limite dans le message d'erreur, lorsqu'elle est disponible.

EINVAL "Invalide argument" vs ÉNOSYS "Fonction ne sauraient mis en œuvre"
Actions non prises en charge (telles que lien symbolique(2) sur un système de fichiers FAT) ne sont pas signalés
systématiquement d'un appel système à l'autre. Il est possible d'avoir soit EINVAL soit
ENOSYS retourné.

En conséquence, il faut prêter attention à ces cas d'erreur pour les corriger, en particulier
car EINVAL pourrait également faire référence à des problèmes avec un ou plusieurs arguments d'appel système.

Notes qui errno(3) is ne sauraient toujours set
Il y a des moments où il est nécessaire de lire les sources [e]glibc pour déterminer comment et
lorsque des erreurs sont renvoyées pour certains appels système.

feof(3), fichier non(3)
On suppose souvent que ces fonctions ne peuvent pas renvoyer d'erreur. Ceci n'est vrai que si
le courant l'argument est valide, mais ils sont capables de détecter un
aiguille.

fpathconf(3), cheminconf(3)
La valeur de retour de fpathconfde Géographie (2) et avec la cheminconf(2) pourrait légitimement être -1, il est donc
nécessaire de voir si errno(3) a été explicitement défini.

ioctl(2)
La valeur de retour de ioctl(2) pourrait légitimement être -1, il faut donc voir si
errno(3) a été explicitement défini.

répertoire de lecture(3)
La valeur de retour de répertoire de lecture(3) est NULL pour les erreurs et la fin de fichier. Il est
nécessaire de voir si errno(3) a été explicitement défini.

setbuf(3), définir le tampon(3), setlinebuf(3), setvbuf(3)
Toutes ces fonctions, sauf la dernière, renvoient void. Et setvbuf(3) n'est documenté que comme
retournant « non-zéro » en cas d'erreur. Il faut voir si errno(3) a été explicitement
défini.

strtod(3), strol(3), dit(3), marche(3), stroul(3), strtoull(3)
Ces fonctions renvoient 0 en cas d'erreur, mais c'est également une valeur de retour légitime. Il est
nécessaire de voir si errno(3) a été explicitement défini.

inoubliable(3)
Bien qu'un seul caractère de sauvegarde soit imposé par la norme ANSI C, il s'avère
dehors que [e]glibc permet plus... mais cela signifie qu'il peut échouer avec ENOMEM. Ça peut
échoue également avec EBADF si fp est faux. Le plus difficile de tous, si vous passez EOF une erreur
return se produit, mais errno n'est pas défini.

La bibliothèque libexplain détecte correctement toutes ces erreurs, même dans les cas où le
les valeurs d'erreur sont mal documentées, voire pas du tout.

ENOSPC, Non espace à gauche on dispositif
Lorsque cette erreur fait référence à un fichier sur un système de fichiers, la bibliothèque libexplain imprime le montage
point du système de fichiers avec le problème. Cela peut rendre la source de l'erreur beaucoup
plus clair.
write(fildes = 1 "example", data = 0xbfff2340, data_size = 5) a échoué, pas d'espace disponible
sur l'appareil (28, ENOSPC) car le système de fichiers contenant des fichiers ("/ home") n'a pas
plus d'espace pour les données
Au fur et à mesure que la prise en charge des appareils spéciaux est ajoutée, les messages d'erreur devraient inclure l'appareil
le nom et la taille réelle de l'appareil.

EROFS, Lecture seulement filet combustion propre
Lorsque cette erreur fait référence à un fichier sur un système de fichiers, la bibliothèque libexplain imprime le montage
point du système de fichiers avec le problème. Cela peut rendre la source de l'erreur beaucoup
plus clair.

Au fur et à mesure que la prise en charge des appareils spéciaux est ajoutée, les messages d'erreur devraient inclure l'appareil
nom et genre.
open(pathname = "/dev/fd0", O_RDWR, 0666) a échoué, système de fichiers en lecture seule (30, EROFS)
parce que la disquette a l'onglet de protection en écriture

...parce qu'un CD‐ROM n'est pas inscriptible
...parce que la carte mémoire a un jeu d'onglets de protection en écriture
...parce que la bande magnétique ½ pouce n'a pas de bague d'écriture

rebaptiser
La rebaptiser(2) l'appel système est utilisé pour changer l'emplacement ou le nom d'un fichier, en le déplaçant
entre les répertoires si nécessaire. Si le chemin de destination existe déjà, il sera
remplacé atomiquement, de sorte qu'il n'y a aucun point auquel un autre processus essayant de
accéder, il le trouvera manquant.

Il y a cependant des limitations : vous ne pouvez renommer un répertoire qu'au dessus d'un autre
répertoire si le répertoire de destination n'est pas vide.
rename(oldpath = "foo", newpath = "bar") a échoué, Répertoire non vide (39,
ENOTEMPTY) car newpath n'est pas un répertoire vide ; c'est-à-dire qu'il contient des entrées
autre que "." et ".."
Vous ne pouvez pas non plus renommer un répertoire au-dessus d'un autre.
rename(oldpath = "foo", newpath = "bar") a échoué, Pas un répertoire (20, ENOTDIR)
parce que oldpath est un répertoire, mais newpath est un fichier normal, pas un répertoire
L'inverse n'est pas autorisé non plus
rename(oldpath = "foo", newpath = "bar") a échoué, est un répertoire (21, EISDIR)
car newpath est un répertoire, mais oldpath est un fichier normal, pas un répertoire

Ceci, bien sûr, rend le travail de la bibliothèque libexplain plus compliqué, car le
dissocier(2) ou rmdir(2) l'appel système est appelé implicitement par rebaptiser(2), et ainsi tous les
dissocier(2) ou rmdir(2) les erreurs doivent également être détectées et traitées.

dup2
La dup2(2) l'appel système est utilisé pour créer un deuxième descripteur de fichier qui référence le
même objet que le premier descripteur de fichier. Généralement, cela est utilisé pour implémenter l'entrée du shell
et la redirection de sortie.

Ce qui est amusant, c'est que, tout comme rebaptiser(2) peut renommer atomiquement un fichier au-dessus d'un
fichier existant et supprimer l'ancien fichier, dup2(2) peut le faire sur un fichier déjà ouvert
descripteur.

Encore une fois, cela complique le travail de la bibliothèque libexplain, car le close(2)
l'appel système est appelé implicitement par dup2(2), et donc tout close(2) les erreurs doivent être
détectés et traités également.

AVENTURES IN IOCTL SUPPORT


La ioctl(2) l'appel système fournit aux auteurs de pilotes de périphériques un moyen de communiquer avec
espace utilisateur qui ne rentre pas dans l'API du noyau existante. Voir liste_ioctl (2).

Le décryptage Demande Nombres
D'un coup d'œil rapide au ioctl(2) interface, il semblerait qu'il y ait une grande mais finie
nombre de possibles ioctl(2) demandes. Chacun différent ioctl(2) la demande est effectivement
un autre appel système, mais sans aucune sécurité de type - le compilateur ne peut pas aider un
programmeur obtenir ces droits. C'était probablement la motivation derrière tcflushde Géographie (3) et avec la
amis.

L'impression initiale est que vous pourriez décoder ioctl(2) requêtes utilisant un énorme commutateur
déclaration. Cela s'avère infaisable car on découvre très rapidement qu'il est
impossible d'inclure tous les en-têtes système nécessaires définissant les différents ioctl(2)
demandes, car ils ont du mal à s'entendre.

Un examen plus approfondi révèle qu'il existe une gamme de numéros de demande « privés » et d'appareils
les auteurs de pilotes sont encouragés à les utiliser. Cela signifie qu'il y a un bien plus grand possible
ensemble de demandes, avec des numéros de demande ambigus, qui sont immédiatement apparents. Aussi,
il y a aussi des ambiguïtés historiques.

Nous savions déjà que le commutateur n'était pas pratique, mais maintenant nous savons que pour sélectionner le
nom et explication appropriés de la demande, nous devons considérer non seulement le numéro de la demande, mais
aussi le descripteur de fichier.

L'implémentation de ioctl(2) le support dans la bibliothèque libexplain est d'avoir une table de
pointeurs vers ioctl(2) descripteurs de demande. Chacun de ces descripteurs comprend une option
pointeur vers une fonction de désambiguïsation.

Chaque requête est en fait implémentée dans un fichier source séparé, de sorte que les
inclure les fichiers sont soulagés de l'obligation de jouer gentiment avec les autres.

Représentation
La philosophie derrière la bibliothèque libexplain est de fournir autant d'informations que
possible, y compris une représentation précise de l'appel système. Dans le cas d
ioctl(2) cela signifie imprimer le numéro de demande correct (par nom) ainsi qu'un numéro correct (ou
au moins utile) représentation du troisième argument.

La ioctl(2) le prototype ressemble à ceci :
int ioctl(int fildes, int requête, ...);
ce qui devrait déclencher vos alarmes de sécurité de type. Interne à [e]glibc, ceci est tourné
sous diverses formes :
int __ioctl(int fildes, int requête, argument long);
int __ioctl(int fildes, int requête, void *arg);
et l'interface d'appel système du noyau Linux attend
asmlinkage long sys_ioctl(fildes int non signé, requête int non signée, long non signé
argument);
L'extrême variabilité du troisième argument est un défi, lorsque la bibliothèque libexplain
essaie d'imprimer une représentation de ce troisième argument. Cependant, une fois que le numéro de demande
a été levée, chaque entrée de la table ioctl de la bibliothèque libexplain a un
fonction print_data personnalisée (OO fait manuellement).

Explications
Il y a moins de problèmes pour déterminer l'explication à utiliser. Une fois le numéro de demande
a été levée, chaque entrée de la table ioctl de la bibliothèque libexplain a un
fonction print_explanation (encore une fois, OO fait manuellement).

Contrairement aux appels système des sections 2 et 3, la plupart ioctl(2) les demandes n'ont pas d'erreurs
documenté. Cela signifie que, pour donner de bonnes descriptions d'erreurs, il est nécessaire de lire le noyau
sources à découvrir

· Quel errno(3) les valeurs peuvent être retournées, et

· la cause de chaque erreur.

En raison de la nature OO de la répartition des appels de fonction dans le noyau, vous devez lire
TOUTE sources mettant en œuvre cela ioctl(2) demande, pas seulement la mise en œuvre générique. Ce
est à prévoir que différents noyaux auront des numéros d'erreur différents et subtilement
différentes causes d'erreur.

EINVAL vs ENOTTY
La situation est encore pire pour ioctl(2) demandes que pour les appels système, avec EINVAL et
ENOTTY étant tous deux utilisés pour indiquer qu'un ioctl(2) la demande est inappropriée en ce que
contexte, et occasionnellement ENOSYS, ENOTSUP et EOPNOTSUPP (destiné à être utilisé pour les sockets) comme
bien. Il y a des commentaires dans les sources du noyau Linux qui semblent indiquer une évolution
le nettoyage est en cours. Pour plus de chaos, BSD ajoute ENOIOCTL à la confusion.

En conséquence, il faut prêter attention à ces cas d'erreur pour les corriger, en particulier
car EINVAL pourrait également faire référence à des problèmes avec un ou plusieurs arguments d'appel système.

intptr_t
La norme C99 définit un type entier qui est garanti pour pouvoir contenir n'importe quel pointeur
sans perte de représentation.

Le prototype d'appel système de la fonction ci-dessus serait mieux écrit
long sys_ioctl (fildes int non signé, requête int non signée, argument intptr_t);
Le problème est la dissonance cognitive induite par un périphérique ou un système de fichiers
ioctl(2) implémentations, telles que :
long vfs_ioctl (fichier struct *filp, cmd int non signé, argument long non signé);
La majorité des ioctl(2) les requêtes ont en fait un troisième argument int *arg. Mais l'avoir
déclaré long conduit à un code traitant cela comme long *arg. Ceci est inoffensif sur 32 bits
(sizeof(long) == sizeof(int)) mais méchant sur 64 bits (sizeof(long) != sizeof(int)).
Selon l'endian-ness, vous obtenez ou n'obtenez pas la valeur que vous attendez, mais vous toujours obtenez
un gribouillage de mémoire ou un gribouillage de pile également.

Écrire tout cela comme
int ioctl(int fildes, int requête, ...);
int __ioctl(int fildes, int requête, intptr_t arg);
long sys_ioctl (fildes int non signé, requête int non signée, argument intptr_t);
long vfs_ioctl (fichier struct *filp, cmd int non signé, arg intptr_t);
souligne que l'entier n'est qu'un entier pour représenter une quantité qui est presque
toujours un type de pointeur sans rapport.

CONCLUSION


Utilisez libexplain, vos utilisateurs l'aimeront.

DROIT D'AUTEUR


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

Utilisez Explain_lca2010 en ligne à l'aide des services onworks.net


Serveurs et postes de travail gratuits

Télécharger des applications Windows et Linux

  • 1
    LAMPÉE
    LAMPÉE
    SWIG est un outil de développement logiciel
    qui relie les programmes écrits en C et
    C++ avec une variété de
    langages de programmation. SWIG est utilisé avec
    différent...
    Télécharger SWIG
  • 2
    Thème de réaction WooCommerce Nextjs
    Thème de réaction WooCommerce Nextjs
    Thème React WooCommerce, construit avec
    Suivant JS, Webpack, Babel, Node et
    Express, en utilisant GraphQL et Apollo
    Client. Boutique WooCommerce dans React (
    contient : Produits...
    Télécharger le thème WooCommerce Nextjs React
  • 3
    archlabs_repo
    archlabs_repo
    Repo de paquets pour ArchLabs Ceci est un
    application qui peut également être récupérée
    de
    https://sourceforge.net/projects/archlabs-repo/.
    Il a été hébergé dans OnWorks en...
    Télécharger archlabs_repo
  • 4
    Projet Zéphyr
    Projet Zéphyr
    Le projet Zephyr est une nouvelle génération
    système d'exploitation en temps réel (RTOS) qui
    prend en charge plusieurs matériels
    architecturales. Il est basé sur un
    noyau à faible encombrement...
    Télécharger le projet Zéphyr
  • 5
    SCons
    SCons
    SCons est un outil de construction de logiciels
    c'est une alternative supérieure à la
    outil de construction classique "Make" qui
    nous connaissons et aimons tous. SCons est
    mis en place un...
    Télécharger SCons
  • 6
    PSeInt
    PSeInt
    PSeInt est un interpréteur de pseudo-code pour
    étudiants en programmation hispanophones.
    Son objectif principal est d'être un outil pour
    apprendre et comprendre les bases
    conception...
    Télécharger PSeInt
  • Plus "

Commandes Linux

  • 1
    7z
    7z
    7z - Un archiveur de fichiers avec le niveau le plus élevé
    ratio de compression ...
    Exécutez 7z
  • 2
    7za
    7za
    7za - Un archiveur de fichiers avec le plus haut
    ratio de compression ...
    Exécutez 7za
  • 3
    terrifiant
    terrifiant
    CREEPY - Une information de géolocalisation
    agrégateur DESCRIPTION : creepy est un
    application qui vous permet de rassembler
    informations relatives à la géolocalisation
    utilisateurs de...
    Courez effrayant
  • 4
    cricket-compiler
    cricket-compiler
    cricket - Un programme pour gérer le
    collecte et affichage de séries chronologiques
    Les données ...
    Exécutez la compilation de cricket
  • 5
    g-wrap-config
    g-wrap-config
    g-wrap-config - script à obtenir
    informations sur la version installée
    de G-Wrap...
    Exécutez g-wrap-config
  • 6
    g.accessgrass
    g.accessgrass
    g.access - Contrôle l'accès au
    jeu de cartes actuel pour les autres utilisateurs sur le
    système. Si aucune option n'est donnée, imprime
    statut actuel. MOTS CLÉS : général, carte
    gestion, p...
    Exécutez g.accessgrass
  • Plus "

Ad