2017-09-02 4 views
2

J'utilise un appel système et en cas d'échec, j'ai besoin de faire des choses différentes pour différents errnos.Comment vérifier la valeur de errno?

J'ai besoin d'écrire du code qui ressemble à ceci:

int res; 
res = systemCall(); 
if (res == -1) 
{ 
    if (errno == ENOMSG) 
    { 
     doSomething(); 
    } 
    else 
    { 
     doSomethingElse(); 
    } 
} 

perror ne fonctionne pas, car elle imprime uniquement la valeur. En ce qui concerne strerro - si c'est ce que j'ai besoin, je ne suis pas suer comment l'utiliser, car here il dit que la chaîne réelle n'est pas la même que l'erreur. Citation de la page man: "(Par exemple, si errnum est EINVAL, la description retournée sera" Invalid argument ")". J'utilise Linux. J'utilise Linux. Appels système: msgsend et msgrcv (https://linux.die.net/man/2/msgrcv). Je ne suis pas sûr de savoir de quelles bibliothèques C vous parlez.

Je vois que je ne me suis pas bien expliqué.

L'instruction if (errno == ENOMSG) est-elle valide? Y a-t-il une telle erreur errno? Fondamentalement ma question est: Que devrait aller dans la déclaration if afin de tester l'errno?

+1

Sur quel système d'exploitation? Quel appel système exact? Quelle bibliothèque C? S'il vous plaît ** modifier votre question pour l'améliorer ** –

+1

Chaque appel système a une liste de valeurs errno possibles (voir sa page 'man'), cela vous permettra de savoir ce que vous devriez tester. – Myst

+1

Ce que vous avez écrit est bien. Rappelez-vous, aucune fonction de bibliothèque ne met à zéro, et vous ne devriez pas le tester, sauf si une fonction indique une erreur et est documentée pour définir 'errno'. Notez que de nombreuses fonctions pthreads ne définissent pas 'errno', par exemple; ils renvoient le numéro d'erreur à la place. De même, les fonctions peuvent définir 'errno' sur une valeur non nulle même si elles réussissent. Par exemple, sur Solaris, si la sortie standard n'est pas un terminal, 'errno' peut être défini sur ENOTTY même si l'appel' printf() 'est réussi. –

Répondre

4

Je suppose que vous utilisez Linux, et je suppose que vous ne directement utiliser l'appel système, mais certains des emballages (simples) (à partir de votre bibliothèque C) répertoriés dans syscalls(2). Notez que certains appels système bizarres ne sont pas enveloppés par la bibliothèque C (un exemple bien connu d'appel système déballé serait sigreturn(2) que vous ne devriez probablement jamais utiliser). Notez que la bibliothèque C est GNU glibc, mais elle peut être musl-libc etc. Notez également que les appels système du noyau bruts ont des fonctions calling conventions différentes de celles de la fonction C ordinaire (donc, en pratique, un wrapper libc est requis et est chargé de traiter errno). Notez également que errno(3) est généralement une macro (se comportant presque comme une variable).

Les documents page de manuel msgrcv(2) que errno pourrait être l'un des E2BIG, EACCES, EFAULT ... ENOMSG, ENOSYS ... (se référer à cette page de manuel pour obtenir la liste de toutes les erreurs possibles).

Vous coderez quelque chose comme

ssize_t siz = msgrcv(msqid, msgp, msgsz, msgtyp, msgflg); 
if (siz<0) { // msgrcv failed and has set errno 
    if (errno == ENOMSG) 
    dosomething(); 
    else if (errno == EAGAIN) 
    dosomethingelse(); 
    /// etc 
    else { 
    syslog(LOG_DAEMON|LOG_ERR, "msgrcv failure with %s\n", 
      strerror(errno)); 
    exit(EXIT_FAILURE); 
    }; 
}; 

La déclaration if (errno == ENOMSG) .... valide?

Oui, c'est; vous souhaitez tester errno uniquement après une défaillance d'appel système (par exemple, lorsque siz<0).

Y a-t-il une telle variable errno?

Plus plus. Veuillez lire attentivement la documentation errno(3). Vous ne devez pas déclarer extern int errno; (ce qui était possible dans les années 1980, pas 21 st siècle), mais vous devriez toujours#include <errno.h> et utilisez errno comme si elle était une variable, mais il est presque toujours une macro (dont la définition apparaît dans /usr/include/bits/errno.h qui est inclus par /usr/include/errno.h). BTW, les installations de type SysV ont tendance à devenir obsolètes et ne sont pas toujours disponibles. Je recommande d'utiliser les installations de files d'attente de messages POSIX, lisez mq_overview(7).

Vous pouvez lire le Advanced Linux Programming téléchargeable gratuitement (un vieux livre, vous pouvez acheter quelque chose de mieux & plus récent) et/ou toutes les pages de manuel accessible depuis intro(2) & syscalls(2) & intro(3).

+0

Le lien vers le téléchargement Advanced Linux Programming peut être cassé ou obsolète. Ça ne semble pas fonctionner pour moi. –

+0

