Чтение 32-битных кешей в 64-битной среде - PullRequest
0 голосов
/ 11 марта 2011

у нас есть много кэшей, которые были построены на 32-битной машине, которые мы теперь должны прочитать в 64-битной среде. Мы получаем ошибку сегментации, когда хотим открыть прочитанный файл кэша.

Потребуются недели, чтобы воспроизвести кэши, поэтому я хотел бы знать, как еще можно обрабатывать наши 32-битные файлы кэша на 64-битных машинах.

Вот код, который мы используем для чтения и записи наших кэшей:

bool IntArray::fload(const char* fname, long offset, long _size){
  long size = _size * sizeof(long);

  long fd = open(fname, O_RDONLY);
  if ( fd >0  ){
    struct stat file_status;
    if ( stat(fname, &file_status) == 0 ){
      if ( offset < 0 || offset > file_status.st_size ){
        std::__throw_out_of_range("offset out of range");
        return false;
      }
      if ( size + offset > file_status.st_size ){
        std::__throw_out_of_range("read size out of range");
        return false;
      }

      void *map = mmap(NULL, file_status.st_size, PROT_READ, MAP_SHARED, fd, offset);
      if (map == MAP_FAILED) {
        close(fd);
        std::__throw_runtime_error("Error mmapping the file");
        return false;
      }

      this->resize(_size);
      memcpy(this->values, map, size);

      if (munmap(map, file_status.st_size) == -1) {
        close(fd);
        std::__throw_runtime_error("Error un-mmapping the file");
        return false;
        /* Decide here whether to close(fd) and exit() or not. Depends... */
      }

      close(fd);
      return true;
    }
  }
  return false;
}
bool IntArray::fsave(const char* fname){
  long fd = open(fname, O_WRONLY | O_CREAT, 0644); //O_TRUNC
  if ( fd >0  ){
    long size = this->_size * sizeof(long);
    long r = write(fd,this->values,size);
    close(fd);

    if ( r != size ){
        std::__throw_runtime_error("Error writing the file");
    }
    return true;
  }
  return false;
}

Ответы [ 3 ]

3 голосов
/ 11 марта 2011

с линии:

long size = this->_size * sizeof(long);

Я предполагаю, что values указывает на массив long. В большинстве ОС, за исключением Widnows, long - это 32 бита в 32-битной сборке и 64 бита в 64-битной сборке.

Вы должны прочитать ваш файл как дамп 32-битных значений, например, int32_t, а затем скопировать его как можно дольше. И, вероятно, версия вашего файла, чтобы вы знали, какую логику применять при чтении.

На самом деле, проектирование формата файла вместо простого использования дампа памяти предотвратит подобные проблемы (порядковый номер, заполнение, формат FP и т. Д. - это другие проблемы, которые могут возникнуть, если вы попытаетесь немного расширить переносимость). чем просто программа, которая написала файл - особенно это может измениться с выпуском компилятора и флагами компиляции).

1 голос
/ 11 марта 2011

Вам необходимо изменить расположение памяти на this->values (каким бы типом это ни было, вы не упоминаете эту важную информацию) на 64-битных машинах таким образом, чтобы макет памяти стал идентичным макету памяти, используемому 32-битныммашины.

Для этого вам, возможно, придется использовать приемы компилятора, такие как упаковка структуры или аналогичные вещи, и если this->values содержит классы, у вас будет много проблем с внутренними указателями классов, которые генерирует компилятор.

Кстати, есть ли в C ++ правильные целочисленные типы явно заданного размера?#include <cstdint>

0 голосов
/ 11 марта 2011

Вы отказались от использования long в качестве 32-битного типа данных ... что, по крайней мере, в системах UN * X, не относится к 64-битным (модель данных LP64, int - 32-битная, но * 1003) * и 64-битные указатели).

В Windows64 (модель данных IL32P64, int и long 32-битная, но указатели 64-битные) ваш код, выполняющий вычисления размера в единицах sizeof(long) и непосредственно выполняющий memcpy() из сопоставленного файла в объектно-ориентированный массив, будет на самом деле продолжать работать ...

В UN * X это означает, что при переходе на 64-битную версию, чтобы сохранить переносимость кода, было бы лучше переключиться на int32_t с явно заданным размером (с <stdint.h>), чтобы убедиться, что структура вашей структуры данных остается неизменным при выполнении как 32-битных, так и 64-битных целевых компиляций.

Если вы настаиваете на сохранении long, то вам придется изменить интернализацию / экстернализацию массива с простого memcpy() / write() на другие действия. Без обработки ошибок (у вас это уже было выше) для метода ::fsave() это выглядело бы так, вместо использования write(), как вы делаете:

long *array = this->values;
int32_t *filebase =
    mmap(NULL, file_status.st_size, PROT_WRITE, MAP_SHARED, fd, offset);

for (int i = 0; i < this->_size; i++) {
    if (array[i] > INT32_MAX || array[i] < INT32_MIN)
        throw (std::bad_cast);  // can't do ...
    filebase[i] = static_cast<int32_t>(array[i]);
}

munmap(filebase, file_status.st_size);

и для ::fload() вместо memcpy() вы должны сделать следующее:

long *array = this->values;
int32_t *filebase =
    mmap(NULL, file_status.st_size, PROT_READ MAP_SHARED, fd, offset);

for (int i = 0; i < this->_size; i++)
    array[i] = filebase[i];

munmap(filebase, file_status.st_size);

Примечание. Как уже упоминалось, этот подход не будет работать, если у вас есть что-то более сложное, чем простой массив, потому что, кроме различий в размерах типов данных, могут быть и другие ограничения выравнивания, и другие правила заполнения. Кажется, что это не так, поэтому имейте это в виду, только когда рассматриваете возможность расширения этого механизма (не используйте проверенную библиотеку, такую ​​как boost :: any или Qt :: Variant, которая может экстернализовать / усвоить).

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