我有以下关于C文件I/O的问题。在物理层面(硬盘),假设每一次fread(n_blocks, size, length,FILE fp)操作都要对第一页(块)进行一次随机访问,对同一缓冲区的后续块进行n-1次顺序访问,这是否有效?我假设这是因为操作系统有这么多的进程,最肯定的是,其中一个也是写入或阅读本地程序的每个fread之间的文件,并通过该假设硬盘驱动器位于另一个扇区/柱面。这个假设可以吗?
fread(n_blocks, size, length,FILE fp)
fread
6fe3ivhb1#
不管你是否这样认为,这都是对现实的过度简化。第一件事:你似乎认为第三个参数(length)对应于一些离散的“访问操作”的数目。事实并非如此。fread所做的只是读取size*length字节;因此,只要乘法运算不溢出,以下三个调用的作用完全相同:
length
size*length
fread(n_blocks, size, length, fp); fread(n_blocks, size*length, 1, fp); fread(n_blocks, 1, size*length, fp);
实际发生的是,fread/fwrite将从 * 您的进程 * 内存中的一个内部缓冲区中读取和写入数据。该缓冲区可以通过setbuf/setvbuf函数控制。当缓冲区满/空时,它们将把读取/写入数据转发给 * 操作系统 *,它有自己的文件缓存。如果你正在阅读而操作系统该高速缓存中找不到文件的那一部分,那么你的程序将等待直到数据从驱动器中被真正地提取出来。当写入时,该数据将被复制到操作系统缓存中,并一直驻留在那里,直到操作系统决定将其写入硬盘,这可能发生在程序关闭文件并存在很长时间之后。反过来,今天的硬盘驱动器有自己的内部缓存,操作系统甚至可能不知道。
fread/fwrite
setbuf/setvbuf
实际上,您不应该关心每个fread/fwrite调用执行多少次驱动器访问。只需知道C、操作系统和底层硬件会尽最大努力尽快提供所请求的数据。但是,请记住,整个堆栈 * 都 * 针对顺序访问进行了优化。因此,请避免在没有充分理由的情况下使用fseek在文件周围跳转。
fseek
bwitn5fc2#
不,不是的。如果文件系统是fragmented,单个文件的块可能分散在硬盘上。
5sxhfpxr3#
你甚至不能假设fread会触发物理I/O。你的操作系统有可能对I/O请求做很多事情,包括缓存结果、重新排序和合并(或拆分)读取(有时甚至是写入)。如果有大量的I/O正在进行,你也不能指望获得顺序读取,这取决于你(可能还有I/O流库)使用的缓冲区大小。一些操作系统提供了一些方法来“提示”你将在文件描述符(或mmap ed区域)上顺序阅读,这可能会有所帮助。
mmap
qjp7pelc4#
从应用程序员的Angular 来看,阅读块的确切过程是不确定的。这一切都要归结到磁盘调度程序,该程序组织来自多个进程的多个请求同时进行的访问操作。There are multiple algorithms to solve this issue,但想法过于简单化(1个随机寻道,n个顺序寻道)根本不现实。最后,C标准和C++标准都没有明确的原因定义这样的事情。
ckx4rj1h5#
正如许多人所解释的,必须考虑缓存(可能在几个级别上)。也许你想知道如何从你的C代码中加速或调优它。这是高度特定于操作系统的。在最新的Linux系统上,您可以使用readahead、madvise(带有mmap)和其他系统调用。通常,您可以简单地提前read一个文件(也许只是用cat yourfile > /dev/null),这样您的程序在Linux上运行得更快。例如,尝试对某个大文件运行两次wc字数统计工具,第二次运行通常比第一次快得多。
cat yourfile > /dev/null
wc
5条答案
按热度按时间6fe3ivhb1#
不管你是否这样认为,这都是对现实的过度简化。
第一件事:你似乎认为第三个参数(
length
)对应于一些离散的“访问操作”的数目。事实并非如此。fread
所做的只是读取size*length
字节;因此,只要乘法运算不溢出,以下三个调用的作用完全相同:实际发生的是,
fread/fwrite
将从 * 您的进程 * 内存中的一个内部缓冲区中读取和写入数据。该缓冲区可以通过setbuf/setvbuf
函数控制。当缓冲区满/空时,它们将把读取/写入数据转发给 * 操作系统 *,它有自己的文件缓存。如果你正在阅读而操作系统该高速缓存中找不到文件的那一部分,那么你的程序将等待直到数据从驱动器中被真正地提取出来。当写入时,该数据将被复制到操作系统缓存中,并一直驻留在那里,直到操作系统决定将其写入硬盘,这可能发生在程序关闭文件并存在很长时间之后。反过来,今天的硬盘驱动器有自己的内部缓存,操作系统甚至可能不知道。实际上,您不应该关心每个
fread/fwrite
调用执行多少次驱动器访问。只需知道C、操作系统和底层硬件会尽最大努力尽快提供所请求的数据。但是,请记住,整个堆栈 * 都 * 针对顺序访问进行了优化。因此,请避免在没有充分理由的情况下使用fseek
在文件周围跳转。bwitn5fc2#
不,不是的。如果文件系统是fragmented,单个文件的块可能分散在硬盘上。
5sxhfpxr3#
你甚至不能假设
fread
会触发物理I/O。你的操作系统有可能对I/O请求做很多事情,包括缓存结果、重新排序和合并(或拆分)读取(有时甚至是写入)。如果有大量的I/O正在进行,你也不能指望获得顺序读取,这取决于你(可能还有I/O流库)使用的缓冲区大小。一些操作系统提供了一些方法来“提示”你将在文件描述符(或
mmap
ed区域)上顺序阅读,这可能会有所帮助。qjp7pelc4#
从应用程序员的Angular 来看,阅读块的确切过程是不确定的。这一切都要归结到磁盘调度程序,该程序组织来自多个进程的多个请求同时进行的访问操作。There are multiple algorithms to solve this issue,但想法过于简单化(1个随机寻道,n个顺序寻道)根本不现实。最后,C标准和C++标准都没有明确的原因定义这样的事情。
ckx4rj1h5#
正如许多人所解释的,必须考虑缓存(可能在几个级别上)。
也许你想知道如何从你的C代码中加速或调优它。这是高度特定于操作系统的。
在最新的Linux系统上,您可以使用readahead、madvise(带有mmap)和其他系统调用。
通常,您可以简单地提前read一个文件(也许只是用
cat yourfile > /dev/null
),这样您的程序在Linux上运行得更快。例如,尝试对某个大文件运行两次
wc
字数统计工具,第二次运行通常比第一次快得多。