用户空间缓冲区和内核空间缓冲区

8

我最近在学习Linux文件系统。我了解到,当我们调用fopen()函数时,库函数会调用malloc()函数为FILE结构体分配空间,在这个FILE结构体中会有一个I/O缓冲区。但后来我发现,写入系统调用实际上是将数据写入内核缓冲区,那么这两个缓冲区有什么区别呢?


不要介意,你需要先有一本书来阅读。 - Sourav Ghosh
2
我有一本书,叫做《高级UNIX编程》。只是想把事情搞清楚。 - Louis Kuang
4个回答

15
你必须理解两件事情:fwrite()是一个操作FILE结构的标准库例程,而write()是一个系统调用。我打赌fwrite()在内部使用write()。没有什么能阻止fwrite()提供用户空间IO缓冲,直到它准备好将你的数据传递给write()系统调用。 write()系统调用直接进入内核并说:“嘿,内核,我有一个用户空间缓冲区在这里。你能写入存储器吗?” 对于内核来说,接下来该做什么取决于内核:它要么直接访问存储器以写入数据,要么(最有可能的情况)将数据复制到内核缓冲区,直到它决定修改存储器数据为止。
回到你的问题。为了推迟转向更昂贵的操作,进行任何类型的缓冲都是为了累积数据:标准库可能会考虑在每个len字节时调用系统调用,内核则认为在每次系统调用时访问硬盘是昂贵的等等。
你可能想阅读这篇文章来了解缓冲到达的程度:https://fgiesen.wordpress.com/2015/10/25/reading-and-writing-are-less-symmetric-than-you-probably-think/

库函数是有自己的缓冲区还是缓冲区在文件结构中? - Louis Kuang
1
我猜它是在FILE结构体中。注意,库缓冲IO是基于每个文件的。为了执行其每个文件的工作,它会提供一些不透明的FILE *文件句柄给应用程序,这样当您调用库函数时,需要将该句柄传入。此句柄向例程指示了执行任务所需知道的一切(包括缓冲)。 - Igor S.K.

1

FILE结构保存了已打开文件的元数据(模式、流位置等)。它是C标准I/O接口的一部分。 作为FILE的一部分分配的缓冲区仅包含有限量的数据(例如,当流被缓冲时)。它在fclose()时被释放。您甚至可以使用setvbuf()提供自己的用户空间stdio缓冲区。

内核缓冲区接收由write()写入的文件内容,每当流被刷新或相关联的文件描述符关闭时。


1
这是否意味着,如果流被缓冲,数据首先被写入用户空间缓冲区,然后再写入内核空间缓冲区?但是,如果流没有被缓冲,例如stderr,这是否意味着输出直接进入错误消息,甚至不会存储在内核缓存中? - Louis Kuang
@lplouis 如果流被缓冲,它首先进入FILE缓冲区。这就是整个目的:避免仅为少量字节而进行昂贵的系统调用。如果未缓冲,则任何操作都直接进入read()或write()。后者通常用于诊断消息:您希望尽快得到有关出现问题的信息。 - Jens

0

两个缓冲区之间有很大的区别,一个是内核缓冲区,另一个是用户缓冲区。因此,当进行I/O操作时,基本上是将用户空间中的缓冲区复制到内核空间中的缓冲区。函数copy_from_user()执行此任务。

现在出现的问题是,既然内核可以访问用户空间,为什么我们需要两个缓冲区呢?原因是内核不想直接读取用户缓冲区,因为内核和用户空间具有不同的地址空间,因此用户空间中的有效地址可能不是内核中的有效地址。

如果在内核中访问了非有效地址,则系统会立即崩溃,因此函数copy_from_user执行映射用户空间地址和内核空间地址的任务,并检查地址是否可访问。如果不可访问,则简单地返回EFAULT(错误地址)。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接