Как mmap () большой файл, не рискуя убийцей OOM? - PullRequest
12 голосов
/ 05 февраля 2020

У меня есть встроенный блок ARM Linux с ограниченным объемом ОЗУ (512 МБ) и без пространства подкачки, на котором мне нужно создать и затем манипулировать довольно большим файлом (~ 200 МБ). Загрузка всего файла в ОЗУ, изменение содержимого в ОЗУ, а затем повторная запись его иногда вызывали бы OOM-killer, чего я хочу избежать.

Моя идея обойти это было использовать mmap() для сопоставления этого файла с виртуальным адресным пространством моего процесса; таким образом, чтение и запись в отображенную область памяти будет go вместо локальной системы fla sh -files, а OOM-killer будет исключен, поскольку, если объем памяти будет недостаточным, Linux может просто вызвать грипп sh некоторые страницы памяти mmap () возвращаются на диск, чтобы освободить часть оперативной памяти. (Это может сделать мою программу медленной, но медленной вполне подойдет для этого варианта использования)

Однако, даже с вызовом mmap() я все еще иногда вижу, как процессы убиваются OOM-killer во время выполнения вышеуказанная операция.

У меня такой вопрос, был ли я слишком оптимистичен c о том, как Linux будет вести себя при наличии большого mmap () и ограниченного объема ОЗУ? (т. е. делает ли mmap () файл размером 200 МБ, а затем для чтения / записи в память mmap () по-прежнему требуется 200 МБ доступной оперативной памяти для надежного выполнения sh?) Или mmap () должен быть достаточно умен, чтобы выводить на экран mmap?) бы страниц, когда памяти мало, но я делаю что-то не так в том, как я использую это?

ПОКАЗАТЬ мой код для отображения здесь:

void FixedSizeDataBuffer :: TryMapToFile(const std::string & filePath, bool createIfNotPresent, bool autoDelete)
{
   const int fd = open(filePath.c_str(), (createIfNotPresent?(O_CREAT|O_EXCL|O_RDWR):O_RDONLY)|O_CLOEXEC, S_IRUSR|(createIfNotPresent?S_IWUSR:0));
   if (fd >= 0)
   {
      if ((autoDelete == false)||(unlink(filePath.c_str()) == 0))  // so the file will automatically go away when we're done with it, even if we crash
      {
         const int fallocRet = createIfNotPresent ? posix_fallocate(fd, 0, _numBytes) : 0;
         if (fallocRet == 0)
         {
            void * mappedArea = mmap(NULL, _numBytes, PROT_READ|(createIfNotPresent?PROT_WRITE:0), MAP_SHARED, fd, 0);
            if (mappedArea)
            {
               printf("FixedSizeDataBuffer %p: Using backing-store file [%s] for %zu bytes of data\n", this, filePath.c_str(), _numBytes);
               _buffer         = (uint8_t *) mappedArea;
               _isMappedToFile = true;
            }
            else printf("FixedSizeDataBuffer %p: Unable to mmap backing-store file [%s] to %zu bytes (%s)\n", this, filePath.c_str(), _numBytes, strerror(errno));
         }
         else printf("FixedSizeDataBuffer %p: Unable to pad backing-store file [%s] out to %zu bytes (%s)\n", this, filePath.c_str(), _numBytes, strerror(fallocRet));
      }
      else printf("FixedSizeDataBuffer %p: Unable to unlink backing-store file [%s] (%s)\n", this, filePath.c_str(), strerror(errno));

      close(fd); // no need to hold this anymore AFAIK, the memory-mapping itself will keep the backing store around
   }
   else printf("FixedSizeDataBuffer %p: Unable to create backing-store file [%s] (%s)\n", this, filePath.c_str(), strerror(errno));
}

Я могу переписать это код, чтобы просто использовать обычный старый файл-ввод-вывод, если мне нужно, но было бы неплохо, если бы mmap() мог выполнить эту работу (или, если нет, я бы хотя бы хотел понять, почему нет).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...