fseek() 是如何在文件系统中实现的?

Posted

技术标签:

【中文标题】fseek() 是如何在文件系统中实现的?【英文标题】:How is fseek() implemented in the filesystem? 【发布时间】:2011-01-27 04:37:48 【问题描述】:

这不是一个纯粹的编程问题,但是它会影响使用 fseek() 的程序的性能,因此了解它的工作原理很重要。一点免责声明,以免它被关闭。

我想知道在文件中间插入数据的效率如何。假设我有一个包含 1MB 数据的文件,然后我在 512KB 偏移处插入了一些东西。与在文件末尾附加我的数据相比,效率如何?为了使示例完整,假设我要插入 16KB 的数据。

我知道答案因文件系统而异,但我认为常见文件系统中使用的技术非常相似,我只是想了解它的正确概念。

【问题讨论】:

使用 fseek() 等,您根本无法在文件中间插入数据,所以您的问题没有实际意义。 您不能简单地在文件中间插入数据(就像您不能从文件的开头或中间删除某些内容一样)。您能做的最好的事情就是覆盖文件中间的数据。 【参考方案1】:

(免责声明:我只想为这个有趣的讨论添加一些提示) 恕我直言,有一些事情需要考虑:

1) fseek 不是一个主要的系统服务,而是一个库函数。为了评估它的性能,我们必须考虑文件流库是如何实现的。一般来说,文件I/O库在用户空间增加了一层缓冲,所以如果目标位置在当前缓冲区之内或之外,fseek的性能可能会有很大的不同。此外,I/O 库使用的系统服务可能会有很大差异。 IE。在某些系统上,如果可能,该库会广泛使用文件内存映射。

2) 正如您所说,不同的文件系统可能以非常不同的方式运行。特别是,我希望事务文件系统必须做一些非常聪明且可能代价高昂的事情,以准备好在文件中间可能回滚中止的写入操作。

3) 现代操作系统具有非常激进的缓存算法。 “fseeked”文件可能已经存在于缓存中,因此操作变得更快。但如果其他进程产生的整体文件系统活动变得重要,它们可能会降级很多。

有没有cmets?

【讨论】:

当然,插入通常比追加更昂贵,因为要插入,至少还必须移动前面的内容,即追加到文件末尾!但 Pajton 问题中有趣的部分是关于 fseek 操作性能的。有什么意见吗?【参考方案2】:

让我们以 ext2 FS 和 Linux OS 为例。我认为插入和追加之间不会有显着的性能差异。在这两种情况下,必须读取文件节点和偏移表,将相关磁盘扇区映射到内存中,更新数据,并在稍后将数据写回磁盘。在此示例中,最大的性能差异是在访问文件的某些部分时具有良好的时间和空间局部性,因为这将减少加载/存储组合的数量。

正如之前的回答所说,如果您处理的数据写入恰好是 FS 块大小的倍数,您可能能够加快这两个操作,在这种情况下,您可以跳过加载阶段,只需将新块插入文件中inode 数据结构。这不切实际,因为您需要对 FS 驱动程序进行低级别访问,并且使用它会非常受限且不可移植。

【讨论】:

【参考方案3】:

fseek(...) 是库调用,而不是操作系统系统调用。运行时库负责处理对操作系统进行系统调用所涉及的实际开销,从技术上讲,fseek 是间接调用系统,但实际上并非如此(这在库调用和系统调用之间的区别)。 fseek(...) 是一个标准的输入输出函数,不管底层系统如何......但是......这是一个很大的然而......

操作系统很可能已经将文件缓存在其内核内存中,即磁盘上存储 1 和 0 的位置的直接偏移量,它是通过操作系统的内核层,超过很可能,内核中的最顶层将拥有文件组成的快照,即数据,无论它包含什么(它不关心任何一种方式,只要指向磁盘结构的“指针”磁盘上 lcoation 的偏移是有效的!)...

fseek(..)发生时,会有很多开销,间接地,内核委派了从磁盘读取的任务,根据文件的碎片程度,理论上可能是“遍地开花” ”,从用户角度来看,这可能是一个很大的开销,即 C 代码执行fseek(...),它可能分散在各处以将数据收集到一个“一个数据的连续视图”,然后插入到文件的中间(请记住,在这个阶段,内核必须将位置/偏移量调整到数据的实际磁盘盘片中)将被认为比附加到文件要慢文件结束。

原因很简单,内核“知道”最后一个偏移量是什么,只需擦除 EOF 标记并插入更多数据,在幕后,内核必须为磁盘分配另一块内存- 数据追加完成后,在 EOF 标记之后的磁盘位置调整偏移量的缓冲区。

【讨论】:

好的提示,数据分散可能是导致性能不佳的重要原因。谢谢!【参考方案4】:

我对 Solaris 上的 fseek 的一个观察是,每次调用它都会重置 FILE 的读取缓冲区。下一次读取将始终读取一个完整的块(默认为 8K)。因此,如果您有大量随机访问和少量读取,那么最好不要缓冲(setvbufNULL 缓冲区),甚至使用直接系统调用(lseek+read 甚至更好的pread只有 1 个系统调用而不是 2 个)。我想这种行为在其他操作系统上会类似。

【讨论】:

【参考方案5】:

只有当数据大小是FS扇区的倍数时才能有效地将数据插入文件中间,但操作系统不提供此类功能,因此您必须使用FS驱动程序的低级接口。

【讨论】:

【参考方案6】:

在文件中间插入数据比追加到末尾效率低,因为插入时您必须将数据移动到插入点之后,以便为插入的数据腾出空间。移动这些数据将涉及从磁盘读取它们,写入要插入的数据,然后在插入数据之后写入旧数据。所以你在插入时至少有一个额外的读写操作。

【讨论】:

以上是关于fseek() 是如何在文件系统中实现的?的主要内容,如果未能解决你的问题,请参考以下文章

ini 在声明性Nginx配置中实现的原始Double A(AAA-minus-Accounting)RBAC系统

Hyper-V Server 2008 和 在Windows Server 2008 中实现的Hyper-V有什么区别?

当我调用 fseek() 时,在低级别会发生啥?

php使用file函数fseek函数读取大文件效率分析

如何从封闭的外部类调用与匿名类中实现的方法同名的方法?

Android 文件系统中添加文件及添加系统属性