从标准input读()

考虑下面的代码行:

while((n = read(STDIN_FILENO, buff, BUFSIZ)) > 0) 

根据我的理解, read/writefunction是非缓冲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 + DunixCtrl + Z在Windows上)?

另外,让我们说BUFSIZ = 100stdin = 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量子,即使你只需要一个字符。 这听起来很浪费,但它避免了系统调用的开销,这使得它更快。

  1. 读取尝试获取所有请求的字符。
  2. 如果EOF在所有请求的字符可以返回之前发生,它返回它得到之后得到的下一个读取返回-1,让你知道你的文件结束。

当它试图读取时会发生什么,并且什么都没有涉及阻塞的东西。 您可以调用open来读取阻塞或非阻塞的文件。 “阻塞”意味着等到有东西要回来。

这就是您在等待输入的shell中看到的内容。 它坐在那里。 直到你回来。

非阻塞意味着如果没有数据,读取将不返回任何数据字节。 根据很多其他的因素,你会得到一个完全正确的答案,阅读将把errno设置成像EWOULDBLOCK这样的东西,它可以让你知道为什么你的读取返回了零字节。 这不一定是一个致命的错误。

你的代码可以测试一个减号找到EOF或错误

当我们说read是无缓冲的,这意味着在数据从底层的打开文件描述(这是一个潜在的共享资源)中取出之后,在进程级别上不会发生缓冲。 如果stdin是一个终端,那么至少有两个额外的缓冲区在使用:

  1. 终端缓冲区,它可能会保持1-4k的数据,直到。
  2. 内核的熟化/规范模式缓冲区用于终端上的行输入/编辑,它允许用户通过按Enter键在行上执行原始编辑(退格,后退,删除行等),直到它被提交(到上述缓冲区) 。

read将提交已经提交的任何内容,直到传递给它的最大读取长度,但是它不能从行编辑缓冲区中取出任何东西。 如果你想禁用这个额外的缓冲层,你需要查找如何禁用使用tcsetattr等终端熟/标准模式。