我很难理解这一点。
我的测试设置有一个 shell 脚本,它会不断对一个 1G 文件调用“ls -la”,并打印出自上次运行以来的时间。然后,我运行一个程序来修改文件的部分内容并将其同步到磁盘。
无论我是否调用 fsync,或者系统是否执行同步,或者即使我使用 pwrite 来写入不同的部分(仍在测试该位),当同步发生时,“ls -la”都会在整个同步时间内冻结 - 大约 7-40 秒(取决于修改的稀疏性)。
如果我使用 msync 一次同步块,或者在写入时尝试更频繁地 fsync,则持续时间会变得更长(可能是 10 倍长,但甚至更长,具体取决于我执行的频率)。上面的 msync 仅以 16KB/Transaction 的速度写入,即使页面是连续的。
我曾在某处读到过 OpenBSD 实现了“部分文件写入”之类的功能。我现在记不太清楚了。
有什么办法可以做一些类似的事情,提高 fsync 的效率,而文件又不被一直锁定?
实际上,问题“A”(我认为“B”是解决方案)就是简单地处理大文件并“鼓励”它们写入磁盘,以便在需要时快速释放内存。简单地省略 NO_SYNC 是没有用的,因为更改将同时发生,从而导致这种情况。其他 madvise 选项似乎也无济于事。也就是说,如果我不同步,那么页面似乎会一直存在,直到内存不足,它们会突然开始交换(尽管只有 16KB/Transaction 和非常低的 MB/s)。
你究竟如何在 FreeBSD 上处理大文件?
解决方案:
我发现,通过调整我的 msync 块并在 msync 调用中使用 MS_ASYNC 而不是 MS_SYNC,我可以获得我想要的性能,同时仍然允许其他进程打开和 mmap/读取文件。
答案1
freeBSD 将使用空闲内存来缓存磁盘 I/O,其他 UNIX 也是如此。在空闲内存很多而用户很少的系统上,真正大的文件可以完全驻留在内存中。这样看起来就使用了更多内存。
close()
( fclose()
) 和fsync
(fflush() )
是唯一强制操作系统写入缓存的系统调用。仅当没有其他进程打开该文件时,这才是正确的。freeBSD 没有这个功能,fdatasync
它只写入缓存数据,而不是将元数据写入物理磁盘。
从 BSD 4.4 开始,您可以使用系统调用跟踪分页和文件缓存mincore()
。
因此,每写入几次后就必须执行一次 fflush 操作。
使用磁盘缓存参数:
http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/configtuning-disk.html
答案2
你解决问题(监控文件状态)的方法完全错误。你的程序不应该定期重新检查文件状态(并且偶尔会遇到 I/O 并发问题),而应该在特定文件(或文件集合)发生更改时通知内核。
所有现代 Unix 上都存在实现此目的的机制,但不幸的是,它们并不相同......
在 BSD 系列 Unix 上,这是通过以下方式实现的kqueue/kevent。在 Linux 上有 inotify。在 Solaris 上有 poll 和 /dev/poll。
有跨平台库,它们隐藏了操作系统实现细节并为您提供可移植的 API。如果您需要可移植性,请查找文件更改监视器或其更现代的子集,称为野孩子(移植到 /usr/ports/devel/gamin)。如果您的应用程序仅适用于 (Free)BSD,您可以直接使用 kqueue/kevent。
答案3
http://www.unix.com/man-page/FreeBSD/4/syncer/
清楚地解释了您的问题。同步器会定期将脏缓冲区(更新的缓存)刷新到磁盘。这些“定期”刷新正是您要避免的。看看 sysctl 可以为您的问题做些什么。