Unix 中行缓冲的两个注意事项是什么?

Unix 中行缓冲的两个注意事项是什么?

APUE 第 5.4 章中关于行缓冲区的一段话:

  1. 行缓冲。在这种情况下,标准 I/O 库在输入或输出中遇到换行符时执行 I/O。这允许我们一次输出一个字符(使用标准 I/O fputc 函数),知道只有当我们写完每一行时才会发生实际的 I/O。当流引用终端(例如标准输入和标准输出)时,通常在流上使用行缓冲。行缓冲有两个注意事项。首先,标准 I/O 库用于收集每一行的缓冲区大小是固定的,因此如果我们在写入换行符之前填充该缓冲区,则可能会发生 I/O。其次,每当通过标准 I/O 库从 (a) 无缓冲流或 (b) 行缓冲流(需要从内核请求数据)请求输入时,所有行缓冲输出流都会被刷新。 (b) 上限定符的原因是请求的数据可能已经在缓冲区中,这不需要从内核读取数据。显然,来自无缓冲流的任何输入(项目(a))都需要从内核获取数据。

我不太明白这两个警告。有人可以举个例子吗?

答案1

  1. 基本上来说,例如,如果您的程序正在将文本写入终端,一次一个字符,速度非常慢,并且该行(例如)520 个字符长,那么库可能会将前 512 个字符写入终端在程序完成写入(甚至生成)该行之前。
  2. 表示如果您的程序正在向终端写入文本,一次一个字符(或其他一些小片段),然后从终端(即键盘)读取,则库将向终端写入以下部分:您的程序到目前为止已生成的输出行。这通常是您想要的,因为写入终端的部分行可能会提示输入,但有时可能不是。

答案2

在探讨两个警告之前,有三件事需要牢记。

无缓冲流调用系统调用每一个 read或者write。而行缓冲流会在每个线(每当缓冲区遇到换行符时)。

此外,stdinstdout都是标准 I/O 流,通常是行缓冲的。当进程启动时,这些流会自动打开。

最后,虽然 APUE 是学习 UNIX 的圣经,但有些部分已经过时了。我并不声称我的答案是最新的。我会尝试从书中举例,以便您的问题在教科书的背景下得到适当的回答。

第一个警告

正如您的报价所述,存在限制多久一条线就可以。超过限制会产生与换行相同的效果。

在开始介绍第一个注意事项的示例之前,我们必须记住write系统调用实际上是排队的(APUE 第 3 章第 6 节,p86)。

假设我们有一个名为 的程序,每 30 秒Jasmine写入一个字符。stdout

Jasmine写入“Hello!\n”(7 个字节),需要 210 秒才能完成。

请记住,缓冲区有一个限制。如果我们的缓冲区限制为 10 个字节,内核可以等待 210 秒并write在最后(遇到换行符后)发出单个调用。这是预期的行为。

然而,如果缓冲区限制为 4 个字节,内核将发行4 个字节后调用一次write(“Hell”),然后在换行符后调用另一个调用(“\n”)。

这是第一个警告。

用户可能期望write出现一次(终端上打印“Hello!\n”)。正如我们在示例中看到的那样,情况并非如此。根据系统的繁忙程度,用户可能会在 210 秒内看到两个输出打印到终端。这是因为即使stream( stdout)是行缓冲的,该行的内容也超出了限制(4字节限制)。每 4 个字节,标准 I/O 库就会调用一次,write就像遇到换行符一样。该示例总共有 7 个字节(“Hello!\n”),这导致了两次写入调用。

第二个警告

对于第二个警告,这里的关键点是相互作用kernelkernel根据我们与它的交互,它的行为会有所不同,从而导致行缓冲区的另一个“意外”写入。

这是您引用的第二个警告:

kernel将两次刷新所有行缓冲流:

  1. 当通过无缓冲标准 I/O 请求输入时。
  2. 当通过行缓冲标准 I/O 请求输入时。

我们可以将其改写如下:

kernel将刷新所有行缓冲流,满足以下先决条件:

  1. 请求输入kernel
  2. 该请求是通过无缓冲或行缓冲的标准 I/O 流进行的。

第一个要求是“必须向内核请求数据”。这允许行缓冲标准 I/O 出现异常。

例外的是kernel 将不会如果请求不需要来自 的任何数据,则刷新kernel。这可能是当行缓冲区已将所有数据存储在缓冲区中时的情况。

作为一个简单的例子,假设行缓冲流中的缓冲区具有“World”字符。用户(调用者)请求从流中读取 1 个字节。对于返回所请求的单字节(“W”)的流,它不需要 的任何帮助kernel。在这种情况下,kernel不会刷新任何行缓冲流。

考虑到这个例外,让我们继续第一个警告中的示例。

(我们应该将缓冲区限制保持为 10 个字节)

输出“Hello!\n”后,我们的程序Jasmine会写入“Name?”到stdout

“姓名?”没有换行符,且小于 10 个字节的限制。标准 I/O 库尚未发出调用write

然后Jasmine从(另一个行缓冲流)请求输入stdin,并发生以下情况:

  1. 流上的行缓冲区stdin是空的,因此它从 请求数据kernel
  2. kernel刷新所有打开的行缓冲流(其中包括“Name?” stdout)。
  3. 结果,“名字?”作为输出打印到终端。
  4. kernel等待用户在终端上的输入(键盘)。

这是第二个警告。

从用户的角度来看,看到“姓名?”终端上的信息可能会令人困惑,因为write尚未发布任何信息。不幸的是,在尝试获取请求的数据之前,kernel问题是否会write调用所有当前打开的行缓冲流。kernel这是因为行缓冲流和交互kernel已经满足了两个先决条件。

相关内容