缓冲区的类型

mou*_*sey 4 c operating-system

最近一位采访者向我询问了缓冲液的类型.有哪些类型的缓冲区?实际上,当我说我将所有系统调用写入日志文件以监视系统时,出现了这个问题.他说,每次调用文件都会很慢.如何防止它.我说我会用缓冲区,他问我什么类型的缓冲区?有人可以解释我的缓冲类型.

pax*_*blo 6

在UNIX下的C(以及可能还有其他操作系统)中,通常有两个缓冲区,至少在您的给定方案中.

第一个存在于C运行时库中,其中要写入的信息在传递到OS之前被缓冲.

第二种是在操作系统本身,其中信息被缓冲,直到它可以物理地写入底层媒体.

作为一个例子,我们在许多月前编写了一个日志库,强制将信息写入磁盘,以便在程序崩溃或操作系统崩溃时它就会存在.

这是通过以下顺序实现的:

fflush (fh); fsync (fileno (fh));
Run Code Online (Sandbox Code Playgroud)

第一个实际确保信息从C运行时缓冲区传递到操作系统,第二个是写入磁盘.请记住,这是一项昂贵的操作,只有在您绝对需要立即写入的信息时才应该这样做(我们只在SUPER_ENORMOUS_IMPORTANT日志级别执行此操作).

说实话,我不完全确定为什么你的面试官认为除非你写了很多信息,否则会很慢.已经存在的两个级别的缓冲应该非常充分.如果这一个问题,那么你可以自己引入另一个层,它将消息写入内存缓冲区,然后fprint在它即将溢出时将其传递给单一类型的调用.

但是,除非你没有任何函数调用,否则我看不到它比fprint-type缓冲已经给你的快得多.


在评论中澄清这个问题实际上是关于内核中的缓冲:

基本上,您希望尽可能快速,高效和可行(不容易出现故障或资源短缺).

可能最好的选择是缓冲区,无论是静态分配还是在引导时动态分配一次(您希望避免动态重新分配失败的可能性).

其他人提出了一个环(或循环)缓冲区,但我不会那样(技术上)出于以下原因:使用经典的循环缓冲区意味着在数据卷绕时写出数据将需要两次独立写入.例如,如果您的缓冲区具有:

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|s|t|r|i|n|g| |t|o| |w|r|i|t|e|.| | | | | | |T|h|i|s| |i|s| |t|h|e| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                                 ^           ^
                                 |           |
                   Buffer next --+           +-- Buffer start
Run Code Online (Sandbox Code Playgroud)

然后你必须写,"This is the "然后"string to write.".

相反,维护next指针,如果缓冲区中的字节加上要添加的字节小于缓冲区大小,只需将它们添加到缓冲区而不对底层媒体进行物理写入.

只有当你要溢出缓冲区时才开始做狡猾的事情.

您可以采用以下两种方法之一:

  • 按原样刷新缓冲区,将next指针设置回开始处理新消息; 要么
  • 添加部分消息以填充缓冲区,然后刷新它并将next指针设置回开始以处理消息的其余部分.

我可能会选择第二个,因为你将不得不考虑对缓冲区来说太大的消息.

我所说的是这样的:

initBuffer:
    create buffer of size 10240 bytes.
    set bufferEnd to end of buffer + 1
    set bufferPointer to start of buffer
    return

addToBuffer (size, message):
    while size != 0:
        xfersz = minimum (size, bufferEnd - bufferPointer)
        copy xfersz bytes from message to bufferPointer
        message = message + xfersz
        bufferPointer = bufferPointer + xfersz
        size = size - xfersz
        if bufferPointer == bufferEnd:
            write buffer to underlying media
            set bufferPointer to start of buffer
        endif
    endwhile
Run Code Online (Sandbox Code Playgroud)

这基本上通过减少物理写入的数量来有效地处理任何大小的消息.当然会有优化 - 消息可能已被复制到内核空间中,因此如果你要编写它,将它复制到缓冲区是没有意义的.您也可以将内核副本中的信息直接写入底层媒体,并将最后一位传输到缓冲区(因为您必须保存它).

此外,如果一段时间没有写入任何内容,您可能希望将不完整的缓冲区刷新到底层媒体.这样可以减少丢失内核本身崩溃的可能性信息的可能性.

旁白:从技术上讲,我猜这一种循环缓冲区,但它具有特殊的案例处理功能,可以最大限度地减少写入次数,并且不需要尾部指针,因为这种优化.