在 Linux 中 stdin 是否被视为字符设备?

Is stdin treated as a character device in Linux?

当我说 stdin 时,我指的是 fd = 0.

所指的 stream

我正在学习 OS 课程,该课程涵盖块设备和字符设备。它专门说键盘是一个字符设备。然而,当我们看到 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 系统调用来读取一般的字符设备会很有用。如果是这样,输入是否缓冲?

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

按照惯例,Unix shell在创建进程时会设置三个文件。按照惯例,它们也被称为 stdin、stdout 和 stderr。

unix进程不要求有这三个文件。您完全有可能创建自己的 shell,这将依次创建进程而无需打开文件 0、1 或 2。

stdin 的行为将取决于它所关联的 "file"(数据流)的类型。 Stdin 可以映射到键盘,也可以映射到文件。无论哪种情况,您都可以读取数据。只有在后者你才能做一个fseek。

内核通常很少或根本不对字符设备进行缓冲。

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

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

program < file

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

program < /dev/hda0

那么fd 0就是一个块设备。如果我努力的话,我可能也能设法让 fd 0 连接到网络套接字。

在Linux里还有/proc/<em>pid</em>/fd/0,不过那不是设备,任何一个;它最终看起来像是指向 /dev 中实际设备的符号链接,无论它是什么。


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


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

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

因此,归根结底:规则可能很复杂,准确的细节肯定不仅取决于所使用的特定设备驱动程序,还可能取决于无数其他细节。