首页 > 解决方案 > 将 mremap(..., MREMAP_FIXED) 与 valgrind 一起使用

问题描述

考虑这种实现包装缓冲区的方法:

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

static int wrapping_buffer(void)
{
    char *b1, *b2;
    long size;

    size = sysconf(_SC_PAGE_SIZE);
    if (size == -1) {
        perror("_SC_PAGE_SIZE");
        return 1;
    }

    b1 = mmap(NULL, 2 * size, PROT_READ|PROT_WRITE,
          MAP_SHARED|MAP_ANONYMOUS, -1, 0);

    if (!b1) {
        perror("mmap");
        return 1;
    }
    memset(b1, 0, 2 * size);

    /* This remaps the upper half of the buffer to the lower half,
       The same memory is now accessed by 2 different virtual adresses */
    b2 = mremap(b1, 0, size, MREMAP_MAYMOVE|MREMAP_FIXED, b1 + size);
    if (b2 != b1 + size) {
        perror("mremap");
        return 1;
    }

    sprintf(b1 + size - 16, "this sentence is too long to fit in buffer");
    printf("end:\t\"%s\"\n", b1 + size - 16);
    printf("start:\t\"%s\"\n", b1);

    if (munmap(b2, size) != 0)
        perror("munmap(2)");
    if (munmap(b1, 2 * size) != 0)
        perror("munmap(1)");

    return 0;
}

int main(void)
{
    return wrapping_buffer();
}

该程序编译并产生如下输出

end:    "this sentence is too long to fit in buffer"
start:  " too long to fit in buffer"

IOW,写入缓冲区的超过一页大小的字符串被包装到缓冲区的开头。这是基于mremap(2)的记录行为:

如果 old_size 的值为零,并且 old_address 指的是可共享映射(请参阅 mmap(2) MAP_SHARED),则 mremap() 将创建相同页面的新映射。

不幸的是,如果我尝试在 valgrind 下运行这个程序,它会在mremap()调用中失败:

==7217== Memcheck, a memory error detector
==7217== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==7217== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==7217== Command: /tmp/wrapping_buffer
==7217== 
mremap: Invalid argument

这意味着我不能在任何应该使用 valgrind 调试的程序中使用这种技术(不是在谈论这个特定的代码,而是在程序的其他可能包含内存泄漏的部分)。这很不幸,因为我真的很喜欢重映射技术的优雅和效率。

有谁知道如何在 valgrind 下进行这项工作?还是我必须为此破解 valgrind 本身?

标签: clinuxmemoryvalgrindmmap

解决方案


推荐阅读