首先,不要在 asm 中编写这部分代码,尤其是。一开始不是。编写一个 C 函数来处理这部分,并从 asm.xml 中调用它。如果您需要对仅在需要转储另一个 16MiB 时才运行的部分进行性能调整,那么您可以手动调整它。系统级编程就是检查来自系统调用(或 C stdio 函数)的错误返回,而在 asm 中这样做会很痛苦。
显然,您可以在 asm 中编写任何内容,因为与 C 相比,进行系统调用并没有什么特别之处。与 C 相比,在 asm 中没有比 C 更容易的任何部分,除了可能会在MFENCE
锁定周围抛出一个。
无论如何,我已经解决了您希望缓冲区发生的三个变化:
- 原地覆盖相同的缓冲区 (
mmap(2)
/ msync(2)
)
- 将缓冲区的快照附加到文件(使用
write(2)
或可能不工作的零拷贝vmsplice(2)
+splice(2)
想法。)
- 在写入旧缓冲区后启动一个新的(归零)缓冲区。
mmap(2)
输出文件的顺序块。
就地覆盖
如果您只想每次都覆盖磁盘的同一区域,mmap(2)
请使用一个文件并将其用作您的阵列。(定期调用msync(2)
以强制将数据写入磁盘。)但是,mmapped 方法不能保证文件的一致状态。写入可以根据请求刷新到磁盘。IDK,如果有办法通过任何形式的保证来避免这种情况(即不仅仅是选择缓冲区刷新计时器等等,所以你的页面通常不会被写入,除非msync(2)
.)
附加快照
将缓冲区附加到文件的简单方法是在您希望将其写入时简单地调用write(2)
。 write(2)
做你需要的一切。如果您的程序是多线程的,您可能需要在系统调用之前对数据进行锁定,然后再释放锁定。我不确定 write 系统调用返回的速度有多快。它可能只有在内核将您的数据复制到页面缓存后才会返回。
如果你只需要一个快照,但所有写入缓冲区的都是原子事务(即缓冲区始终处于一致状态,而不是需要相互一致的值对),那么你不需要采取调用前的锁write(2)
。在这种情况下会有少量的偏差(假设内核按顺序复制,缓冲区末尾的数据将比开始时的数据稍晚)。
IDK 如果write(2)
使用直接 IO(零拷贝,绕过页面缓存)返回更慢或更快。 您的文件通常open(2)
带有。O_DIRECT
write(2)
如果要写入缓冲区的快照然后继续修改它,则必须在过程中的某处有一个副本。或者 MMU 写时复制诡计:
零拷贝追加快照
有一个 API 用于将用户页面零拷贝写入磁盘文件。Linux 的vmsplice(2)
顺序splice(2)
将让您告诉内核将您的页面映射到页面缓存中。如果没有SPLICE_F_GIFT
,我假设它将它们设置为写时复制。(哎呀,实际上手册页说没有SPLICE_F_GIFT
,以下splice(2)
内容必须复制。所以如果有一种机制可以获取写时复制语义的机制。)
假设有一种方法可以为您的页面获取写时复制语义,直到内核完成将它们写入磁盘并可以释放它们:
进一步的写入可能需要内核在数据到达磁盘之前 memcpy 一两页,但保存复制整个缓冲区。无论如何,软页面错误和页表操作开销可能不值得,除非您的数据访问模式在短时间内非常空间本地化,直到写入命中磁盘并且可以释放要写入的页面。(我认为不存在以这种方式工作的 API,因为没有机制可以在页面到达磁盘后立即释放它们。Linux 想要接管它们并将它们保存在页面缓存中。)
我从来没有使用过 vmsplice,所以我可能弄错了一些细节。
如果有一种方法可以为同一内存创建一个新的写时复制映射,可能是mmap
通过创建一个临时文件的新映射(在 tmpfs 文件系统上,/dev/shm
可能。 . 然后,您可以将快照传递给write(2)
,并在发生太多写时复制页面错误之前尽快取消映射。
每个块的新缓冲区
如果可以在每次写入后从零缓冲区开始,您可以mmap(2)
连续的文件块,因此您生成的数据总是已经在正确的位置。
- (可选)
fallocate(2)
输出文件中的一些空间,以防止在您的写入模式不是顺序的情况下出现碎片。
mmap(2)
您的缓冲区到输出文件的前 16MiB。
- 正常运行
- 当您想继续下一个 16MiB 时:
- take a lock to prevent other threads from using the buffer
munmap(2)
your buffer
mmap(2)
the next 16MiB of the file to the same address, so you don't need to pass the new address around to writers. These pages will be pre-zeroed, as required by POSIX (can't have the kernel exposing memory).
- release the lock
Possibly mmap(buf, 16MiB, ... MAP_FIXED, fd, new_offset)
could replace the munmap
/ mmap
pair. MAP_FIXED
discards old mmap
ings that it overlaps. I assume this doesn't mean that modifications to the file / shared memory are discarded, but rather that the actual mapping changes, even without an munmap
.