2015-01-16 38 views
1

我非常难以理解缓冲的深度,特别是在C编程中,我已经在这个主题上搜索了很长时间,但至今没有找到令人满意的东西。我会明白其背后的概念(即协调不同硬件设备的操作,并尽量减少这些设备的速度差异),但我希望对这些设备的更全面的解释和其他潜在的缓冲原因(并且我认为完整越长越好),给出一些如何在I/O流中实现缓冲的具体示例也是非常好的。了解C中的缓冲

的其他问题将是我注意到,在缓冲区刷新一些规则不跟我的方案,作为古怪,因为这听起来像下面这个简单的片断:

#include <stdio.h> 

int main(void) 
{ 
    FILE * fp = fopen("hallo.txt", "w"); 

    fputc('A', fp); 
    getchar(); 
    fputc('A', fp); 
    getchar(); 

    return 0; 
} 

该计划旨在证明当即将调用第一个getchar()时,即将发生的输入将立即刷新任意流,但这不会像我尝试它那样频繁发生,并且只要我想要—就不会发生如stdout(与printf()例如)流被刷新,没有任何输入请求也否定规则,因此我理解这个规则错误或我有什么其他的考虑

我在Windows 8.1上使用Gnu GCC。

更新:

我忘了问我一些网站上阅读别人如何访问例如字符串文字作为缓冲区或甚至数组作为缓冲区;这是正确的还是我错过了什么? 请解释这一点。

+2

至于示例,您并未写入'stdout',而是写入任意文件。 – Aneri

+0

对不起,我改变了它 – Lockon2000

+0

对于你最后一个问题(来自更新);是的,数组(尤其是字符)被称为缓冲区,但这与I/O缓冲区不同,即使I/O缓冲区通常使用缓冲区来保存数据,直到它被写入或读取。 –

回答

6

单词缓冲区用于计算机科学中的许多不同的东西。从更一般的意义上讲,它是任何一段内存,其中数据暂时存储直到它被处理或复制到最终目的地(或其他缓冲区)为止。

正如在问题暗示有许多类型的缓冲液,但作为一个广泛的分组:

  1. 硬件缓冲器:这些是其中数据被移动到HW设备之前存储的缓冲器。或从硬件设备接收数据直至由应用程序处理数据的缓冲区。这是必需的,因为I/O操作通常具有内存和时序要求,并且这些由缓冲器来满足。考虑直接读写内存的DMA设备,如果内存设置不正确,系统可能会崩溃。或者声音设备必须具有亚微秒精度,否则将效果不佳。

  2. 缓存缓冲区:这些缓冲区是在写入/读取文件/设备之前对数据进行分组以便性能得到普遍提高的缓冲区。

  3. 辅助缓冲区:将数据移入/移出缓冲区,因为它对您的算法更简单。

案例#2是您的FILE*示例。想象一下,调用编写系统调用(Win32中的WriteFile())只需要1ms的通话加上每个字节1us(与我一起,现实世界中的情况更加复杂)。然后,如果你这样做:

FILE *f = fopen("file.txt", "w"); 
for (int i=0; i < 1000000; ++i) 
    fputc('x', f); 
fclose(f); 

如果没有缓冲,这个代码将采取1000000 * (1ms + 1us),这是大约1000秒。但是,使用10000字节的缓冲区时,将只有100个系统调用,每个都有10000个字节。那将是100 * (1ms + 10000us)。那只是0.1秒!

另请注意,操作系统将自行缓冲,以便使用最有效的大小将数据写入实际设备。这将是一个HW和高速缓存缓冲区在同一时间!

关于你的冲洗问题,文件通常在关闭或手动刷新时被刷新。某些文件(例如stdout)是行刷新的,也就是说,只要写入'\n'就会刷新它们。 stdin/stdout也是特殊的:当您从stdin读取stdout时,将被刷新。其他文件不变,只有stdout。如果你正在编写一个交互式程序,这很方便。

我的情况#3是例如当你这样做:

FILE *f = open("x.txt", "r"); 
char buffer[1000]; 
fgets(buffer, sizeof(buffer), f); 
int n; 
sscanf(buffer, "%d", &n); 

您使用缓冲保存从文件中的一行,然后你解析从行的数据。是的,您可以直接拨打fscanf(),但在其他API中可能没有相同的功能,而且您可以通过以下方式进行更多控制:您可以分析线路,跳过评论,计数线路的类型...

或想象你一次只能收到一个字节,例如从键盘上收到一个字节。您只需在缓冲区中累积字符并在按下Enter键时解析该行。这是大多数交互式控制台程序所做的。

+0

感谢您的回答是非常有帮助的 – Lockon2000

+0

我想先验证一些要点是较慢的设备总是目标设备我的意思是例如在写入文件时,显然硬盘比处理器和RAM但在接收输入时,硬盘上的文件速度较慢,难以写入缓冲区,或者处理器处理接收到的数据更加清晰的是数据非常快速地积累,然后按照步调或其他方式 - 我希望我的问题已经足够清楚 – Lockon2000

+0

@ Lockon2000:这取决于你对数据做了什么。例如,如果您想将刚刚开采的比特币写入文件,那么CPU将比HD更慢。在这种情况下,您在写入之前不会缓存几个BT,您将立即写入&flush。当然,在保存BT之前,你仍然有缓冲区。读者一样:如果你的CPU丢弃了数据,高清速度会变慢,但是如果你的确在计算,那么CPU就会变慢。它将工作,因为任何理智的实现将限制未决缓冲区的最大大小并阻止最快的进程。 – rodrigo

1

第一个问题有点太宽泛。在许多情况下使用缓冲,包括实际使用前的消息存储,DMA使用,加速使用等。总之,整个缓冲事件可以概括为“保存我的数据,让我继续执行,而您对数据做了些什么”。

有时您可能会在将缓冲区传递给函数后修改缓冲区,有时候可能不会。有时缓冲区是硬件,有时候是软件。有时他们驻留在RAM中,有时候驻留在其他存储器类型中。

所以,请问更具体的问题。作为一个开始点,使用维基百科,它几乎总是有帮助的:wiki

至于代码示例,我没有发现任何提及的所有输出缓冲区正在刷新getchar。对于文件的缓冲区一般冲洗三种情况:

  1. fflush()或同等
  2. 文件被关闭
  3. 缓冲区溢出是。

由于这两种情况都不是你真正的文件不会被清理(注意,申请终止在这个列表)。

+0

http://www.gnu.org/software/libc/manual/html_node/Flushing-Buffers.html#Flushing-Buffers 最后一条规则非常清楚地指出,任何流上的任何输入都应该至少冲洗流,如果我我正确理解它 – Lockon2000

1

名词“缓冲区”实际上指的是一种用法,而不是一个明显的东西。任何存储块都可以作为缓冲区。尽管C I/O流函数的文档倾向于避免这种情况,但是该术语有意地与各种I/O函数结合使用。取POSIX read()功能作为一个例子,但是:“读()尝试读取多达计数从文件描述符FD字节到缓冲区开始于BUF”。在这种情况下,“缓冲区”仅仅意味着将读取字节的内存块;它通常作为char[]或动态分配的块来实现。

因为某些设备(尤其是硬盘)在大中型块中的读取效率最高,其中程序通常希望以较小的块数来使用该数据,所以尤其与I/O配合使用。一些其他形式的I/O,例如网络I/O,可能固有地以块形式出现,因此您必须记录每个整块(在缓冲区中),否则就会丢失您不立即准备使用的部分。类似的考虑适用于产出。

至于测试程序的行为,您希望演示的“规则”特定于控制台I/O,但只涉及其中一个流连接到控制台。