考虑下面的代码行:
while((n = read(STDIN_FILENO, buff, BUFSIZ)) > 0)
根据我的理解, read/write
function是非缓冲I / O的一部分。 那么这是否意味着read()
函数将从stdio每次只读取一个字符? 换句话说,n的值将会是
-1 in case of error n = 0 in case of EOF 1 otherwise
如果不是这种情况,上面的read()
函数什么时候会返回,为什么?
注意:我也在考虑read()
将等待,直到它成功地从标准input读取BUFSIZ
字符数。 但是,在可读取的大小写字符数量less于BUFSIZ
的情况下会发生什么? 将读取永久等待或直到EOF到达( Ctrl + D
在unix或Ctrl + Z
在Windows上)?
另外,让我们说BUFSIZ = 100
和stdin = ACtrl+D
(即EOF紧跟在单个字符之后)。 现在while loop
会迭代多less次?
read()的行为方式取决于正在读取的内容。 对于普通文件,如果要求输入N个字符,如果可用,则会得到N个字符,如果文件结束,则会得到N个字符。
如果read()正在以规范/熟化模式从终端读取,则tty驱动程序一次提供一行数据。 所以,如果你告诉read()得到3个字符或者300,那么read会挂起,直到tty驱动程序看到一个换行符或者终端的定义的EOF键,然后read()将返回该行的字符数或者您请求的字符数量,以较小者为准。
如果read()正在以非规范/原始模式从终端读取,则read将立即访问按键。 如果您要求read()获取3个字符,则可能会返回0到3个字符的任意位置,具体取决于输入时间以及终端的配置方式。
如果在任何字符到达之前信号中断读取,则read()在面对信号时会有不同的表现,返回的字符数小于请求的字符数,或者当errno设置为EINTR时返回-1。
如果描述符已配置为非阻塞I / O,则read()的行为将有所不同。 如果没有输入立即可用,则read()将返回-1,并将errno设置为EAGAIN或EWOULDBLOCK。 这适用于套接字。
正如你所看到的,当你调用read()时,你应该准备好了。 您不会总是得到您请求的字符数,并且您可能会遇到像EINTR这样的非致命错误,这意味着您应该重试read()。
你的代码如下:
while((n = read(0, buff, BUFSIZ) != 0))
这是有缺陷的 – 括号表示它被解释为:
while ((n = (read(0, buff, BUFSIZ) != 0)) != 0)
布尔条件在赋值之前被求值,所以n
只会得到值0(条件不成立)和1(条件成立)。
你应该写:
while ((n = read(0, buff, BUFSIZ)) > 0)
这在EOF或读取错误上停止, n
让你知道你遇到了哪种情况。
显然,上面的代码是一个错字。
无缓冲I / O将读取您读取的字符数(但不是更多)。 由于EOF或错误,它可能会读取较少。 由于通话时间较短,因此可能会读取较少。 考虑一个终端; 通常情况下,这只会读到行的末尾,因为没有更多的可用。 考虑一个管道; 如果馈送进程已经产生了128个未读字节,那么如果BUFSIZ是4096,则只能从读取中获得128个字节。 无阻塞的文件描述符可能会返回,因为什么都不可用; 套接字可能会返回更少的字节,因为没有更多的信息可用; 磁盘读取可能会返回更少的字节,因为执行读取时文件中剩余的字节数少于请求的字节数。
但是,一般来说,如果请求多个字节, read()
将不会返回一个字节。
正如read()
页所述:
返回值
成功时返回读取的字节数(零表示文件结束),文件位置按此编号提前。 如果这个数字小于请求的字节数就不是错误; 这可能发生,例如因为现在实际上可用的字节更少(可能是因为我们接近文件结束,或者因为我们正在从管道读取或从终端读取),或者因为read()被中断信号。 出错时,返回-1,并适当地设置errno。 在这种情况下,没有指定文件位置(如果有的话)是否改变。
所以,每个read()
将读取最多的指定字节数; 但可能会少一些。 “非缓冲”意味着如果指定read(fd, bar, 1)
,读将只读取一个字节。 缓冲IO尝试读入BUFSIZ
量子,即使你只需要一个字符。 这听起来很浪费,但它避免了系统调用的开销,这使得它更快。
当它试图读取时会发生什么,并且什么都没有涉及阻塞的东西。 您可以调用open来读取阻塞或非阻塞的文件。 “阻塞”意味着等到有东西要回来。
这就是您在等待输入的shell中看到的内容。 它坐在那里。 直到你回来。
非阻塞意味着如果没有数据,读取将不返回任何数据字节。 根据很多其他的因素,你会得到一个完全正确的答案,阅读将把errno设置成像EWOULDBLOCK这样的东西,它可以让你知道为什么你的读取返回了零字节。 这不一定是一个致命的错误。
你的代码可以测试一个减号找到EOF或错误
当我们说read
是无缓冲的,这意味着在数据从底层的打开文件描述(这是一个潜在的共享资源)中取出之后,在进程级别上不会发生缓冲。 如果stdin
是一个终端,那么至少有两个额外的缓冲区在使用:
read
将提交已经提交的任何内容,直到传递给它的最大读取长度,但是它不能从行编辑缓冲区中取出任何东西。 如果你想禁用这个额外的缓冲层,你需要查找如何禁用使用tcsetattr
等终端熟/标准模式。