Как проверить значение errno?

Я использую системный вызов, и в случае сбоя мне нужно делать разные вещи для разных ошибок.

Мне нужно написать код, который выглядит примерно так:

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

perror не помогает, потому что печатает только значение.

Что касается strerro - если это то, что мне нужно, я не знаю, как его использовать, потому что здесь говорит, что фактическая строка не совпадает с ошибкой. Цитата из справочной страницы: «(Например, если errnum равен EINVAL, возвращаемое описание будет «Неверный аргумент»)».

Я использую Линукс. Системные вызовы: msgsend и msgrcv (https://linux.die.net/man/2/msgrcv< /а>). Я не уверен, о каких библиотеках C вы спрашиваете.

Вижу, я плохо объяснил.

Верно ли выражение if (errno == ENOMSG)? Есть ли такая переменная errno? В основном мой вопрос: что должно быть в инструкции if, чтобы проверить errno?


person Hana    schedule 02.09.2017    source источник
comment
На какой операционной системе? Какой именно системный вызов? Какая библиотека Си? Пожалуйста, отредактируйте свой вопрос, чтобы улучшить его   -  person Basile Starynkevitch    schedule 02.09.2017
comment
У каждого системного вызова есть список возможных значений errno (см. man страницу), это позволит вам узнать, что вам следует протестировать.   -  person Myst    schedule 02.09.2017
comment
То, что вы написали, хорошо. Помните, что никакая библиотечная функция не устанавливает errno в ноль, и вы не должны тестировать ее, если только функция не указывает на ошибку и документально не устанавливает errno. Обратите внимание, что многие функции pthreads, например, не устанавливают errno; вместо этого они возвращают номер ошибки. Кроме того, функции могут устанавливать errno в ненулевое значение даже в случае успеха. Например, в Solaris, если стандартный вывод не является терминалом, errno можно установить в ENOTTY, даже если вызов printf() выполнен успешно.   -  person Jonathan Leffler    schedule 02.09.2017


Ответы (3)


Я предполагаю, что вы используете Linux, и я полагаю, что вы не напрямую используете системный вызов, а некоторые (простые) оболочки (из вашей библиотеки C), перечисленные в системные вызовы(2). Обратите внимание, что некоторые странные системные вызовы не обертываются библиотекой C (хорошо известным примером развернутого системного вызова может быть sigreturn(2), который вам, вероятно, никогда не следует использовать). Довольно часто библиотека C — это GNU glibc, но это может быть и musl-libc и т. д. Также обратите внимание, что необработанные системные вызовы ядра имеют разные соглашения о вызовах, чем обычные функции C (поэтому на практике требуется оболочка libc, которая отвечает за работу с errno). Заметьте также, что errno(3) обычно является макросом (почти ведет себя как некоторая переменная).

msgrcv(2) документирует, что errno может быть одним из E2BIG, EACCES , EFAULT ... ENOMSG, ENOSYS ... (обратитесь к этой справочной странице, чтобы получить список всех возможных ошибок).

Итак, вы бы написали что-то вроде

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); 
  };
};

Является ли заявление if (errno == ENOMSG) .... действительным?

Да, это; вы хотите протестировать errno только после некоторого сбоя системного вызова (например, когда siz<0).

Есть ли такая переменная errno?

Уже нет. Пожалуйста, внимательно прочитайте документацию errno(3). Вы не должны объявлять extern int errno; (это было возможно в 1980-х, а не в 21st веке), но вы должны всегда #include <errno.h> и использовать errno как если бы это была переменная, но это почти всегда является некоторым макросом (определение которого появляется в /usr/include/bits/errno.h, который включен в /usr/include/errno.h).

Кстати, средства в стиле SysV, как правило, устаревают и не всегда доступны. Я рекомендую использовать средства очередей сообщений POSIX, прочитайте mq_overview(7). .

Вы можете прочитать бесплатно загружаемый Advanced Linux Programming (старая книга; вы можете купить что-то лучше и новее) и/или все справочные страницы, доступные на intro(2) & системные вызовы(2) и введение(3 ).

person Basile Starynkevitch    schedule 02.09.2017
comment
Ссылка на загрузку Advanced Linux Programming может быть недействительной или устаревшей. Кажется, у меня не работает. - person Matthew Plemmons; 25.01.2018
comment
Исправлено, сегодня работает какая-то рандомная ссылка. На самом деле, лучше прочитать новую книгу, если вы можете себе это позволить. - person Basile Starynkevitch; 25.01.2018
comment
Спасибо за внимание! Поищу что-нибудь поновее. Буду рад услышать предложения, если что-то придет в голову. - person Matthew Plemmons; 25.01.2018
comment
Я дал ссылку на копию ALP, которую сегодня можно скачать. Вам разрешено распространять его (но IANAL) - person Basile Starynkevitch; 25.01.2018

Как проверить значение errno:

  1. Вам нужно будет #include <errno.h>.
  2. Да, вы определенно можете сказать что-то вроде if(errno == ENOENT) { ... }, и это общепринятый и рекомендуемый способ.
  3. Как правило, не используйте errno, чтобы определить, что произошла ошибка. Проверьте возвращаемое значение функции, и если возвращаемое значение указывает на ошибку, проверьте errno, чтобы узнать, в чем заключалась ошибка. (Подробнее об этом ниже.)
  4. errno выглядит как переменная, но на самом деле это не так. Это вас не касается, пока вы просто говорите что-то вроде if(errno == ENOENT) { ... }. Но вам, вероятно, не стоит пытаться сделать что-то вроде int errno_ptr = &errno;.
  5. Вы можете использовать такие функции, как perror() и strerror(), чтобы получить удобочитаемые строки ошибок, соответствующие значениям errno. Но да, строки, которые вы получаете, обычно имеют вид «Нет такого файла или каталога». Я не знаю хорошего способа преобразовать значение errno ENOENT в строку "ENOENT".

Еще немного о №3. Иногда хочется сказать что-то вроде

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

Но не делай этого. Вместо этого сделайте

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

Причина в том, что где-то в процессе выполнения своей работы printf мог сделать что-то, что привело к ошибке, что-то, что установило errno, но printf могло восстановиться после этой ошибки и продолжить успешное завершение.

Есть очень мало библиотечных функций, которые гарантированно не коснутся errno, если ошибки не было (я думаю, одним из примеров может быть atoi), но в целом с этим нужно быть осторожным. из.

Еще немного о №4. errno выглядит как переменная, точнее, как глобальная переменная. Но, конечно, глобальные переменные — это плохо. Но errno существовал всегда; его используют десятки миллионов строк кода; это все еще в основном довольно удобно; уже поздно "исправлять". Итак, вместо этого, если вы заглянете за кулисы, вы обнаружите, что большинство реализаций делают что-то вроде

extern int __errno_pointer;
#define errno (*__errno_pointer)

or

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

Таким образом, они могут обеспечить правильную работу errno даже, скажем, в многопоточном коде.

person Steve Summit    schedule 02.09.2017

Включить errno.h

Некоторые примеры:

// 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 */

В вашей реализации может быть больше ошибок, например /usr/include/asm-generic/errno.h.

person 0___________    schedule 02.09.2017