为什么成功读取数据集的/ dev源errno?

C中的简单测试程序名为get1:

#include <stdio.h>
#include <string.h>
#include <errno.h>

int main(void) {
    errno = 0;
    int ch = fgetc(stdin);
    printf("ch = %d\n", ch);
    if (errno)
        printf("errno = %d: %s\n", errno, strerror(errno));
    return 0;
}

它只打印以十进制读取的第一个字节,然后显示errno和相关的错误消息,如果errno不为零.

一些结果(foo是一个文本文件,空是一个长度为零的文件):

% ./get1 < foo
ch = 104
% ./get1 < empty
ch = -1

好的,正如所料.然而:

% ./get1 < /dev/zero 
ch = 0
errno = 25: Inappropriate ioctl for device
% ./get1 < /dev/null 
ch = -1
errno = 25: Inappropriate ioctl for device
% ./get1 < /dev/random 
ch = 196
errno = 25: Inappropriate ioctl for device

读取工作正常,但是当我从这些设备中读取时,它正在设置errno.为什么?

那是在macOS(达尔文)上.我在Linux(Debian)和NetBSD上得到了同样的东西,除了针对/ dev / random的不同错误(其他设备上的错误与macOS上的错误相同):

% ./get1 < /dev/random 
ch = 170
errno = 22: Invalid argument

据我所知,如果你没有得到EOF,那就没有错误.如果你得到EOF,那么你检查errno以查看是否有错误.在所有这些情况下,从设备获取字节似乎没有错误,也不应该存在.然而,errno已定.

(如果有人想知道,删除printf不会改变任何东西.)

事实证明,在AIX,SunOS或OpenSUSE上从这样的设备读取时,不会设置errno.

那么这里的交易是什么?这是一个错误,尽管很普遍吗?这是可接受的行为吗?这是预期的行为吗?为什么设置errno?

解决方法:

我不会说它是预期的行为(实际上,我不能在我方便的系统上重现它),但是C标准和Posix都允许这样做.

C标准和Posix通常都禁止库函数将errno设置为0,因此它们无法清除errno以指示成功.但是,C允许通过库函数修改errno“是否存在错误,前提是errno的使用没有记录在
函数描述“(§7.5第3段).C标准没有记录fgetc对errno的使用.

Posix更灵活:它指定了

The setting of errno after a successful call to a function is unspecified unless the description of that function specifies that errno shall not be modified.

它确实记录了fgetc对errno的使用,但该文档并未指出errno不会被成功的调用修改,只是它应该被读错误修改.

具体而言,Posix所说的是

If a read error occurs, the error indicator for the stream shall be set, fgetc() shall return EOF, and shall set errno to indicate the error.

换句话说,EOF可能表示调用成功(因为文件末尾不是错误)或者它可能表示错误.只有在后一种情况下才将errno设置为有意义的值.因此,在检查errno之前,您需要验证ferror(stdin)是否报告错误.

因此,正如我所说,标准允许这种行为.为什么fgetc实现会这样做?我的猜测是你的系统上的libc实现在第一次调用时正在进行某种延迟初始化.

上一篇:PyAudio [Errno -9981] Input overflowed错误


下一篇:Last_IO_Errno: 1032或者 Last_IO_Errno: 1062