Corrigé, avec un lien aléatoire fonctionne aujourd'hui. En fait, mieux vaut lire un livre plus récent si vous pouvez vous le permettre. –

+0

Merci pour les heads up! Je vais chercher quelque chose de plus récent. Serait heureux d'entendre des suggestions si quelque chose vient à l'esprit. –

1

Inclure errno.h

Quelques exemples:

// Error codes 
#define EPERM  1 /* Operation not permitted */ 
#define ENOENT  2 /* No such file or directory */ 
#define ESRCH  3 /* No such process */ 
#define EINTR  4 /* Interrupted system call */ 
#define EIO   5 /* I/O error */ 
#define ENXIO  6 /* No such device or address */ 
#define E2BIG  7 /* Argument list too long */ 
#define ENOEXEC  8 /* Exec format error */ 
#define EBADF  9 /* Bad file number */ 
#define ECHILD  10 /* No child processes */ 
#define EAGAIN  11 /* Try again */ 
#define ENOMEM  12 /* Out of memory */ 
#define EACCES  13 /* Permission denied */ 
#define EFAULT  14 /* Bad address */ 
#define ENOTBLK  15 /* Block device required */ 
#define EBUSY  16 /* Device or resource busy */ 
#define EEXIST  17 /* File exists */ 
#define EXDEV  18 /* Cross-device link */ 
#define ENODEV  19 /* No such device */ 
#define ENOTDIR  20 /* Not a directory */ 
#define EISDIR  21 /* Is a directory */ 
#define EINVAL  22 /* Invalid argument */ 
#define ENFILE  23 /* File table overflow */ 
#define EMFILE  24 /* Too many open files */ 
#define ENOTTY  25 /* Not a typewriter */ 
#define ETXTBSY  26 /* Text file busy */ 
#define EFBIG  27 /* File too large */ 
#define ENOSPC  28 /* No space left on device */ 
#define ESPIPE  29 /* Illegal seek */ 
#define EROFS  30 /* Read-only file system */ 
#define EMLINK  31 /* Too many links */ 
#define EPIPE  32 /* Broken pipe */ 
#define EDOM  33 /* Math argument out of domain of func */ 
#define ERANGE  34 /* Math result not representable */ 

Votre mise en œuvre peut avoir plus errno comprend, comme par exemple /usr/include/asm-generic/errno.h.

4

Comment vérifier la valeur de errno:

  1. Vous devrez #include <errno.h>.
  2. Oui, vous pouvez certainement dire des choses comme if(errno == ENOENT) { ... }, et c'est la façon commune et recommandée de le faire.
  3. En général, ne pas utiliser errno pour déterminer qu'une erreur s'est produite. Vérifiez la valeur de retour de la fonction, et si la valeur de retour indique une erreur, vérifiez errno pour voir quelle était l'erreur. (Plus de détails ci-dessous.)
  4. errno ressemble à une variable, mais ce n'est pas le cas. Cela ne vous concerne pas tant que vous dites des choses comme if(errno == ENOENT) { ... }. Mais vous ne devriez probablement pas essayer de faire quelque chose comme int errno_ptr = &errno;.
  5. Vous pouvez utiliser des fonctions telles que perror() et strerror() pour obtenir des chaînes d'erreur lisibles par l'utilisateur correspondant aux valeurs errno. Mais, oui, les chaînes que vous obtenez sont généralement des choses comme "Aucun fichier ou répertoire". Il n'y a aucun moyen que je connaisse de convertir la valeur errno ENOENT à la chaîne "ENOENT".

Pour en dire un peu plus sur # 3. Parfois, il est tentant de dire quelque chose comme

errno = 0; 
printf("Hello, world!\n"); 
if(errno != 0) { 
    fprintf(stderr, "printf failed!\n"); 
} 

Mais ne faites pas cela. faire au lieu

errno = 0; 
int retval = printf("Hello, world!\n"); 
if(retval < 0) { 
    fprintf(stderr, "printf failed!\n"); 
} 

La raison en est que, quelque part dans le processus de faire son travail, printf aurait pu faire quelque chose qui a donné lieu à une erreur, ce qui a mis errno, mais printf aurait pu récupérer de cette erreur et continué pour terminer avec succès.

Il y a très peu de fonctions de bibliothèque qui sont garantis pas toucher errno s'il n'y avait pas une erreur (je pense qu'un exemple pourrait être atoi), mais en général, cela est quelque chose que vous devez faire attention .

Pour en dire un peu plus sur # 4.errno ressemble à une variable, et plus précisément, il ressemble à une variable globale. Mais bien sûr, les variables globales sont mauvaises. Mais errno a été autour pour toujours; il y a des dizaines de millions de lignes de code qui l'utilisent; c'est toujours plutôt pratique; il est trop tard pour "réparer". Ainsi, au lieu, si vous jeter un regard derrière le rideau, vous trouverez que la plupart des implémentations faire quelque chose comme

extern int __errno_pointer; 
#define errno (*__errno_pointer) 

ou

extern int *__errno_pointer_function(); 
#define errno (*__errno_function()) 

De cette façon, ils peuvent organiser errno fonctionner raisonnablement bien même dans, disons, du code multithread.