почему fopen() или open () используют errno вместо простого возврата кода ошибки?

в обычном случае open() возвращает новый дескриптор файла, или -1, если произошла ошибка и в этом случае errno правильно настроена.

Я не понимаю, почему этот механизм errno здесь используется? какова цель этого? почему просто мы не можем сопоставить все ошибки с некоторым отрицательным возвратом нет?

как

fd = open("/dev/tty0", O_RDWR | O_SYNC);
if(fd == -1)
  printf("this is EACCES error");
else if (fd == -2)
    printf("this is EPERM error");

есть ли какая-либо польза errno механизм.? если да, то я хотел бы знать / понимать, то в других вещах я также могу использовать этот механизм.

4 ответов


С fopen возвращает FILE* вы не можете ожидать, что он вернет код ошибки в этом указателе: единственное "специальное" значение для указателей -0.

как вы заметили, для open это ограничение не действует. На самом деле системы как linux делают именно то, что вы предлагаете на их более низких уровнях. Системный вызов под капотом возвращает отрицательный код ошибки, если что-то пойдет не так. Этот (отрицаемый) код затем подключается к errno мелкой оболочкой пространства пользователя, которая затем возвращает -1 для указания ошибки в приложении.

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

это не идеально, но накладные расходы не так плохо, как это звучит, так как они явно признаки того, что должно происходить только в исключительных случаях.


для меня преимущество в том, что получение информации об ошибке унифицировано таким образом, возврат некоторого отрицательного значения будет работать нормально с open как он возвращает целое число, но fopen возвращает FILE * поэтому там придется использовать другую технику.


errno - Это код ошибки. Важно сопоставить ошибки с тем, что происходит на самом деле, чтобы вы могли принимать стратегические решения в своем коде о том, что делать дальше. Например, ERANGE который определен в errno.h, скажет вам, что результат strtol("0xfffffffff",NULL,0) находится вне диапазона этой функции. Что еще более важно в вашем примере, хорошо знать, есть ли у вас EACCES или EPERM ошибки, чтобы вы знали, как обрабатывать файл.

вы не можете сопоставить все проблемы с одним кодом ошибки поскольку у вас может быть несколько проблем, которые вы хотели бы поймать и обработать. Когда я говорю поймать, я не имею в виду try/catch.

использование errno устанавливает и механизм обработки ошибок, поэтому вы получаете больше информации, чем просто -1.

ERANGE, EACCES, EPERM и другие считаются макросами, которые сопоставляются с определенным номером ошибки для удобства.


предоставление каждой функции определенного набора возвращаемых значений делает слишком сложным написание кода в общем виде. С текущей семантикой вы можете принять универсальный шаблон:

int fd;
if ((fd = some_function(arg1, arg2)) == -1)
{
    perror("some_function");
    exit(1);
}

вы даже можете обернуть это в макросе:

#define CALL_OR_DIE(function, ret, ...)      \
    if ((ret = function(__VA_ARGS__)) == -1) \
    { perror(#function); exit(1); }

использование:

int fd;
CALL_OR_DIE(open, fd, "/dev/tty0", O_RDWR | O_SYNC);