You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何安全写入mmap映射区域至文件?解决SIGBUS异常问题

解决方案:避免mmap截断文件导致的SIGBUS问题

你遇到的SIGBUS本质是:当mmap映射的区域超出文件当前实际大小,访问超出范围的内存页时,内核会触发该信号终止进程。针对你的转储场景,以下是几个可行的安全解决方案:

方案一:用分块pread替代mmap(最直接解决当前代码问题)

放弃直接mmap整个大区域,改用pread分块读取原文件内容,再写入压缩文件。pread会自动处理文件大小变化,读取到文件末尾时会返回实际可读字节数,不会触发任何信号。

代码替换示例:

// 移除原mmap相关代码,替换为以下逻辑
size_t remaining = segment->size;
off_t file_offset = segment->file_offset;
const size_t chunk_size = 4096; // 按页大小分块,可根据性能调整
char buf[chunk_size];

while (remaining > 0) {
    ssize_t bytes_read = pread(mmap_fd, buf, chunk_size, file_offset);
    if (bytes_read <= 0) {
        // 读取失败或已到文件末尾,终止循环
        break;
    }
    gzwrite(gzfile, buf, bytes_read);
    remaining -= bytes_read;
    file_offset += bytes_read;
}

优缺点:

  • ✅ 彻底规避SIGBUS问题,并发场景下文件继续缩小也不会出错
  • ✅ 无需修改信号处理逻辑,不会导致压缩文件损坏
  • ❌ 相比mmap的直接内存复制,有轻微的性能开销,但转储工具通常可接受

方案二:直接读取目标进程的内存(符合转储进程信息的核心需求)

你的工具核心是转储进程的内存信息,而非原文件内容。直接通过process_vm_readv或ptrace读取目标进程的内存,完全绕开原文件的状态变化问题。

代码示例(用process_vm_readv高效读取):

#include <sys/uio.h>
#include <sys/ptrace.h>
#include <sys/wait.h>

// ...

size_t remaining = segment->size;
void* remote_addr = (void*)segment->addr; // 进程内存段的起始地址
const size_t chunk_size = 4096;
char buf[chunk_size];

// 附加到目标进程
if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) == -1) {
    // 处理权限或进程不存在等错误
    goto cleanup;
}
waitpid(pid, NULL, 0); // 等待进程进入停止状态

struct iovec local_iov = {.iov_base = buf, .iov_len = chunk_size};
struct iovec remote_iov = {.iov_base = remote_addr, .iov_len = chunk_size};

while (remaining > 0) {
    size_t read_len = remaining < chunk_size ? remaining : chunk_size;
    local_iov.iov_len = read_len;
    remote_iov.iov_len = read_len;
    remote_iov.iov_base = remote_addr;

    ssize_t bytes_read = process_vm_readv(pid, &local_iov, 1, &remote_iov, 1, 0);
    if (bytes_read <= 0) {
        break;
    }
    gzwrite(gzfile, buf, bytes_read);
    remaining -= bytes_read;
    remote_addr += bytes_read;
}

// 分离目标进程,恢复其运行
ptrace(PTRACE_DETACH, pid, NULL, NULL);
cleanup:
close(mmap_fd);

优缺点:

  • ✅ 直接获取进程当前内存中的真实内容,不受原文件修改/截断影响
  • ✅ 对于MAP_PRIVATE类型的映射,能获取进程修改后的私有数据(原文件中没有)
  • ❌ 需要足够的权限(通常是root或同用户组),附加进程会短暂暂停目标进程

方案三:安全映射+分段处理(兼容原mmap思路)

如果一定要保留mmap的使用,可以先获取文件当前大小,只映射安全范围的区域,超出部分根据场景处理:

  1. 用fstat获取文件当前大小,计算安全映射长度:size_t safe_len = min(segment->size, stat_buf.st_size - segment->file_offset)
  2. 仅映射safe_len大小的区域,写入压缩文件
  3. 对于超出safe_len的部分:
    • 如果是转储进程内存:用方案二的方式读取进程内存
    • 如果仅需原文件内容:直接填充空字节或记录缺失

注意:

这个方案仍存在极小的并发风险(fstat后文件立即缩小),可以结合pread验证每块数据的可用性,进一步降低风险。


内容的提问来源于stack exchange,提问作者HardcoreHenry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 12:30:55