Kernel Stdio Theory

来自osdev
跳到导航 跳到搜索

标准输入/输出理论

什么是标准输入/输出 ?

标准输入和输出以及标准错误是作为C标准库的一部分实现的流。 流是用于访问文件,硬件资源或其他进程的读/写接口。

包含 stdio.h 标头后,将自动创建三个流,并将其与环境的标准输入,标准输出和标准错误流相关联。 这是常规的,并且在大多数情况下是默认的,用于将过程的标准输出和标准误差绑定到打开它的终端。 除非重定向StdIn,否则标准输入的默认来源将被视为键盘。

这些效果由C库处理,并且它们与底层OS接口以提供对流资源的访问。流资源可能具有以下属性:

  • Read/Write
  • Text/Binary
  • Buffered/Unbuffered

在大多数情况下,默认情况下,stdoout是缓冲的,而StdErr是未缓冲的。 这让用户立即看到StdErr上的数据输出。 缓冲可以是 “行” 缓冲的,也可以是 “完全” 缓冲的。 但是,C标准库的复杂性并不是本文的重点。

内核必须提供基础设备的API并将其呈现给正在运行的应用程序。 出于编写目的,所有应用程序默认都绑定到StdIn的键盘和stdoout的控制台。

由于C用于开发Unix内核,并且Unix提供了将设备抽象为文件的抽象,因此进程可以轻松地将设备作为资源流打开,并将其StdIO连接到设备以进行读取或写入。

关于Unix的StdIO如何工作的一些基本知识的经典示例是终端本身。 请注意,无需通过文件来提供此类抽象。 Windows API通过其API函数调用提供抽象。 程序可能会访问StdIn,stdoout和StdErr,但这些实际上仅在控制台模式下工作时才相关。 对于GUI应用程序,应用程序开发人员会用相关的API功能代替StdIn、stdoout和StdErr。 如: 错误和警告等的警报框

在Unix内核中,默认的标准输出连接到用户的终端设备。 (例如: /dev/tty0)。 尝试进入Unix/Linux命令提示符并键入 我是谁 。 这是表示您当前终端的标准输出的设备。 在微型计算机 (您现在正在使用的计算机) 上,没有连接到大型大型机的终端,因此现代的Linux/Unix微型计算机无论如何都只创建一个终端设备,并将其与内核的标准输出相关联。

现在: 尝试 echo Hello从Std输出! >file

通过将程序的标准输出重定向到文件,将字符串 “Hello从Std输出” 打印到名为 “文件” 的文件。 该程序为其stdot设备提供了文件的句柄,并出于所有意图和目的将其写入文件。

返回并通过运行 who am i 或简单地键入 tty 来找出您是哪个终端。 从 tty 中获取输出,并将其放在行的末尾,即上次 “文件”。 我的内核将我的终端报告为/dev/pts/1。

现在试试: echo Hello从Std输出!>/dev/pts/1

这一次,你会看到终端上的 “回声” 发出的声音。 为什么? 您明确地将Stdout的输出重定向到您的Stdout。 /dev/pts/1在我的情况下,无论您获得什么,都与当前用户的标准输出同义。

有趣的是,此文件 “也” 充当Stdin设备: 从中读取会导致您在键盘上键入的所有字符都报告给当前从中读取的程序。

unix cat 命令读取并输出它在文件中看到的内容。 当您在键盘上键入时,这是针对Stdin的,应用程序可以从中读取。 为了更快地获得更好的理解,请尝试:

cat /dev/pts/1

现在继续输入单词和字符,然后在您喜欢的时候按enter键。 您在终端键入的字符将发送到Stdin,cat 正在从中读取。 当您按下 “输入” 时,内核停止从键盘读取,已经到达当前输入位的行尾,并将这行键盘输入发送到标准输入,当前,cat 正在读取。 Cat认为它是从普通文件中读取的,它执行了应该执行的操作: 将文件的内容回显到终端上。

完成键入行后,按 “ctl d”,这是 * nix中的 “不再输入/输入结束” 的同义词。 在处理文件时,这是 “文件结束” 的同义词。 内核接收到没有更多输入的指示,并将其转发到Stdin,后者将其发送给 catcat 将 “没有更多数据” 信号解释为表示已到达其当前正在读取的文件的末尾。 就像任何其他文件一样,它停止从内核读取。 这是所有Unices的共同点,因为他们将所有内容都视为普通文件。

但是我们可以理解,该文件的动态性质终端StdIO文件表明它显然不是磁盘上的普通文件,而是指向管理Stdin和stdoout的内核API的链接。

如何在我的内核中实现StdIO ?

关于设计考虑的简短论述

