Хорошим способом создания моментального снимка процесса является использование fork()
для создания дочернего процесса.Память дочернего процесса будет копией родительского процесса.
Вместо того, чтобы активно копировать всю память, ОС просто помечает страницы как копирование при записи : страницыбудет клонирован, если событие одного из процессов записывает в него.Это экономит время и пространство, и это здорово.
В случае выхода из дочернего процесса поведение copy-on-write должно быть отключено.Тем не менее, я получаю ошибки страницы для всего массива - есть ли способ оптимизировать эти ошибки страницы?например, аналогично тому, как MAP_POPULATE
позволяет избежать сбоев страниц при начальном доступе к страницам отображаемой области.
Ниже приведен простой тест, демонстрирующий поведение, которое я выполняю.спрашивать о.Я проверяю ошибки страниц через perf stat -e minor-faults,major-faults ./a.out
.
Если дочерний процесс не создан (WITH_CHILD
установлен на false
), у меня очень мало ошибок страниц (около 125
и постоянных).Однако, просто создавая и пожирая дочерний процесс, я получаю ошибки страниц во всем (около 131260
, пропорционально размеру массива). Поскольку страницы отображаются одним процессом, я не ожидал бы, что возникнут сбои страниц!Почему они?
Это продолжение Копирование ядра CoW-страниц после завершения дочернего процесса .
#include <unistd.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <array>
#include <cassert>
#include <cstring>
#include <iostream>
#define ARRAY_SIZE 536870912 // 512MB
#define WITH_CHILD true
using inttype = uint64_t;
constexpr uint64_t NUM_ELEMS() {
return ARRAY_SIZE / sizeof(inttype);
}
int main() {
// allocate array
void *arraybuf = mmap(nullptr, ARRAY_SIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
assert(arraybuf != nullptr);
std::array<inttype, NUM_ELEMS()> *array =
new (arraybuf) std::array<inttype, NUM_ELEMS()>();
#if WITH_CHILD
// spawn checkpointing process
int pid = fork();
assert(pid != -1);
// child process -- do nothing, just exit
if (pid == 0) {
exit(0);
}
// wait for child thread to exit
assert(waitpid(pid, nullptr, 0) == pid);
#endif
// write to array -- this shouldnt generate page faults, right? :(
std::fill(array->begin(), array->end(), 0);
// cleanup
munmap(array, ARRAY_SIZE);
}