web-dev-qa-db-fra.com

Comment vérifier la valeur de errno?

J'utilise un appel système et en cas d'échec, je dois faire différentes choses pour différents errnos.

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

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

perror n'aide pas, car il imprime uniquement la valeur.

En ce qui concerne strerro - si c'est ce dont j'ai besoin, je ne sais pas comment l'utiliser, car ici il dit que la chaîne réelle n'est pas la même que l'erreur. Citation de la page de manuel: "(Par exemple, si errnum est EINVAL, la description retournée sera" Argument invalide ")".

J'utilise Linux. Appels système: msgsend et msgrcv ( https://linux.die.net/man/2/msgrcv ). Je ne sais pas de quelles bibliothèques C vous parlez.

Je vois que je ne m'explique pas bien.

La déclaration est-elle valable (errno == ENOMSG)? Existe-t-il une telle variable errno? Fondamentalement, ma question est la suivante: que doit contenir l'instruction if pour tester l'errno?

7
Hana

Je suppose que vous utilisez Linux et je suppose que vous n'utilisez pas directement l'appel système, mais certains des wrappers (simples) (de votre Bibliothèque C) répertoriée dans syscalls (2) . Notez que certains appels système étranges ne sont pas encapsulés par la bibliothèque C (un exemple bien connu d'appel système non encapsulé serait sigreturn (2) que vous ne devriez probablement jamais utiliser). Assez souvent, la bibliothèque C est GNU glibc , mais cela peut être musl-libc etc. Notez également que les appels système bruts du noyau ont différents conventions d'appel que la fonction C ordinaire (donc en pratique un wrapper libc est requis, et est en charge de traiter avec errno). Notez également que errno (3) est généralement une macro (se comportant presque comme une variable).

La page de manuel msgrcv (2) indique que errno pourrait être l'un de E2BIG, EACCES, EFAULT ... ENOMSG, ENOSYS ... (reportez-vous à cette page de manuel pour obtenir la liste de toutes les erreurs possibles).

Donc, vous coderiez 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); 
  };
};

L'instruction if (errno == ENOMSG) .... est-elle valide?

Oui, ça l'est; vous souhaitez tester errno uniquement après un échec d'appel système (par exemple lorsque siz<0).

Existe-t-il une telle variable errno?

Plus maintenant. Veuillez lire attentivement la documentation errno (3) . Vous ne devez pas déclarer extern int errno; (C'était possible dans les années 80, pas en 21st siècle) mais vous devriez toujours #include <errno.h> et utiliser errno comme si c'était une variable, mais c'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 style 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 voudrez peut-être lire le programme de téléchargement gratuit Advanced Linux Programming (un vieux livre; vous pouvez acheter quelque chose de mieux et de plus récent) et/ou toutes les pages de manuel accessibles depuis intro (2) & syscalls (2) & intro (3) .

10

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 manière courante et recommandée de le faire.
  3. En général, faites pas utilisez 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 d'informations ci-dessous.)
  4. errno ressemble à une variable, mais ce n'est pas le cas. Cela ne vous concerne pas tant que vous dites simplement 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'homme correspondant aux valeurs de errno. Mais, oui, les chaînes que vous obtenez sont généralement des choses comme "Aucun fichier ou répertoire". Je ne connais aucun bon moyen de convertir la valeur errno ENOENT en 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 fais pas ça. Au lieu de cela,

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 réalisation de son travail, printf peut avoir fait quelque chose qui a entraîné une erreur, quelque chose qui a défini errno, mais printf a peut-être récupéré de cette erreur et a continué à se terminer avec succès.

Il y a très peu de fonctions de bibliothèque qui sont garanties pas pour toucher errno s'il n'y a pas eu d'erreur (je pense qu'un exemple pourrait être atoi), mais en général, c'est quelque chose il faut 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 existe depuis toujours; il y a des dizaines de millions de lignes de code qui l'utilisent; c'est toujours assez pratique; il est trop tard pour le "réparer". Donc, à la place, si vous regardez derrière le rideau, vous constaterez que la plupart des implémentations font 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 faire en sorte que errno fonctionne raisonnablement correctement même dans, disons, du code multithread.

11
Steve Summit

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 implémentation peut comporter plusieurs erreurs, comme par exemple /usr/include/asm-generic/errno.h.

3
P__J__