当我说stdin时,我指的是fd = 0引用的 stream 。
我正在修读一个涵盖块和字符设备的操作系统课程。它特别说键盘是一个字符设备。但是,当我们看到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 syscall来读取字符设备是很有用的。如果是,输入是缓冲的吗?
答案 0 :(得分:4)
内核通常对字符设备进行很少或没有缓冲。
当从文件系统中的文件读取时,内核会执行一定量的缓冲。
您不能说设备标准输入是什么类型,因为它因处理而异。默认情况下,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个会为你保存,再次暗示了相当数量的内核级缓冲。
但是在原始模式下,如果你没有足够快地阅读它们,我很确定你会丢失字符。将我们的注意力从终端驱动程序切换到网络套接字,我原以为在某些情况下如果在UDP模式套接字上执行read()
,并且实际UDP数据包大于您的读取请求,则可能会丢失其余的包也在那里。 [虽然一位评论者暗示我可能错了。](另一方面,TCP模式套接字显然是非常缓冲的!)
所以,底线:规则可能很复杂,精确的细节肯定不仅取决于使用的特定设备驱动程序,还可能取决于其他无数细节。
答案 1 :(得分:1)
Unix中没有真正的标准输入。 C运行时库定义了与进程的第一个(第0个)文件描述符相关联的符号stdin。
按照惯例,Unix shell在创建进程时会设置三个文件。按照惯例,它们也被称为stdin,stdout和stderr。
不要求unix进程具有这三个文件。您完全可以创建自己的shell,而不会打开文件0,1或2来创建进程。
stdin的行为将取决于它与之关联的“文件”(数据流)的类型。 Stdin可以映射到键盘,也可以映射到文件。在任何一种情况下,您都可以读取数据。只有在后者才能做到这一点。