1

当我说标准输入时,我指的是 fd = 0 所指的流。

我正在学习一门涵盖块设备和字符设备的操作系统课程。它专门说键盘是一种字符设备。然而,当我们看到read系统调用时,我们被告知内核并不关心它从什么读取,只要它是块设备或块设备上的文件。

这是我们得到的代码:

#include <stdlib.h>
#include <unistd.h>

const int BUFFSIZE = 5;

int main () {
  int fd, n;
  char buffer[BUFFSIZE];

  int stdin = 0;
  int stdout = 1;
  int stderr = 2;

  do {
    n = read (0, buffer, BUFFSIZE);
    if (n < 0) {
      write (stderr, "Error occurred\n", 10);
    } else {
      write (stdout, "Entered if\n", 20);
      write (stdout, buffer, n);
    }
  } while (n > 0);
  return 0;
}

我的问题是:Linux 如何处理标准输入(fd = 0)?它是否被视为字符设备,或者内核是否进行了某种缓冲(根据我在运行代码时得到的结果来判断,这似乎很可能。)

此外,了解我是否可以使用 read 系统调用从一般的字符设备中读取会很有用。如果是这样,输入是否缓冲?

4

2 回答 2

6

内核通常在字符设备上做很少或没有缓冲。

从文件系统中的文件读取时,内核会进行一定量的缓冲。

你不能说设备标准输入是什么类型的,因为它因进程而异。默认情况下,fd 0 通常是用户的键盘,它是一个字符设备。但如果我说

program < file

那么 fd 0 就是一个普通文件。如果我说

program < /dev/hda0

那么 fd 0 是一个块设备。如果我这样做,我可能也可以设法让 fd 0 连接到网络套接字。

在 Linux 中,也有,但这也不是设备;它最终看起来像是指向实际设备的符号链接,无论它是什么。/proc/pid/fd/0/dev


附录:特定设备是否被缓冲实际上取决于该设备的驱动程序是如何编写的。任何给定的驱动程序可能会或可能不会实现某种形式的缓冲。此外,是否实际使用缓冲可能最终取决于其他因素。(例如,默认情况下,Unix 终端驱动程序都是行缓冲的,但是如果您将驱动程序置于“cbreak”或“raw”模式,则该缓冲将被关闭)。我认为您不能做出任何一般性陈述来说明字符或块设备是否已缓冲。


附录 2:当你开始剥离层时,它会变得非常复杂。Unix 努力(并且通常做得非常好)在按照我的意思去做和保持简单、愚蠢之间取得适当的平衡。例如,如果您有一个没有行缓冲的终端,并且您要求 10 个字符,但只有 3 个可用,read()将返回 3。这是正确的,但它表明某处仍有缓冲区,这三个字符在输入和阅读它们之间累积。此外,如果您只要求 3 个,但有 10 个可用,在某些情况下,我认为其他 7 个会为您保存,这再次表明相当数量的内核级缓冲。

但是在原始模式下,我敢肯定,如果您阅读速度不够快,您可能会丢失字符。将我们的注意力从终端驱动程序转移到网络套接字上,我曾认为在某些情况下,如果您read()在 UDP 模式套接字上执行操作,并且实际的 UDP 数据包大于您的读取请求,您可能会丢失那里的其余数据包, 也。[尽管评论者认为我可能是错的。](另一方面,TCP 模式的套接字显然有很大的缓冲!)

所以,底线是:规则可能很复杂,确切的细节肯定不仅取决于使用的特定设备驱动程序,还取决于潜在的无数其他细节。

于 2016-04-11T22:03:20.350 回答
1

Unix 中没有真正的标准输入。C 运行时库定义了一个符号 stdin,它与进程的第一个(第 0 个)文件描述符相关联。

按照惯例,Unix shell 在创建进程时会设置三个文件。按照惯例,它们也被称为标准输入、标准输出和标准错误。

unix 进程不需要这三个文件。您完全可以创建自己的 shell,然后创建进程而无需打开文件 0、1 或 2。

标准输入的行为将取决于它关联的“文件”(数据流)类型。标准输入可以映射到键盘,也可以映射到文件。无论哪种情况,您都可以读取数据。只有在后者中,您才能进行 fseek。

于 2016-04-11T22:01:04.377 回答