Подтвердить что ты не робот

Mmap, msync и завершение процесса linux

Я хочу использовать mmap для реализации сохранения некоторых частей состояния программы в программе на C, запущенной под Linux, путем связывания структуры фиксированного размера с известным именем файла с помощью mmap() с установленным флагом MAP_SHARED. По соображениям производительности я бы предпочел не вызывать msync() вообще, и никакие другие программы не будут обращаться к этому файлу. Когда моя программа завершается и перезапускается, она снова отображает тот же файл и выполняет некоторую обработку на нем, чтобы восстановить состояние, в котором оно было до завершения. Мой вопрос заключается в следующем: если я никогда не вызову msync() в файловом дескрипторе, ядро ​​гарантирует, что все обновления в памяти будут записаны на диск и впоследствии будут восстановлены, даже если мой процесс будет завершен с помощью SIGKILL? Кроме того, будет ли общая накладная система из ядра периодически записывать страницы на диск, даже если моя программа никогда не вызывает msync()?

РЕДАКТИРОВАТЬ: Я решил проблему записи данных, но я все еще не уверен, приведет ли это к непредвиденной загрузке системы из-за попытки решить эту проблему с помощью open ( )/write()/fsync() и риск того, что некоторые данные могут быть потеряны, если процесс попадет в KILL/SEGV/ABRT/и т.д. Добавлен тег "linux-kernel" в надежде, что какой-нибудь знающий человек может перезвонить.

4b9b3361

Ответ 1

Я решил быть менее ленивым и ответить на вопрос, записаны ли данные на диск окончательно, написав какой-то код. Ответ заключается в том, что он будет записан.

Вот программа, которая быстро убивает себя после записи некоторых данных в файл mmap'd:

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>

typedef struct {
  char data[100];
  uint16_t count;
} state_data;

const char *test_data = "test";

int main(int argc, const char *argv[]) {
  int fd = open("test.mm", O_RDWR|O_CREAT|O_TRUNC, (mode_t)0700);
  if (fd < 0) {
    perror("Unable to open file 'test.mm'");
    exit(1);
  }
  size_t data_length = sizeof(state_data);
  if (ftruncate(fd, data_length) < 0) {
    perror("Unable to truncate file 'test.mm'");
    exit(1);
  }
  state_data *data = (state_data *)mmap(NULL, data_length, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_POPULATE, fd, 0);
  if (MAP_FAILED == data) {
    perror("Unable to mmap file 'test.mm'");
    close(fd);
    exit(1);
  }
  memset(data, 0, data_length);
  for (data->count = 0; data->count < 5; ++data->count) {
    data->data[data->count] = test_data[data->count];
  }
  kill(getpid(), 9);
}

Вот программа, которая проверяет полученный файл после того, как предыдущая программа мертва:

#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>

typedef struct {
  char data[100];
  uint16_t count;
} state_data;

const char *test_data = "test";

int main(int argc, const char *argv[]) {
  int fd = open("test.mm", O_RDONLY);
  if (fd < 0) {
    perror("Unable to open file 'test.mm'");
    exit(1);
  }
  size_t data_length = sizeof(state_data);
  state_data *data = (state_data *)mmap(NULL, data_length, PROT_READ, MAP_SHARED|MAP_POPULATE, fd, 0);
  if (MAP_FAILED == data) {
    perror("Unable to mmap file 'test.mm'");
    close(fd);
    exit(1);
  }
  assert(5 == data->count);
  unsigned index;
  for (index = 0; index < 4; ++index) {
    assert(test_data[index] == data->data[index]);
  }
  printf("Validated\n");
}

Ответ 2

Я нашел комментарий от Линуса Торвальдса, который отвечает на этот вопрос http://www.realworldtech.com/forum/?threadid=113923&curpostid=114068

Отображаемые страницы являются частью кеша файловой системы, а это означает, что даже если процесс пользователя, внесший изменения на эту страницу, умирает, страница все еще управляется ядром, и поскольку все одновременные обращения к этому файлу будут проходить через ядро, другие процессы будут обслуживаться из этого кеша. В некоторых старых ядрах Linux было иначе, что причина, по которой некоторые документы ядра все еще говорят, чтобы заставить msync.

EDIT: Спасибо, что RobH исправил ссылку.

Ответ 3

Я нашел что-то, добавляющее к моей путанице:

munmap не влияет на отображаемый объект, вызов munmap не приводит к записи содержимого отображаемой области к файлу диска. Обновление файла диска для MAP_SHARED область выполняется автоматически с помощью алгоритма виртуальной памяти ядра как мы сохраняем в области с отображением памяти.

это выдержка из расширенного программирования в среде UNIX®.

из man-страницы linux:

MAP_SHARED Поделитесь этим сопоставлением со всеми другими процессами, которые отображают это объект. Сохранение в регионе равносильно письменному файл. Файл не может быть обновлен до msync (2) или munmap (2).

эти два кажутся противоречивыми. неверно APUE?

Ответ 4

Я не нашел очень точного ответа на ваш вопрос, поэтому решил добавить еще одно:

  • Во-первых, о потере данных, используя механизмы write или mmap/memcpy, как записывает в кеш страницы, так и синхронизирует их с базовым хранилищем в фоновом режиме ОС на основе настроек /algo. Например, у linux есть vm.dirty_writeback_centisecs, который определяет, какие страницы считаются "старыми", которые должны быть сброшены на диск. Теперь, даже если ваш процесс умирает после того, как вызов записи преуспел, данные не будут потеряны, поскольку данные уже присутствуют на страницах ядра, которые в конечном итоге будут записаны на хранение. Единственный случай, когда вы потеряете данные, - это то, что сама ОС сбой (паника ядра, выключение питания и т.д.). Способ абсолютно убедиться, что ваши данные достигли хранилища, будет вызван fsync или msync (для mmapped областей), как это может быть.
  • О проблеме загрузки системы, да, вызов msync/fsync для каждого запроса будет замедлять вашу пропускную способность, поэтому сделайте это, только если вам нужно. Помните, что вы действительно защищаете от потери данных об авариях ОС, которые, как я полагаю, редки и, вероятно, с чем-то может жить. Одна общая оптимизация заключается в том, чтобы выдать синхронизацию через равные промежутки времени, скажем, 1 секунду, чтобы получить хороший баланс.

Ответ 5

Либо личная страница руководства Linux неверна, либо Linux ужасно несоответствует. msync не должен иметь ничего общего с тем, связаны ли изменения с логическим состоянием файла или другие процессы, использующие mmap или read для доступа к файлу, видят изменения; это чисто аналог fsync и должен рассматриваться как no-op, за исключением целей обеспечения целостности данных в случае сбоя питания или другого сбоя аппаратного уровня.

Ответ 6

Согласно man-странице,

Возможно, файл не может быть                   обновляется до вызова msync (2) или munmap().

Таким образом, вам нужно будет убедиться, что вы вызываете munmap() до выхода, по крайней мере.