значение ошибки не обновляется

Что может быть из-за проблемы, когда значение errno не обновляется во время последовательных вызовов функций сокета?

socket (AF_INET, -1, 0);
socket (AF_INET, SOCK_STREAM, -1);

Первый должен иметь errno = EINVAL Второй должен иметь errno = EPROTONOSUPPORT


Из приведенного ниже кода @JonathanLeffler вывод cygwin выглядит следующим образом:

*$ ./socket.exe
Error from socket(AF_INET, -1, 0): 124 (Socket type not supported)
-- errno = 124 (Socket type not supported)
Error from socket(AF_INET, SOCK_STREAM, -1): 123 (Protocol not supported)
-- errno = 123 (Protocol not supported)
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 123 (Protocol not supported)*

Указанный ниже код был отредактирован для создания сокета fd0 перед созданием сокета.

int fd0 = socket(AF_INET, SOCK_STREAM, 0);
if (fd0 < 0)
    printf("Error from socket(AF_INET, SOCK_STREAM, 0): %d (%s)\n", errno, strerror(errno));
else
{
    printf("socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = %d)\n", fd0);
    close(fd0);
}
printf("-- errno = %d (%s)\n", errno, strerror(errno));

int fd1 = socket(AF_INET, -1, 0);
.....

И результат следующий:

*$ ./socket.exe
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 0 (No error)
Error from socket(AF_INET, -1, 0): 124 (Socket type not supported)
-- errno = 124 (Socket type not supported)
Error from socket(AF_INET, SOCK_STREAM, -1): 123 (Protocol not supported)
-- errno = 123 (Protocol not supported)
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 123 (Protocol not supported)*

Ожидается, что первое и последнее создание сокета должны иметь одинаковые значения errno.


Но как объяснить этот результат? Создание 5-го и 6-го сокетов имеет одинаковое значение errno, но причина ошибки различна.

$ ./test_select.exe
[1] ierr = 124, iSocket = -1 socket(AF_INET, -1, 0);
 : Socket type not supported
[2] ierr = 124, iSocket = -1 socket(AF_INET, -1, 0);
 : Socket type not supported
[3] ierr = 124, iSocket = 3 socket(AF_INET, SOCK_STREAM, 0);
 : Socket type not supported
[4] ierr = 124, iSocket = -1 socket (AF_INET, -1, 0);
 : Socket type not supported
[5] ierr = 123, iSocket = -1 socket (AF_INET, SOCK_STREAM, -1);
 : Protocol not supported
[6] ierr = 123, iSocket = -1 socket (AF_INET, -1, 0)
 : Protocol not supported
[7] ierr = 124, iSocket = -1 socket(AF_INET, SOCK_STREAM, -1)
 : Protocol not supported

Фактический код выглядит следующим образом:

   sockfd = socket(AF_INET, -1, 0);
    err = errno;
    printf("[1] err = %d, sockfd = %d socket(AF_INET, -1, 0);\n", err, sockfd);
    perror(" ");
    sockfd = socket(AF_INET, -1, 0);
     err = errno;
    printf("[2] err = %d, sockfd = %d socket(AF_INET, -1, 0);\n", err, sockfd);
    perror(" ");
     sockfd = socket(AF_INET, SOCK_STREAM, 0);
    err = errno;
    printf("[3] err = %d, sockfd = %d socket(AF_INET, SOCK_STREAM, 0);\n", err, sockfd);
    perror(" ");
    close(sockfd);
    sockfd = socket(AF_INET, -1, 0);
    err = errno;
    printf("[4] err = %d, sockfd = %d socket (AF_INET, -1, 0);\n", err, sockfd);
    perror(" ");
    sockfd = socket(AF_INET, SOCK_STREAM, -1);
    err = errno;
    printf("[5] err = %d, sockfd = %d socket (AF_INET, SOCK_STREAM, -1);\n", err, sockfd);
    perror(" ");
    sockfd = socket(AF_INET, -1, 0);
    err = errno;
    printf("[6] err = %d, sockfd = %d socket (AF_INET, -1, 0)\n", err, sockfd);
    perror(" ");
    err = errno;
    sockfd = socket(AF_INET, SOCK_STREAM, -1);
    printf("[7] err = %d, sockfd = %d socket(AF_INET, SOCK_STREAM, -1)\n", err, sockfd);
    perror(" ");

person grace    schedule 18.04.2012    source источник
comment
Не могли бы вы предоставить какой-то контекст? Тот факт, что ВЫ ожидаете, что errno изменится, не означает, что это действительно должно произойти :)   -  person Scott Hunter    schedule 18.04.2012
comment
Вы уверены, что правильное значение должно быть EINVAL, а не EBADF?   -  person Dan Fego    schedule 18.04.2012
comment
извините за путаницу ... я обновил вопрос, это должен был быть socket (), а не select (), извините, мой плохой.   -  person grace    schedule 18.04.2012


Ответы (2)


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

Вы можете осмысленно проверить errno для получения информации только тогда, когда функция сообщает, что она не удалась (и даже в этом случае, только если на ее справочной странице указано, что она устанавливает errno). Вы можете обнаружить, что errno установлено в ненулевое значение успешной функции. Например, в Solaris вы часто найдете ENOTTY в errno после стандартного вызова ввода-вывода при записи в файл вместо терминала.