大多数早期开发人员没有考虑到他们的内核应该为应用程序实现某种形式的标准输入/输出的事实。 大多数操作系统开发人员编写的两个最早期的驱动程序实际上是两个驱动程序,它们应该是标准输入/输出设置的一部分: 控制台驱动程序和键盘驱动程序。

请注意,Stdin和stdot不必 “绑定” 到键盘设备,控制台屏幕或其他屏幕输出。 程序可以打开一个文件句柄,并将其写入为Std输出流。

现在,在包括作者在内的大多数内核中,在启动时实现任何形式的stdoout都是相对没有意义的: 除了内核之外,没有应用程序可以使用它。 不仅如此,stdoout还必须能够绑定到需要内核支持的多个流。

(Todo: 给出可以绑定到StdIO的设备/资源 (例如文件) 的示例)。

虽然屏幕不是stdot可以绑定到的唯一设备或资源,但有必要注意的是,从所有指示来看,所有OSDev-ers都编写了一个驱动程序,该驱动程序在启动时直接引用VGA文本模式Framerbuffer。 这不是有害或错误的。 但是,当内核能够运行应用程序时,内核中必须有一个可用的资源来支持至少将控制台屏幕作为stdoout资源写入。 当然,在启动后,当内核的图形端必须发挥作用时,由于缺乏适当的设计,许多 “仍然” 不会编写stdoter界面。

通常可以假设stdoout是应用程序可以访问的读或写流。

让我们进行一个整洁的实验,以了解 * Nix中的StdIO终端文件会发生什么: 在 * Nix下打开两个终端窗口。 在一个终端上,键入命令: cat /dev/pts/1 (或者无论您当前的终端设备是什么。从现在开始,当我键入/dev/pts/1时,这就是我所指的),然后按enter键。 你会看到猫已经开始从终端阅读。

在第二个上,键入 echo <在此处插入随机短语>。 请注意,当shell从键盘读取输入时,它允许您通过转义发送不可打印的字符。 如果你输入

echo hello \<enter> Jane\<enter> I\'m glad to meet you<enter>

您会发现 “转义” <enter> 按键不会导致 echo 命令被处理,但是shell将其解释为文字换行字符,并继续读取要发送给 echo 的字符。 行提要将像其他任何字符一样发送到 echo。所以你也可以在多行上键入内容。

我们这里有一个设置,其中 cat 正在从终端读取,而 echo 正在对其进行写入。 保持cat打开的窗口,并继续向终端发送 echo 命令。

您会发现带有 cat 的窗口什么也没有显示。 现在修改您的 echo 命令直接写入终端:

echo <insert random phrase here> >/dev/pts/1.

现在发生了什么?是的。 cat 读取 echo 发送的字符。 那为什么Cat以前没有得到它们?每个应用程序都有其 “自己的” 标准输出,因此 echo 正在写入 “its” 标准输出,而不是写入终端/系统标准输出。

回到我们以前所讨论的,我说每个应用程序都应该有自己的stdoout。 本文尚未成熟,目前仅考虑基本的StdIn设备之一: 键盘。 将Stdin连接到键盘的有效设计是:

  • 执行任务。 有一个全局变量来指示哪个进程是当前活动的进程,因此将从用户的键盘输入中读取。 (请注意,我没有说哪个是当前的 “执行” 过程,而是当前的 “活跃” 过程。 即: 用户在他面前打开的那个,即当前活动的光标所在的位置。
  • 当引发键盘IRQ时,键盘驱动程序将从设备中获取击键,并将其附加到StdIn以进行活动进程。 活动进程从StdIn读取,并获取放置在 “its” Stdin缓冲区中的键。 问题 : 但是在后台运行的其他应用程序呢?就像用户打开了记事本,并且在论坛上发布了Firefox一样。 Firefox窗口处于活动状态。

好吧,花点时间考虑一下。 处理器在进程之间不断旋转时间轴。 当然,需要从Stdin读取的每个过程都是从StdIn读取的。 “默认” Stdin资源是键盘,只要该进程尚未打开另一个句柄,并通过其句柄指定了另一个资源,所有读取都将假定来自键盘。 但是目前Stdin与 “活跃” 应用程序窗口过程相关。 因此,只有 “活动” 进程实际上接收在键盘上输入的键,因为键盘直接写入 “活动” 进程的特定缓冲区。 嗯... 怎么样?“关于” 其他进程窗口?他们是否需要知道我在Firefox网页上的文本框元素中键入的内容?没有。 当我切换到 “他们的” 进程,并且 “他们” 成为活动进程时,然后当他们从Stdin读取时,他们也将接收键盘的输入。

有道理,对吧?

这是该理论一小部分的基本内容。以后的添加应该添加更多的正文内容,甚至实现细节。