当我说stdin时,我指的是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 occurredn", 10);
} else {
write (stdout, "Entered ifn", 20);
write (stdout, buffer, n);
}
} while (n > 0);
return 0;
}
我的问题是:Linux如何处理标准输入(fd=0)?它是被当作一个字符设备来处理,还是内核做了某种缓冲(这似乎是根据我运行代码时得到的结果来判断的。)
此外,了解我是否可以使用read syscall从字符设备中进行读取,这将非常有用。如果是,输入是否已缓冲?
内核通常很少或根本不缓冲字符设备。
内核在读取文件系统中的文件时会进行一定量的缓冲。
你不能说什么样的设备标准输入,因为它因过程而异。默认情况下,fd 0通常是用户的键盘,它是一个字符设备。但如果我说
program < file
则fd 0是一个普通文件。如果我说
program < /dev/hda0
则fd 0是块设备。如果我能做到这一点,我可能也能把fd 0连接到网络插座上。
在Linux中,也有/proc/pid/fd/0
,但它也不是一个设备;它最终看起来像是/dev
中实际设备的符号链接,不管它是什么
附录:一个特定的设备是否被缓冲实际上取决于该设备的驱动程序是如何编写的。任何给定的驱动程序可以实现也可以不实现某种形式的缓冲。此外,缓冲是否实际使用可能最终取决于其他因素。(例如,Unix终端驱动程序默认情况下都是行缓冲的,但如果将驱动程序设置为"cbreak"或"raw"模式,则会关闭缓冲)。我认为你不能笼统地说字符或块设备有缓冲区或没有缓冲区。
附录2:当你开始剥离这些层时,它可能会变得非常复杂。Unix非常努力地(而且通常做得很好)在"做我的事"one_answers"保持简单、愚蠢"之间取得正确的平衡。例如,如果您有一个而非行缓冲的终端,并且您要求输入10个字符,但只有3个可用,则read()
将返回3。这是正确的,但它表明,在某个地方仍然有一个缓冲区,这三个字符在输入它们和阅读它们之间积累起来。此外,如果你只要求3个,但有10个可用,在某些情况下,我认为其他7个会为你节省下来,这再次表明有相当多的内核级缓冲。
但在原始模式下,如果你读得不够快,我敢肯定你会丢失字符。将我们的注意力从终端驱动程序转移到网络套接字上,我曾认为,在某些情况下,如果你在UDP模式套接字上执行read()
,而实际的UDP数据包比你的读取请求大,你也可能会丢失那里的其余数据包。[尽管一位评论者认为我可能错了。](另一方面,TCP模式的套接字显然有很大的缓冲!)
所以,底线是:规则可能很复杂,确切的细节肯定不仅取决于使用的特定设备驱动程序,还可能取决于无数其他细节。
Unix中没有真正的stdin。C运行时库定义了一个符号stdin,该符号与进程的第一个(第0个)文件描述符相关联。
按照惯例,Unix shell在创建进程时会设置三个文件。按照惯例,它们也被称为stdin、stdout和stderr。
unix进程不需要有这三个文件。您完全可以创建自己的shell,从而在不打开文件0、1或2的情况下创建进程。
stdin的行为将取决于它所关联的"文件"(数据流)的类型。Stdin可以映射到键盘,也可以映射到文件。无论哪种情况,都可以读取数据。只有在后者中,你才能做fseek。