В вашем примере нет гарантии, какое из нескольких возможных значений ошибки будет установлено данным ошибочным вызовом функции. Пока сообщается об одном действительном условии ошибки, система сама решает, о какой ошибке она сообщает. Итак, вам нужно привести убедительный пример того, почему второй вызов должен возвращать EINVAL вместо EBADF; ваш вопрос в том виде, в котором он написан, не содержит достаточно информации, чтобы позволить нам рассуждать.

Также обратите внимание, что единственный безопасный способ объявить errno — через заголовок: #include <errno.h>. В частности, в многопоточных средах это часто не просто extern int errno;. Например, в Mac OS X это можно определить как:

extern int * __error(void);
#define errno (*__error())

То есть __error — это функция, возвращающая указатель на целое число, а errno — это макрос, который разыменовывает этот указатель, становясь модифицируемым lvalue.


Анализ кода

Данный код:

socket (AF_INET, -1, 0);
socket (AF_INET, SOCK_STREAM, -1);

Преобразованный в исполняемый код, это может стать:

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

int main(void)
{
    int fd1 = socket(AF_INET, -1, 0);
    if (fd1 < 0)
        printf("Error from socket(AF_INET, -1, 0): %d (%s)\n", errno, strerror(errno));
    else
    {
        printf("socket(AF_INET, -1, 0) succeeded (fd = %d)\n", fd1);
        close(fd1);
    }
    printf("-- errno = %d (%s)\n", errno, strerror(errno));

    int fd2 = socket(AF_INET, SOCK_STREAM, -1);
    if (fd2 < 0)
        printf("Error from socket(AF_INET, SOCK_STREAM, -1): %d (%s)\n", errno, strerror(errno));
    else
    {
        printf("socket(AF_INET, SOCK_STREAM, -1) succeeded (fd = %d)\n", fd2);
        close(fd2);
    }
    printf("-- errno = %d (%s)\n", errno, strerror(errno));

    int fd3 = socket(AF_INET, SOCK_STREAM, 0);
    if (fd3 < 0)
        printf("Error from socket(AF_INET, SOCK_STREAM, 0): %d (%s)\n", errno, strerror(errno));
    else
    {
        printf("socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = %d)\n", fd3);
        close(fd3);
    }
    printf("-- errno = %d (%s)\n", errno, strerror(errno));

    return(0);
}

Работая в Mac OS X 10.7.3, это производит:

Error from socket(AF_INET, -1, 0): 43 (Protocol not supported)
-- errno = 43 (Protocol not supported)
Error from socket(AF_INET, SOCK_STREAM, -1): 43 (Protocol not supported)
-- errno = 43 (Protocol not supported)
socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3)
-- errno = 43 (Protocol not supported)

Как я уже говорил ранее, возвращаемая ошибка зависит от системы.

person Jonathan Leffler    schedule 18.04.2012
comment
Я включил errno.h вместо extern int errno. Но тем не менее отображаемое значение errno по-прежнему равно EINVAL. Я прочитал в другом потоке, что обычно errno - это макрос, который вызывает функцию, возвращающую адрес номера ошибки для текущего потока, а затем разыменовывает его. - person grace; 18.04.2012
comment
Спасибо за указание на то, что возвращаемая ошибка зависит от системы. Я пытался использовать cygwin, и результат вашего кода выглядит следующим образом: $ ./socket.exe Error from socket(AF_INET, -1, 0): 124 (Socket type not supported) -- errno = 124 (Socket type not supported) Error from socket(AF_INET, SOCK_STREAM, -1): 123 (Protocol not supported) -- errno = 123 (Protocol not supported) socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3) -- errno = 123 (Protocol not supported) - person grace; 18.04.2012
comment
из предоставленного вами кода создание 3-го сокета должно завершиться успешно. Я скопировал создание 3-го сокета до создания 1-го сокета (fd0) и работал нормально. Вывод выглядит следующим образом: $ ./socket.exe socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3) -- errno = 0 (No error) Error from socket(AF_INET, -1, 0): 124 (Socket type not supported) -- errno = 124 (Socket type not supported) Error from socket(AF_INET, SOCK_STREAM, -1): 123 (Protocol not supported) -- errno = 123 (Protocol not supported) socket(AF_INET, SOCK_STREAM, 0) succeeded (fd = 3) -- errno = 123 (Protocol not supported) - person grace; 18.04.2012
comment
@grace: ваш первый комментарий с выводом программы показывает, что третий вызов выполнен успешно (вывод включает succeeded (fd = 3)). Тот факт, что errno не изменяется при любом системном вызове, демонстрируется безусловным выводом errno, значение которого не изменилось с момента последней ошибки. Это не было гарантировано. Например, если бы я позвонил close() в fd1 или fd2, то errno, вероятно, изменился бы на EBADF. Это подчеркивает мысль: не проверяйте errno, если функция не указывает, что она не удалась. - person Jonathan Leffler; 18.04.2012

errno никогда не очищается стандартными библиотечными функциями, а только устанавливается. Кроме того, он может быть ложно установлен любой стандартной библиотечной функцией, которая не задокументирована иным образом, чтобы запретить такое поведение.

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

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

person R.. GitHub STOP HELPING ICE    schedule 18.04.2012