如何实现确定性malloc

时间:2011-12-07 13:04:19

标签: c linux x86 malloc

假设我有两个应用程序实例,具有相同的输入和相同的执行顺序。因此,一个实例是冗余实例,用于将存储器中的数据与另一个实例进行比较,作为一种错误检测机制。

现在,我希望所有内存分配和解除分配在两个进程中以完全相同的方式发生。实现这一目标的最简单方法是什么?写我自己的malloc并免费?那么用mmap等其他函数分配的内存呢?

5 个答案:

答案 0 :(得分:3)

我想知道你想要实现的目标。如果您的流程是确定性的,那么分配/解除分配的模式应该是相同的。

唯一可能的区别可能是malloc返回的地址。但您可能不应该依赖它们(最简单的方法是不使用指针作为键映射或其他数据结构)。即便如此,如果分配不是通过sbrk(glibc使用匿名mmap进行大型分配),或者如果您使用mmap(默认情况下),则应该只有差异地址由内核选择。

如果你真的想拥有完全相同的地址,一个选项是拥有一个大的静态缓冲区并编写一个自定义分配器,它使用这个缓冲区中的内存。这样做的缺点是迫使您事先知道您需要的最大内存量。如果你使用address space layout randomization的内核,这可能会破坏。

如果您事先不知道要使用的内存的最大大小,或者每次此大小增加时您不想重新编译,您还可以使用mmap来映射大型内存固定地址的匿名缓冲区。只需将缓冲区的大小和地址作为参数传递给您的流程,并使用返回的内存在其上实现您自己的malloc

static void* malloc_buffer = NULL;
static size_t malloc_buffer_len = 0;

void* malloc(size_t size) {
    // Use malloc_buffer & malloc_buffer_len to implement your
    // own allocator. If you don't read uninitialized memory,
    // it can be deterministic.
    return memory;
}

int main(int argc, char** argv) {
    size_t buf_size = 0;
    uintptr_t buf_addr = 0;
    for (int i = 0; i < argv; ++i) {
        if (strcmp(argv[i], "--malloc-size") == 0) {
            buf_size = atoi(argv[++i]);
        }
        if (strcmp(argv[i], "--malloc-addr") == 0) {
            buf_size = atoi(argv[++i]);
        }
    }

    malloc_buffer = mmap((void*)buf_addr, buf_size, PROT_WRITE|PROT_READ,
                         MAP_FIXED|MAP_PRIVATE, 0, 0);
    if (malloc_buffer == MAP_FAILED || malloc_buffer != (void*)but_addr) {
        // Could not get requested memory block, fail.
        exit(1);
    }

    malloc_size = buf_size;
}

通过使用[MAP_FIXED][2],我们请求具有所需大小的内存块并从给定地址开始。如果使用此块来实现自己的malloc并且不在代码中使用其他非确定性操作,则可以完全控制指针值。

这假设您对malloc / free的模式使用是确定性的。并且您不使用非确定性的库。

但是,我认为更简单的解决方案是保持算法的确定性,而不是依赖于地址。这个有可能。我曾经在一个大型项目上工作,多台计算机必须确定性地更新状态(这样每个程序都具有相同的状态,同时只传输输入)。如果你不使用指针来引用其他东西而不是引用对象(最重要的是永远不要使用指针值作为任何东西,而不是作为哈希,而不是地图中的键,...),那么你的状态将保持确定性

除非您想要做的是能够对整个进程内存进行快照并执行二进制差异以发现分歧。我认为这是一个坏主意,因为你怎么知道他们两个都在他们的计算中达到了相同的点?比较输出要容易得多,或者让进程能够计算状态的哈希并使用它来检查它们是否同步,因为你可以控制它何时完成(因此它也变得具有确定性,否则你的测量结果是不确定的。)

答案 1 :(得分:3)

简单地说,就像其他人所说:如果你的程序指令的执行是确定性的,那么malloc()返回的内存将是确定性的。这假设您的系统实施没有调用random()或其他相关内容。如果您不确定,请阅读系统malloc

的代码或文档

正如其他人所说的那样,这可能是ASLR的例外情况。如果您没有root权限,则可以通过personality(2)系统调用和ADDR_NO_RANDOMIZE参数对每个进程禁用它。有关个性的更多信息,请参阅here

编辑:我还应该说,如果你不知道:你正在做的事情被称为 bisimulation ,并且是一项经过充分研究的技术。如果您不了解术语,则可以使用该关键字进行搜索。

答案 2 :(得分:2)

编写高可靠性代码时,通常的做法是避免malloc和其他动态内存分配。有时使用的折衷方案是仅在系统初始化期间进行所有此类分配。

答案 3 :(得分:2)

不确定性不仅仅是malloc而是mmap(获得更多内存空间的基本系统调用;它不是函数,它是system call所以是基本的或原子的从应用程序的角度来看;所以你不能在应用程序中重写它,因为Linux上有address space layout randomization

你可以用

禁用它
 echo 0 > /proc/sys/kernel/randomize_va_space

以root身份,或通过sysctl

如果你没有禁用地址空间布局随机化,你就会陷入困境。

你确实问了一个类似的问题previously,在那里我解释说你的malloc - s并不总是确定性的。

我仍然认为,对于某些实际应用,malloc不能是确定性的。想象一下,例如,一个程序的哈希表由它正在启动的子进程的pid - s键控。该表中的冲突在所有流程中都不一样等等。

所以我相信你不会在你的意义上成功地确定malloc确定性,无论你要尝试什么(除非你把自己限制在一个非常狭窄的应用程序类别的检查点,如此狭窄以至于你的软件赢得了'非常有用)。

答案 4 :(得分:-1)

您可以使用共享内存来存储数据。它可以从两个过程访问,您可以以确定的方式填充它。