实验在Linux上,x86 32位。
因此,假设在我的汇编程序中,我需要定期(例如每次执行100000个基本块后)将.bss部分中的数组从内存转储到磁盘。数组的起始地址和大小是固定的。数组记录执行的基本块的地址,现在大小为16M。
我尝试将一些本机代码从.bss部分写入memcpy
到堆栈,然后将其写回磁盘。但在我看来,这是非常乏味的,我担心性能和内存消耗,比如,每次在堆栈上分配一个非常大的内存......
所以这是我的问题,如何以有效的方式从全局数据部分转储内存?我清楚了吗?
答案 0 :(得分:2)
首先,不要在asm中写这部分代码,尤其是不是一开始。编写一个C函数来处理这个部分,并从asm调用它。如果您需要对仅在转储另一个16MiB时运行的部件进行全局调整,则可以手动调整它。系统级编程就是检查系统调用(或C stdio函数)的错误返回,而在asm中执行此操作会很痛苦。
显然你可以在asm中写任何东西,因为与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(2)
之前需要锁定。在这种情况下会有一点偏差(缓冲区末尾的数据将比起始时的数据稍晚一些,假设内核按顺序复制)。
IDK,如果write(2)
使用直接IO(零拷贝,绕过页面缓存)返回更慢或更快。 open(2)
您的文件通常包含O_DIRECT
,write(2)
。
如果要编写缓冲区的快照然后继续修改,那么在此过程中必须有一个副本。或者MMU拷贝写入技巧:
有一个API可以将用户页面的零拷贝写入磁盘文件。 Linux的vmsplice(2)
和splice(2)
将按顺序让您告诉内核将您的页面映射到页面缓存中。没有SPLICE_F_GIFT
,我认为它将它们设置为写时复制。 (哎呀,实际上man页面没有SPLICE_F_GIFT
,下面的splice(2)
必须复制。所以IDK是否有机制来获得写时复制语义。)
假设有一种方法可以为您的页面获取写时复制语义,直到内核完成将它们写入磁盘并可以释放它们:
进一步写入可能需要内核在数据命中磁盘之前记忆一两页,但保存复制整个缓冲区。无论如何,软页面错误和页面表操作开销可能都不值得,除非您的数据访问模式在短时间内非常空间本地化,直到写入命中磁盘并且可以释放要写入的页面。 (我认为以这种方式工作的API并不存在,因为在它们访问磁盘后没有立即释放页面的机制.Linux希望将它们接管并将它们保存在页面缓存中。)
我还没有使用过vmsplice,所以我可能会错误地提供一些细节。
如果有方法可以创建相同内存的新的写时复制映射,可以通过mmap
一个新的临时文件映射(在tmpfs文件系统上,prob。{{ 1}}),这样可以在不长时间保持锁定的情况下获得快照。然后,您可以将快照传递给/dev/shm
,并在发生太多写时复制页面错误之前尽快取消映射。
如果在每次写入后都可以使用归零缓冲区启动,那么您可以write(2)
连续的文件块,因此您生成的数据始终位于正确的位置。
mmap(2)
输出文件中的一些空格,以防止在您的写入模式不是顺序时出现碎片。fallocate(2)
您的缓冲区到输出文件的第一个16MiB。mmap(2)
你的缓冲区munmap(2)
文件的下一个16MiB到同一地址,因此您不需要将新地址传递给作者。根据POSIX的要求,这些页面将被预先置零(不能让内核暴露内存)。 mmap(2)
可能会取代mmap(buf, 16MiB, ... MAP_FIXED, fd, new_offset)
/ munmap
对。 mmap
会丢弃它重叠的旧MAP_FIXED
。我认为这并不意味着对文件/共享内存的修改被丢弃,而是实际的映射会发生变化,即使没有mmap
。
答案 1 :(得分:1)
对Peter的回答中的附加快照案例进行了两次澄清。
<强> 1。不加O_DIRECT
正如彼得所说,如果你不使用O_DIRECT
,write()
会在数据被复制到页面缓存后立即返回。如果页面缓存已满,它将阻塞,直到某些过时的页面刷新到磁盘。
如果您只是在不读取数据的情况下附加数据(很快),您可以定期调用sync_file_range(2)
为先前写入的页面安排刷新,并使用POSIX_FADV_DONTNEED
标记posix_fadvise(2)
来删除从页面缓存中刷新页面。这可能会显着降低write()
阻止的可能性。
<强> 2。附加O_DIRECT
使用O_DIRECT
时,write()
通常会阻止数据发送到磁盘(尽管没有严格保证,请参阅here)。由于速度很慢,如果您需要非阻塞写入,请准备好实现自己的I / O调度。
您可以归档的好处是:更可预测的行为(您可以控制何时阻止),并且可能通过应用程序和内核的协作减少内存和CPU使用。