在汇编代码中将全局数据转储到磁盘

时间:2015-07-16 15:23:39

标签: linux assembly x86 memory-dump

实验在Linux上,x86 32位。

因此,假设在我的汇编程序中,我需要定期(例如每次执行100000个基本块后)将.bss部分中的数组从内存转储到磁盘。数组的起始地址和大小是固定的。数组记录执行的基本块的地址,现在大小为16M。

我尝试将一些本机代码从.bss部分写入memcpy到堆栈,然后将其写回磁盘。但在我看来,这是非常乏味的,我担心性能和内存消耗,比如,每次在堆栈上分配一个非常大的内存......

所以这是我的问题,如何以有效的方式从全局数据部分转储内存?我清楚了吗?

2 个答案:

答案 0 :(得分:2)

首先,不要在asm中写这部分代码,尤其是不是一开始。编写一个C函数来处理这个部分,并从asm调用它。如果您需要对仅在转储另一个16MiB时运行的部件进行全局调整,则可以手动调整它。系统级编程就是检查系统调用(或C stdio函数)的错误返回,而在asm中执行此操作会很痛苦。

显然你可以在asm中写任何东西,因为与C相比,系统调用并不是什么特别的事情。并且这些都不属于任何一部分。 asm比C更容易,除了可能在锁定周围投掷MFENCE

无论如何,我已经解决了你想要在缓冲区中发生什么的三种变化:

  1. 覆盖相同的缓冲区(mmap(2) / msync(2)
  2. 将缓冲区的快照附加到文件中(包含write(2)或可能无效的零拷贝vmsplice(2) + splice(2)提示。)
  3. 在写入旧缓冲区后启动一个新的(归零)缓冲区。输出文件的mmap(2)个连续块。
  4. 就地覆盖

    如果您只是想每次都覆盖相同的磁盘区域,mmap(2)一个文件并将其用作数组。 (定期调用msync(2)以强制数据到磁盘。但是,mmapped方法不能保证文件的一致状态。除请求之外,写入可以刷新到磁盘。 IDK是否有办法避免任何形式的保证(即不只是选择缓冲区冲洗定时器等等,因此除了msync(2)之外,您的页面通常不会被写入。)

    追加快照

    将缓冲区附加到文件的简单方法是在您希望编写时简单地调用write(2)write(2)可以完成您需要的一切。如果您的程序是多线程的,则可能需要在系统调用之前锁定数据,然后释放锁。我不确定写系统调用的返回速度有多快。它只能在内核将数据复制到页面缓存后返回。

    如果您只需要一个快照,但所有写入缓冲区的内容都是原子事务(即缓冲区始终处于一致状态,而不是需要相互一致的值对),那么您就不会这样做。 ;在调用write(2)之前需要锁定。在这种情况下会有一点偏差(缓冲区末尾的数据将比起始时的数据稍晚一些,假设内核按顺序复制)。

    IDK,如果write(2)使用直接IO(零拷贝,绕过页面缓存)返回更慢或更快。 open(2)您的文件通常包含O_DIRECTwrite(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。
    • 正常运行
    • 当您想要继续下一个16MiB时:
      1. 采取锁定以防止其他线程使用缓冲区
      2. mmap(2)你的缓冲区
      3. munmap(2)文件的下一个16MiB到同一地址,因此您不需要将新地址传递给作者。根据POSIX的要求,这些页面将被预先置零(不能让内核暴露内存)。
      4. 释放锁

    mmap(2)可能会取代mmap(buf, 16MiB, ... MAP_FIXED, fd, new_offset) / munmap对。 mmap会丢弃它重叠的旧MAP_FIXED。我认为这并不意味着对文件/共享内存的修改被丢弃,而是实际的映射会发生变化,即使没有mmap

答案 1 :(得分:1)

对Peter的回答中的附加快照案例进行了两次澄清。

<强> 1。不加O_DIRECT

附加

正如彼得所说,如果你不使用O_DIRECTwrite()会在数据被复制到页面缓存后立即返回。如果页面缓存已满,它将阻塞,直到某些过时的页面刷新到磁盘。

如果您只是在不读取数据的情况下附加数据(很快),您可以定期调用sync_file_range(2)为先前写入的页面安排刷新,并使用POSIX_FADV_DONTNEED标记posix_fadvise(2)来删除从页面缓存中刷新页面。这可能会显着降低write()阻止的可能性。

<强> 2。附加O_DIRECT

使用O_DIRECT时,write()通常会阻止数据发送到磁盘(尽管没有严格保证,请参阅here)。由于速度很慢,如果您需要非阻塞写入,请准备好实现自己的I / O调度。

您可以归档的好处是:更可预测的行为(您可以控制何时阻止),并且可能通过应用程序和内核的协作减少内存和CPU使用。